Get Function() result as Control - excel

Background:
I needed a function to correlate Controls efficiently based on a STD name and the text analyzed. IG:
a) Some other input throws the variable "mytext"
b) If the ListBox1.Value has "mytext" then I have to relate it with ToggleButton1
Approach:
I made the following function which partially works
Code:
Private Function RelateControl_ToggleVsList(ToggleCtrl As Control) As Control
Dim ItemControl As Control
Dim myControl As Object
For Each ItemControl In Me.Controls
If TypeName(ItemControl) = "ListBox" Then ' 1. If TypeName(ItemControl) = "Label"
'text lenghts const 13 for ListBox_TimeXX and 22 for ToggleButton_PriorityXX
If Mid(ItemControl.Name, 13, 2) = Mid(ToggleCtrl.Name, 22, 2) Then Set RelateControl_ToggleVsList = ItemControl: Exit Function
End If ' 1. If TypeName(ItemControl) = "Label"
Next ItemControl
End Function
Problem:
I get a null property when Setting the result:
Set RelateControl_ToggleVsList = ItemControl 'This is nullSet
Debugging process:
Question:
How can I set a Control as a result of this function?
EDIT:
Per request I add the whole Debugging in order to see where it is being called form
Calling Code
Private Sub ToggleButtons_Active()
Dim ItemControl As Control
Dim ItemTextBox As Variant
Dim TxtControl As String
For Each ItemControl In Me.Controls
If TypeName(ItemControl) = "ToggleButton" Then ' 1. If TypeName(ItemControl) = "ToggleButton"
TxtControl = CStr(RelateControl_ToggleVsList(ItemControl).Value)
If InStr(TextBox_Notes.Value, TxtControl) > 0 And TxtControl <> "" Then ItemControl.Value = True
End If ' 1. If TypeName(ItemControl) = "ToggleButton"
Next ItemControl
End Sub

Your error is occurring on your line which says
TxtControl = CStr(RelateControl_ToggleVsList(ItemControl).Value)
because the returned Control's Value property is currently Null which can't be cast to a String.
I recommend that you change TxtControl to be a Variant type, then say
TxtControl = RelateControl_ToggleVsList(ItemControl).Value
If IsNull(TxtControl) Then
TxtControl = ""
Else
TxtControl = CStr(TxtControl)
End If
Or you could define a Control object and then use it:
Dim MyControl As Control
MyControl = RelateControl_ToggleVsList(ItemControl)
If IsNull(MyControl.Value) Then
TxtControl = ""
Else
TxtControl = CStr(MyControl.Value)
End If

Solution:
The object itself is calling a property which does not belong to it (as intended).
ListBox Properties does not show "Value" as its text, the real thing should have called it as:
TxtControl = CStr(RelateControl_ToggleVsList(ItemControl).List(0))CStr(RelateControl_ToggleVsList(ItemControl).List(0))
Furthermore:
Thanks to the solutions provided and the debugging process, I could notice even the object is Set in the function, it is shown as the "value" property of it while debugging.

Related

Loop Through VBA Variables

