I cannot figure out how to access object properties in an array using VBA. I have created an array like:
Dim objectArray(10) as Variant
Dim counter as Integer 'used to move to next element in array
Next, I declared an object and store it in the array:
Dim object as Variant
objectArray(0) = object 'object stored in array[0]
counter = counter + 1 'increment counter
I want to pass the array to a function.
Call function(objectArray())
That function receives the array of objects like:
Public function(objectArray() as Variant)
So far, it seems to have worked when I have debugged it. My objectArray() seems to contain the object. When I store one object in the array, the debuger shows
objectArray(0)(1,1) .... 'this is in the Watch section of the debugger
I want to access the properties of that object in the first position of the array. That object will contain a name, several values, and a date. I've been trying to access the properties on that object like
Dim separateVar as Variant 'declare new var to hold "name"
separateVar = objectArray(0)(1,1).Value
However, when I run the macro, I get "Some Error Occured, 13, Type Mismatch". Am I accessing the object property values incorrectly?
Any help would be appreciated. Finding articles about accessing objects is easy, but finding ones about accessing their individual properties has been very difficult.
Try this.
Sub T()
Dim objInner1(1) As String
objInner1(0) = "Hello"
objInner1(1) = "World 1"
Dim objInner2(1) As String
objInner2(0) = "Hello"
objInner2(1) = "World 2"
Dim objInner3(1) As String
objInner3(0) = "Hello"
objInner3(1) = "World 3"
Dim objOuter(2) As Variant
objOuter(0) = objInner1
objOuter(1) = objInner2
objOuter(2) = objInner3
PrintArray objOuter
End Sub
Sub PrintArray(objArray As Variant)
Dim idx As Long
For idx = LBound(objArray) To UBound(objArray)
Debug.Print objArray(idx)(0) & " " & objArray(idx)(1)
Next idx
End Sub
'Hello World 1
'Hello World 2
'Hello World 3
Related
hi I have a code that can transform the lower case letters of each element of a String array using for loop. the problem is only the last element is appearing on the output(label) but shows fine on the debug output
Dim lst() = {"apple", "banana", "cherry"}
For Each item As String In lst
Dim array() As Char = item.ToCharArray
array(0) = Char.ToUpper(array(0))
Dim newS = New String(array)
Dim value As String = String.Join("/", newS)
TextBox1.Text = value
Debug.Write(value)
Output.Text = value
Next
Debug.WriteLine("")
this is the problem that occurs, it changes the label into an into the last element with the uppercase letter as it it is meant to be, but
i want the output to be the same as the debug output which is
AppleBananaCherry
You don't need to explicitly define a Char array. A String has a Char(index) property which is the Default property. We can use this directly on the String. MyString(index)
We assign the new String that is returned by the Replace method to an element of the array. index starts at 0, the default value of an Integer, and is incremented on each iteration of the For Each loop.
Finally, after the For Each, we assign the Join to the label.
Your code and where it went wrong.
Private Sub Button4_Click(sender As Object, e As EventArgs) Handles Button4.Click
Dim lst() = {"apple", "banana", "cherry"}
For Each item As String In lst
Dim array() As Char = item.ToCharArray
array(0) = Char.ToUpper(array(0))
Dim newS = New String(array)
Dim value As String = String.Join("/", newS)
TextBox1.Text = value 'Overwrites the last value you assigned to the Text property
Debug.Write(value) 'Adds on to the last value it wrote in the debug window.
Label1.Text = value 'Overwrites the last value you assigned to the Text property
Next
Debug.WriteLine("")
End Sub
Corrected code.
Private Sub Button3_Click(sender As Object, e As EventArgs) Handles Button3.Click
Dim lst = {"apple", "banana", "cherry"}
Dim index As Integer
For Each item As String In lst
lst(index) = item.Replace(item(0), Char.ToUpper(item(0)))
index += 1
Next
Label1.Text = String.Join("", lst)
End Sub
This is WinForms but the code you care about shouldn't be any different, just the Event procedure format.
You can even make it in a single line without an explicit loop.
Dim lst = {"apple", "banana", "cherry"}
Output.Text = String.Join("", lst.Select(Function(x) CultureInfo.CurrentCulture.TextInfo.ToTitleCase(x))))
While it seems very "smart" there are a couple of things to consider.
If the original list is very big (meaning thousands of strings) this
is less efficient because the Linq Select and the subsequent Join
will create a new list doubling the memory used.
If you want to change more than the first character, the ToTitleCase
method cannot work
I got a problem I just cant fix. I have a string, want to split it at ";" (that is working) and then trim the values.
Dim cell As String
Dim objects() As String
cell = Range("X74").Text
objects= Trim(Split(cell, ";"))
I get my error on the Trim-function. I then tried the following approach:
For Each object In objects
object = Trim(object)
Debug.Print object
Next
This works, but doesnt save the trimmed value to my objects-array.
Despite naming your variables objects and object, they are an array of simple Strings resp. a simple String, and in VBA a string is not an object.
In your For Each-Loop, you are copying a string to the variable object, and no matter what you do with it, it doesn't change the content of the objects-array.
If you really need to change the content of the objects-array, use a loop like that:
Dim i As Long
For i = LBound(objects) To UBound(objects)
objects(i) = Trim(objects(i))
Debug.Print objects(i)
Next
And you should think about changing the name of your variables...
I would try to avoid vba names as variables:
Sub tst()
Dim yourcell As String, i As Long
Dim yourobjects() As String
yourcell = Range("X74").Text
yourobjects = Split(yourcell, ";")
For i = LBound(yourobjects) To UBound(yourobjects)
yourobjects(i) = Trim(yourobjects(i))
Debug.Print yourobjects(i)
Next i
End Sub
I am passing the content of a two dimensional array (first column header and than all column numbers) to a userform named Listbox1. Here the user selects items that are passed to Listbox2 and at last the user activates a process that sends data to a webservice.
So far I have managed to populate Listbox1 with data headers only but have the problem to retain all values in listbox2, not only the header. I thought of creating a private variable at the userform level to store the full array but it seems to generate a type mismatch issue with the let/get properties.
What is the best strategy to handle a full set of data with two lists?
Private pArr As Variant
Public Property Get arr() As Variant
Set arr = pArr
End Property
Public Property Let arr(Value As Variant)
Set pArr = Value
End Property
Private Sub LoadModelData()
Dim i As Integer
Dim myArray As Variant
Dim v As Variant
Dim mystring As String
myArray = ReadModelData(this is the function returning the array data from a range)
Set pArr = myArray
For i = LBound(myArray) To UBound(pArr)
If pArr(i)(1, 1) <> vbNullString Then
frmListModelItms.List1.AddItem (pArr(i)(1, 1))
End If
Next i
End Sub
my understading is arr property of your class is an array, not an object
hence don't use Set keyword:
Private pArr As Variant
Public Property Get arr() As Variant
arr = pArr
End Property
Public Property Let arr(Value As Variant)
pArr = Value
End Property
I am trying to create a VBA function which writes an array to a .NET System.Collections.ArrayList and returns it.
So far I have:
Function arrayToArrayList(inArray As Variant) As ArrayList
'function to take input array and output as arraylist
If IsArray(inArray) Then
Dim result As New ArrayList
Dim i As Long
For i = LBound(inArray) To UBound(inArray)
result.Add inArray(i) 'throws the error
Next i
Else
Err.Raise 5
End If
Set arrayToArrayList = result
End Function
Called with (for example)
Sub testArrayListWriter()
'tests with variant/string array
'intend to pass array of custom class objects
Dim result As ArrayList
Dim myArray As Variant
myArray = Split("this,will,be,an,array",",")
Set result = arrayToArrayList(myArray)
End Sub
But I get the error
Variable uses an Automation type not supported in Visual Basic
Presumably because my array is not the correct type (maybe Variant). However
Dim v As Variant, o As Variant
v = "test_string"
set o = New testClass
result.Add v 'add a string in variant form
result.Add o 'add an object in variant form
raises no errors, so the problem isn't directly to do with the Variant type
What's going on here, and is there any way of writing an array of unspecified type to the ArrayList, or will I have to define the type of inArray?
Change
result.Add inArray(i)
to
result.Add CVar(inArray(i))
Two ways to do this. First is late binding if you don't have a reference to mscorlib.dll. You'll see that I've changed your ArrayList to Object for declaring the function and the return value (retVal). The test sub also declares result as Object. The retVal and result are both late bound to System.Collections.ArrayList. You also need to declare inArray and myArray as dynamic arrays of string. In your example, Split expects returns an array of strings, so you need provide a declared dynamic array of strings. If you wanted to other object types, then you'd pass those declared object types to your function.
Private Function arrayToArrayList(inArray() As String) As Object
'function to take input array and output as arraylist
Dim retVal As Object
Set retVal = CreateObject("System.Collections.ArrayList")
If IsArray(inArray) Then
Dim i As Long
For i = LBound(inArray) To UBound(inArray)
retVal.Add inArray(i)
Next i
Else
Err.Raise 5
End If
Set arrayToArrayList = retVal
End Function
Public Sub testArrayListWriter()
'tests with variant/string array
'intend to pass array of custom class objects
Dim result As Object
Dim myArray() As String
Set result = CreateObject("System.Collections.ArrayList")
myArray = Split("this,will,be,an,array", ",")
Set result = arrayToArrayList(myArray)
End Sub
The second way is to add a reference to mscorlib.dll through the Tools->Reference menu item. When the dialog box appears you'll have to click browse. You'll need to browse to C:\Windows\Microsoft.NET\Framework and then select the folder with the current version of .NET on your machine. In that folder you'll find mscorlib.dll and mscorelib.tlb. Highlight the file ending in .TLB file, click the Open button, on the Tools Reference dialog, click OK.
Now you can use any of the classes in Systems.Collections directly in your code. This is called early binding and looks like this
Private Function arrayToArrayList(inArray() As String) As ArrayList
'function to take input array and output as arraylist
Dim retVal As ArrayList
If IsArray(inArray) Then
Dim i As Long
For i = LBound(inArray) To UBound(inArray)
retVal.Add inArray(i)
Next i
Else
Err.Raise 5
End If
Set arrayToArrayList = retVal
End Function
Public Sub testArrayListWriter()
'tests with variant/string array
'intend to pass array of custom class objects
Dim result As ArrayList
Dim myArray() As String
myArray = Split("this,will,be,an,array", ",")
Set result = arrayToArrayList(myArray)
End Sub
I think the problem is with the assignment of the return value from the Split function to a variable that has not been decalred anywhere.
Try adding:
Dim myArray() as string
inside the testArrayListWriter() procedure.
I'm working on an Excel VBA addin that exchanges objects with a COM server, something like this:
'get an array of objects
Dim Ents() As ISomething
ComObject.GetEntities Ents
'send an array with 10 objects
ReDim Ents(9)
Set Ents(0) = ...
...
ComObject.SetEntities Ents
Getting the arrays works well: if the array contains objects it works as expected, if the array is empty then UBound(Ents) = -1 and everything works as expected.
Sending the arrays works only with not empty arrays, because I can't Redim Ents(-1), and Eraseing the array both VBA and the COM server crash: Debug.Print UBound(Ents) crashes in VBA and who knows what crashes the server.
It looks like the Erase statement leaves the array undefined/corrupted rather than empty.
EDIT (clarification to a comment below):
Executing this code it crashes because it can't calculate the UBound:
Sub Test()
Dim Ents() As ISmartId
Debug.Print UBound(Ents)
End Sub
But if you add Ents to the watch window, then set a break point to the Debug.Print line and execute, the debugger shows the ISmartId(0 to -1) in the Type column. After this the execution continues without crash, and the Debug window shows the expected -1.
It looks like the debugger was able to correctly initialize the empty array the way I need it just to show its value.
For objects, you can do this just by copying an undefined array into a variant and back:
Dim o() As Worksheet
Dim v As Variant
v = o
o = v
For non-objects, make an empty array in a variant and then change its type code:
Private Declare Sub GetMem2 Lib "msvbvm60" (src As Any, dest As Any)
Dim i() as Long
Dim v as Variant
v = Array()
Dim NewTypeCode As Integer
NewTypeCode = vbArray Or vbLong
GetMem2 NewTypeCode, v
i = v
If you need a fresh array you could create a "factory" function to return one
Function FreshArray() As ISomething()
Dim rv() As ISomething
FreshArray = rv
End Function
Ents = FreshArray()
ComObject.GetEntities Ents