I'm making a Userform in VBA where I have Text Box's where I can search items. I have about 30 Text Box's so I want to cut down the code using a loop instead of copy and pasting the same code 30 times.
Problem: I don't know how to loop through a public variable
Public Variable: Public oEventHandler(Number) As New clsSearchableDropdown
oEventHandler would go from 1 to 30 (e.g oEventHandler2,oEventHandler3...oEventHandler30)
clsSearchableDropdown is the Class Module for the search feature
Text Box: TextBox(Number)
ListBox: ListBox(Number)
Here is the original code (No Issue Just to Compare):
With oEventHandler1
' Attach the textbox and listbox to the class
Set .SearchListBox = Me.ListBox1
Set .SearchTextBox = Me.TextBox1
' Default settings
.MaxRows = 10
.ShowAllMatches = True
.CompareMethod = vbTextCompare
.WindowsVersion = False
End With
This is what I'm trying to do:
Dim i As Integer
for i = 1 to 30
With Me.Controls.Item("oEventHandler" & i)
' Attach the textbox and listbox to the class
Set .SearchListBox = Me.Controls.Item("ListBox" & i)
Set .SearchTextBox = Me.Controls.Item("TextBox" & i)
' Default settings
.MaxRows = 10
.ShowAllMatches = True
.CompareMethod = vbTextCompare
.WindowsVersion = False
End With
Next i
I know that oEventHandler is not a control but is there a similar code I can use to loop through a public variable?
Here is the code that worked for me:
' Make a New Collection
Dim coll As New Collection
' Add all Public Variables to Collection (n = Number 1 to 30)
coll.Add uQuote.oEventHandler(n)
(e.g oEventHandler1, oEventHandler2... oEventHandler30)
Dim i As Integer
for i = 1 to 30
With coll(i)
' Attach the textbox and listbox to the class
Set .SearchListBox = Me.Controls.Item("ListBox" & i)
Set .SearchTextBox = Me.Controls.Item("TextBox" & i)
' Default settings
.MaxRows = 10
.ShowAllMatches = True
.CompareMethod = vbTextCompare
.WindowsVersion = False
End With
Next i
If I understand you correctly, in the userform you have 30 Textboxes and 30 Listboxes, where each Textbox(N) is to search the value in the Listbox(N) located under that TextBox(N). So it looks something like this :
On the left side is TextBox01, under TextBox01 is ListBox01
On the right side is TextBox02, under TextBox02 is ListBox02
If the animation is similar with your expectation....
Preparation :
Make a named range (as many as needed) with something like List01, List02, List03, and so on for the value to populate each ListBox.
Name each ListBox with something like ListBox01, ListBox02, and so on.
Name each TextBox with something like TextBox01, TextBox02, and so on.
In the Userform module:
Dim MyTextBoxes As Collection
Private Sub UserForm_Initialize()
'populate the ListBoxes with value in a named range
Dim LBname As String: Dim RGname As String: Dim i As Integer
For i = 1 To 2
LBname = "ListBox" & Format(i, "00")
RGname = "List" & Format(i, "00")
Controls(LBname).List = Application.Transpose(Range(RGname))
Next i
'add each TextBox to class
Set MyTextBoxes = New Collection
For Each ctl In Me.Controls
Set TextBoxClass = New Class1
If TypeName(ctl) = "TextBox" And InStr(ctl.Name, "TextBox") Then Set TextBoxClass.obj = ctl
MyTextBoxes.Add TextBoxClass
Next
End Sub
In the Class Module named Class1:
Private WithEvents tb As MSForms.TextBox
Property Set obj(t As MSForms.TextBox)
Set tb = t
End Property
Private Sub tb_Change()
Dim idx As String: Dim LBname As String: Dim arr
idx = Right(tb.Name, 2)
LBname = "ListBox" & idx
arr = Application.Transpose(Range("List" & idx))
With Userform1.Controls(LBname)
If tb.text = "" Then
.Clear
.List = arr
Else
.Clear
For i = LBound(arr, 1) To UBound(arr, 1)
If LCase(arr(i)) Like "*" & LCase(tb.value) & "*" Then .AddItem arr(i)
Next i
End If
End With
End Sub
If in your userform you have another textbox which not to use as a search of the items in respective listbox, then maybe don't name the textbox with "TextBox" but something else, for example "blablabla".
if your existing textbox and listbox already named something like ListBox1, ListBox2, ListBox3 and so on, TextBox1, TextBox2, TextBox3 and so on... then name the named range like List1, List2, List3 and so on. In the class module, change the code for idx using the replace method, something like idx = replace(tb.name,"TextBox",""). Also in the Userform module for LBname and RGname use the replace method.
Because I'm limited in English language, I'm sorry I can't detail the code for further explanation.

VBA Excel ListView Checkboxes do not show in Userform

I have a UserForm with a MultipageControl (name Controller_MultiPage).
At runtime my code adds pages to the Multipage and creates a newListView on each page.
Every ListView has:
With newListView
.MultiSelect = False
.Width = Controller_MultiPage.Width - 10
.Height = Controller_MultiPage.Height - 20
.View = lvwReport
.HideColumnHeaders = False
.ColumnHeaders.Add Text:="Signal Name", Width:=.Width / 10 * 4
.ColumnHeaders.Add Text:="Type", Width:=.Width / 10
.ColumnHeaders.Add Text:="I/O", Width:=.Width / 10
.ColumnHeaders.Add Text:="Description", Width:=.Width / 10 * 4
.CheckBoxes = True
.FullRowSelect = True
End With
then I populate the newListView with data from an XML file:
For Each node In list
With node.Attributes
Set listItem = newListView.ListItems.Add(Text:=.getNamedItem("Name").Text)
listItem.ListSubItems.Add = .getNamedItem("Type").Text
listItem.ListSubItems.Add = IIf(.getNamedItem("Input").Text = "1", "IN", "OUT")
listItem.ListSubItems.Add = .getNamedItem("Description").Text
listItem.Checked = False
End With
Next
but the checkboxes do not show. I can see the space for them in front of the first column and by clicking that space the checkbox of that particular row then appears. What I also noticed is that if I change the property
listItem.Checked = True
the behavior described above does not change, and when I click the free space in front of the first column (checkboxes space) the chsckbox that then shows up is still unchecked.
Any idea?
The problem seems to be in the behavior of the MultiPage control.
What I noticed was that if I forced the checkboxes' status (checked or unchecked) from the code, using the MultiPage_Change event, then the checkboxes show up.
So what I did was to create a class that holds the status of all checkboxes of all listviews on a single page, instantiate the Class for each ListView and store everything into a Dictionary, using the newListView.Name as Key
Then when the user changes page, the MultiPage_Change event that fires resets all the values of the checkboxes according to the Dictionary stored values.
In the Listview_N_ItemChecked event some other code updates the status of the item stored in the Dictionary.
Kind of cumbersome but it works.
the class (updated):
' Class Name = ComponentsSignalsRecord
Option Explicit
Dim Name As String
' NOTE: Signals(0) will always be empty and status(0) will always be False
Dim Signals() As String
Dim Status() As Boolean
Dim Component As String
Property Let SetComponentName(argName As String)
Component = argName
End Property
Property Get GetComponentName() As String
GetComponentName = Component
End Property
Property Get getSignalName(argIndex) As String
If argIndex >= LBound(Signals) And argIndex <= UBound(Signals) Then
getSignalName = Signals(argIndex)
Else
getSignalName = vbNullString
End If
End Property
Property Get dumpAll() As String()
dumpAll = Signals
End Property
Property Get Count() As Long
Count = UBound(Signals)
End Property
Property Get getStatus(argName As String) As Integer
' returns: -1 = Not Found; 1 = True; 0 = False
getStatus = -1
Dim i As Integer
For i = 0 To UBound(Signals)
If argName = Signals(i) Then getStatus = IIf(Status(i) = True, 1, 0): Exit For
Next
End Property
Property Let setName(argName As String)
Name = argName
End Property
Property Get getName() As String
getName = Name
End Property
Public Sub UncheckAll()
Dim i As Integer
For i = 0 To UBound(Status)
Status(i) = False
Next
End Sub
Public Sub CheckAll()
Dim i As Integer
For i = 0 To UBound(Status)
Status(i) = True
Next
End Sub
Public Sub deleteSignal(argName As String)
Dim spoolSignals() As String
Dim spoolStatus() As Boolean
Dim i As Integer
spoolSignals = Signals
spoolStatus = Status
ReDim Signals(0)
ReDim Status(0)
For i = 1 To UBound(spoolSignals)
If argName <> spoolSignals(i) Then
ReDim Preserve Signals(UBound(Signals) + 1): Signals(UBound(Signals)) = spoolSignals(i)
ReDim Preserve Status(UBound(Status) + 1): Status(UBound(Status)) = spoolStatus(i)
End If
Next
End Sub
Public Sub addSignal(argName As String, argValue As Boolean)
Dim i As Integer
For i = 0 To UBound(Signals)
If argName = Signals(i) Then GoTo bye
Next
ReDim Preserve Signals(UBound(Signals) + 1)
ReDim Preserve Status(UBound(Status) + 1)
Signals(UBound(Signals)) = argName
Status(UBound(Status)) = argValue
bye:
End Sub
Public Sub setStatus(argName As String, argValue As Boolean)
Dim i As Integer
For i = 0 To UBound(Signals)
If argName = Signals(i) Then Status(i) = argValue: Exit For
Next
End Sub
Private Sub Class_Initialize()
ReDim Signals(0)
ReDim Status(0)
End Sub
The Form relevant code. Module level:
Dim myDict As New Dictionary ' the Dictionary
Dim ComponentsSignalsList As ComponentsSignalsRecord ' the Class
for each ListView created, may be one or more for every single MultiPage page :
Set ComponentsSignalsList = New ComponentsSignalsRecord
ComponentsSignalsList.setName = newListView.name
while populating the listview(s) in a loop for each single item added:
ComponentsSignalsList.addSignal List_Item.Text, List_Item.Checked
end of each loop, add the Class instance to the Dictionary:
myDict.Add ComponentsSignalsList.getName, ComponentsSignalsList
Now when changing Page in the MultiPage widget:
Private Sub Controller_MultiPage_Change()
If isLoading Then Exit Sub 'avoid errors and undue behavior while initializing the MultiPage widget
Dim locControl As Control
Dim controlType As String: controlType = "ListView"
With Controller_MultiPage
For Each locControl In .Pages(.value).Controls
If InStr(1, TypeName(locControl), controlType) > 0 Then
Call Check_CheckBoxes(locControl)
End If
Next
End With
End Sub
Private Sub Check_CheckBoxes(argListView As listView)
If argListView.CheckBoxes = False Then Exit Sub 'some ListViews don't have checkboxes
Dim myItem As ListItem
For Each myItem In argListView.ListItems
With myItem
.Checked = myDict.Item(argListView.name).getStatus(.Text)
End With
Next
End Sub
when ticking/unticking a checkbox (note the the ItemChecked event handler is defined in another Class Public WithEvents, where the handler calls this method passing both the ListView ID and the Item object) :
Public Sub ListViewsEvents_ItemCheck(argListView As listView, argItem As MSComctlLib.ListItem)
With argItem
myDict.Item((argListView .name).setStatus argName:=.Text, argValue:=.Checked
End With
End Sub
I just found the answer to the same problem that I also had and I feel so stupid. I had the first column of the Listview set to Width = 0... and thus the checkboxes would no longer show.
I gave it a width and everithing is back to normal...

Display controls with order during For-Next loop vba

I have a VBA application with a lot of controls.
I would like accessing the controls with an order of reading during For-Next loop.
' Parcours des contrĂ´les de l'userform
For Each cCont In Me.Controls
' TypeName(cCont)
MsgBox (cCont.Name)
Next cCont
Actually, I think I am accessing with creation date...
Do you know if I could configure the order of reading ?
Thanks
One way to do this is to sort them by the TabIndex property. Set the tab indices to the desired order, then use this:
Private Sub test()
Dim cCont As Control
Dim i As Integer
Dim maxIndex As Integer
Dim controls As Object
Dim key As Variant
Set controls = CreateObject("Scripting.Dictionary")
'Add controls to dictionary, key by tabindex property
For Each cCont In Me.controls
maxIndex = IIf(maxIndex < cCont.TabIndex, cCont.TabIndex, maxIndex)
controls.Add cCont.TabIndex, cCont
Next cCont
'Get controls in order
For i = 0 To maxIndex
If controls.exists(i) Then
MsgBox controls(i).Name
End If
Next i
End Sub
The posted code is a great solution, which I made workable for me with this minor change. I passed the user form, because I used the code in a module. I excluded Label, CommandButton, and Image in order to make Valon Miller's code work for me, otherwise the depicted runtime error wouldruntime error '-2147352573 (800 200037': Member not found show.
Private Sub test(frm As UserForm)
Dim cCont As Control
Dim i As Integer
Dim maxIndex As Integer
Dim controls As Object
Dim key As Variant
Set controls = CreateObject("Scripting.Dictionary")
'Add controls to dictionary, key by tabindex property
For Each cCont In frm.controls
If TypeName(cCont) <> "Label" And _
TypeName(cCont) <> "Image" And _
TypeName(cCont) <> "CommandButton" Then
maxIndex = IIf(maxIndex < cCont.TabIndex, cCont.TabIndex, maxIndex)
controls.Add cCont.TabIndex, cCont
End If
Next cCont
'Get controls in order
For i = 0 To maxIndex
If controls.exists(i) Then
Debug.Print controls(i).Name & vbTab & i
MsgBox controls(i).Name
End If
Next i
End Sub
--------------------------------------------
Originally I needed a solution to get the order of sql statements synced with the order of my form controls. I wanted to do this:
fld1 = recordset1.value
fld2 = recordset2.value
fld3 = recordset3.value
was looking for a solution to get my controls and my SQL statement in order like field1 -> recordset.value1.
So instead of ordering my controls using the taborder, I created control arrays. i.e.
sub useControlArray()
dim a as variant, rs as new recordset, strSQL as string
strSQL = "select fld1, fld2, fld3 from table"
rs.open strSQL, connection
a = array("fld1", "fld2", "fld3")
'This array would help like this:
for i = lbound(a) to ubound(a)
frm.controls(a(i)) = rs(i)
debug.print frm.controls(a(i)) ' Form is a user form
next i
end sub
This would restrict the controls to the number of controls needed to fill them using the same order as in my SQL statement and I did not need to pay attention to whether or not a control would exist.
I hope this is helpful.

Compile error "expected: end of statement"

I have the following calcScores function written:
Function calcScores(category As String) As Integer
Dim count As Integer
count = 0
For Each Ctl In UserForm1.Controls
If Ctl.Tag = category And TypeName(Ctl) = "CheckBox" Then
Dim box As MSForms.CheckBox
Set box = Ctl
If box.Value = True Then
count = count + 1
End If
End If
Next
calcScores = count
End Function
This function takes a tag named "category" as a string and then checks the form for all check boxes with that tag and counts the ones that are checked. I know it works and counts the right number, because I have slightly edited it to output it's value to a label on the form instead of returning it.
When I try to call it in another function like this:
Function sortScores()
Dim scores(0 to 5) as Integer
scores(0) = calcScores "rChk"
**CODE CONTINUES**
End Function
I get an error that says "Expected: End of Statement" as soon as I leave the line that assigns the function's return to scores(0). calcScores is assigned before sortScores, and was succesfully called in a sub before using the same syntax.
Any idea what the error could be?
Call you function like this
scores(0) = calcScores("rChk")
Functions are called like that. Subs are called by
subName argument

VBA (Excel) Dictionary on Mac?

I have an Excel VBA project that makes heavy use of Windows Scripting Dictionary objects. I recently had a user attempt to use it on a Mac and received the following error:
Compile Error: Can't find project or library
Which is the result of using the Tools > References > Microsoft Scripting Runtime library.
My question is, is there a way to make this work on a Mac?
The following are the 3 cases I can think of as being possible solutions:
Use a Mac plugin that enables use of Dictionaries on Macs (my favorite option if one exists)
Do some kind of variable switch like the following:
isMac = CheckIfMac
If isMac Then
' Change dictionary variable to some other data type that is Mac friendly and provides the same functionality
End If
Write 2 completely separate routines to do the same thing (please let this not be what needs to happen):
isMac = CheckIfMac
If isMac Then
DoTheMacRoutine
Else
DoTheWindowsRoutine
End If
Pulling the Answer from the comments to prevent link rot.
Patrick O'Beirne # sysmod wrote a class set that addresses this issue.
Be sure to stop by Patirk's Blog to say thanks! Also there is a chance he has a newer version.
save this as a plain text file named KeyValuePair.cls and import into Excel
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "KeyValuePair"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
'Unrestricted class just to hold pairs of values together and permit Dictionary object updating
Public Key As String
Public value As Variant
save this as a plain text file named Dictionary.cls and import into excel
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "Dictionary"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
Option Explicit
'Collection methods: Add, Count, Item, Remove
'Dictionary : .Add(Key as string, Item as variant), .CompareMode, .Count, .Exists(Key); _
.Item(Key) - writeable, .Items, .Keys, .Remove(Key), .RemoveAll
'plus KeyValuePairs collection, KeyValuePair(Index as long), Tag as variant
' 25-11-2011 KeyValuePair helper object
Public KeyValuePairs As Collection ' open access but allows iteration
Public Tag As Variant ' read/write unrestricted
Private Sub Class_Initialize()
Set KeyValuePairs = New Collection
End Sub
Private Sub Class_Terminate()
Set KeyValuePairs = Nothing
End Sub
' in Scripting.Dictionary this is writeable, here we have only vbtextCompare because we are using a Collection
Public Property Get CompareMode() As VbCompareMethod
CompareMode = vbTextCompare '=1; vbBinaryCompare=0
End Property
Public Property Let Item(Key As String, Item As Variant) ' dic.Item(Key) = value ' update a scalar value for an existing key
Let KeyValuePairs.Item(Key).value = Item
End Property
Public Property Set Item(Key As String, Item As Variant) ' Set dic.Item(Key) = value ' update an object value for an existing key
Set KeyValuePairs.Item(Key).value = Item
End Property
Public Property Get Item(Key As String) As Variant
AssignVariable Item, KeyValuePairs.Item(Key).value
End Property
' Collection parameter order is Add(Item,Key); Dictionary is Add(Key,Item) so always used named arguments
Public Sub Add(Key As String, Item As Variant)
Dim oKVP As KeyValuePair
Set oKVP = New KeyValuePair
oKVP.Key = Key
If IsObject(Item) Then
Set oKVP.value = Item
Else
Let oKVP.value = Item
End If
KeyValuePairs.Add Item:=oKVP, Key:=Key
End Sub
Public Property Get Exists(Key As String) As Boolean
On Error Resume Next
Exists = TypeName(KeyValuePairs.Item(Key)) > "" ' we can have blank key, empty item
End Property
Public Sub Remove(Key As String)
'show error if not there rather than On Error Resume Next
KeyValuePairs.Remove Key
End Sub
Public Sub RemoveAll()
Set KeyValuePairs = Nothing
Set KeyValuePairs = New Collection
End Sub
Public Property Get Count() As Long
Count = KeyValuePairs.Count
End Property
Public Property Get Items() As Variant ' for compatibility with Scripting.Dictionary
Dim vlist As Variant, i As Long
If Me.Count > 0 Then
ReDim vlist(0 To Me.Count - 1) ' to get a 0-based array same as scripting.dictionary
For i = LBound(vlist) To UBound(vlist)
AssignVariable vlist(i), KeyValuePairs.Item(1 + i).value ' could be scalar or array or object
Next i
Items = vlist
End If
End Property
Public Property Get Keys() As String()
Dim vlist() As String, i As Long
If Me.Count > 0 Then
ReDim vlist(0 To Me.Count - 1)
For i = LBound(vlist) To UBound(vlist)
vlist(i) = KeyValuePairs.Item(1 + i).Key '
Next i
Keys = vlist
End If
End Property
Public Property Get KeyValuePair(Index As Long) As Variant ' returns KeyValuePair object
Set KeyValuePair = KeyValuePairs.Item(1 + Index) ' collections are 1-based
End Property
Private Sub AssignVariable(variable As Variant, value As Variant)
If IsObject(value) Then
Set variable = value
Else
Let variable = value
End If
End Sub
Public Sub DebugPrint()
Dim lItem As Long, lIndex As Long, vItem As Variant, oKVP As KeyValuePair
lItem = 0
For Each oKVP In KeyValuePairs
lItem = lItem + 1
Debug.Print lItem; oKVP.Key; " "; TypeName(oKVP.value);
If InStr(1, TypeName(oKVP.value), "()") > 0 Then
vItem = oKVP.value
Debug.Print "("; CStr(LBound(vItem)); " to "; CStr(UBound(vItem)); ")";
For lIndex = LBound(vItem) To UBound(vItem)
Debug.Print " (" & CStr(lIndex) & ")"; TypeName(vItem(lIndex)); "="; vItem(lIndex);
Next
Debug.Print
Else
Debug.Print "="; oKVP.value
End If
Next
End Sub
'NB VBA Collection object index is 1-based, scripting.dictionary items array is 0-based
'cf Scripting.Dictionary Methods s.Add(Key, Item), s.CompareMode, s.Count, s.Exists(Key); _
s.Item(Key) - updateable, s.Items, s.Key(Key), s.Keys, s.Remove(Key), s.RemoveAll
'Scripting.Dictionary has no index number; you can index the 0-based variant array of Items returned
' unlike Collections which can be indexed starting at 1
'Efficient iteration is For Each varPair in thisdic.KeyValuePairs
'Another difference I introduce is that in a scripting.dictionary, the doc says
' If key is not found when changing an item, a new key is created with the specified newitem.
' If key is not found when attempting to return an existing item, a new key is created and its corresponding item is left empty.
'but I want to raise an error when addressing a key that does not exist
'similarly, the scripting.dictionary will create separate integer and string keys for eg 2
Patirk's implementation doesn't work for MS Office 2016 on Mac. I made use of the implementation by Tim Hall.
Here is the link: https://github.com/VBA-tools/VBA-Dictionary
Also import of cls files into Excel doesn't work in MS Office 2016 on Mac as of September 2017. So I had to create a class module and to copy and paste the contents of Dictionary.cls manually in that module while removing meta info from Dictionary.cls such as VERSION 1.0 CLASS, BEGIN, END, Attribute.
I have at last updated the files for Excel 2016 for Mac.
http://www.sysmod.com/Dictionary.zip
(capital D in Dictionary)
Unzip this and import the class files (tested in Excel 2016 for Mac 16.13 Build 424, 27-Apr-2018)
My bug report to MS is at answers.microsoft.com
Excel 16.13 for Mac User Defined Class passed as parameter all properties are Null
Let me know if I've missed anything else!
Good luck,
Patrick O'Beirne

Resources