redim property in class module - vba

I am pretty new to using classes in vba. I am trying to use an array as a property where the length of the array has to be variable. I was looking around for a way to do that, but I dont really understand how those properties work.
so I define my array in the class module
Private pTestArray() As String
and the properties to get and set values
Private Property Get TestArrayValue(index As Long) As String
TestArrayValue = qTestArray(index)
End Property
Private Property Let ArrayValue(index As Long, strValue As String)
pTestArray(index) = strValue
End Property
but I cant find a way to redim the array. any clues?
Thanks
C

So you want to resize on assignment? then you can check & handle the bounds in the Let property;
Private Property Let ArrayValue(index As Long, strValue As String)
If index > UBound(pTestArray) Then ReDim Preserve pTestArray(index)
pTestArray(index) = strValue
End Property
You will also need to initially dimension with redim pTestArray(0) in the Class_Initialize event.

Related

VBA. Access to an array element of the Variant. An array is a class property

I have a class with an ArrSignalCabel variable. In this variable I write an array of objects.
Property Get bottom of my class returns only an array. And I do not have access to the array element.
Option Explicit
Private ArrSignalCabel As Variant
Property Let ArrSignalCab(ByVal ArrValue As Variant)
ArrSignalCabel = ArrValue
End Property
Property Get ArrSignalCab() As Variant
ArrSignalCab = ArrSignalCabel
End Property
Public Property Get ArrSignalCabIn(index As Integer) As Variant ' not work
ArrSignalCabIn(index) = ArrSignalCabel(index)
End Property
////////////////
NameObjTM2 = TempPanel.ArrCabelPan(i).ArrSignalCabIn(0) ' not work
NameObjTM3 = TempPanel.ArrCabelPan(i).ArrSignalCab ' work
NameObjTM4= NameObjTM3(0).NameSig ' work
PS:
TempPanel - an object that contains an array of objects(ArrCabelPan).
ArrCabelPan - the class structure is identical to that described.
Have you tried ArrSignalCabIn = ArrSignalCabel(index)?
The name of the property should match what you declared in the Property Get, like Function.
I find answer
Public Property Get ArrSignalCabIn(Index As Integer) As Variant
If (ArrSignalCabel(Index) Is Nothing) Then Set ArrSignalCabel(Index) = New Signal
Set ArrSignalCabIn = ArrSignalCabel(Index)
End Property
Thanks to those who helped

Compile error: Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions

I'm struggling with a little bit of VBa and Excel. I need to create a structure in VBa, which is a Type. The problem I have is, I get an error message when I try to execute the code! I feel I need to explain how I have arrived where I am in case I've made an error.
I have read that to create a type, it needs to be made public. As such I created a new Class (under Class Modules). In Class1, I wrote
Public Type SpiderKeyPair
IsComplete As Boolean
Key As String
End Type
And within ThisWorkbook I have the following
Public Sub Test()
Dim skp As SpiderKeyPair
skp.IsComplete = True
skp.Key = "abc"
End Sub
There is no other code. The issue I have is I get the error message
Cannot define a public user-defined type within an object module
If I make the type private I don't get that error, but of course I can't access any of the type's properties (to use .NET terminology).
If I move the code from Class1 into Module1 it works, but, I need to store this into a collection and this is where it's gone wrong and where I am stuck.
I've updated my Test to
Private m_spiderKeys As Collection
Public Sub Test()
Dim sKey As SpiderKeyPair
sKey.IsComplete = False
sKey.Key = "abc"
m_spiderKeys.Add (sKey) 'FAILS HERE
End Sub
Only user-defined types defined in public object modules can be coerced to or from a variant or passed to late-bound functions
I have looked into this but I don't understand what it is I need to do... How do I add the SpiderKeyPair to my collection?
Had the exact same problem and wasted a lot of time because the error information is misleading. I miss having List<>.
In Visual Basic you can't really treat everything as an object. You have Structures and Classes which have a difference at memory allocation: https://learn.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/structures-and-classes
A Type is a structure (so are Arrays), so you if you want a "List" of them you better use an Array and all that comes with it.
If you want to use a Collection to store a "List", you need to create a Class for the object to be handled.
Not amazing... but it is what the language has available.
You seem to be missing basics of OOP or mistaking VBA and VB.NET. Or I do not understand what are you trying to do. Anyhow, try the following:
In a module write this:
Option Explicit
Public Sub Test()
Dim skpObj As SpiderKeyPair
Dim m_spiderKeys As New Collection
Dim lngCounter As Long
For lngCounter = 1 To 4
Set skpObj = New SpiderKeyPair
skpObj.Key = "test" & lngCounter
skpObj.IsComplete = CBool(lngCounter Mod 2 = 0)
m_spiderKeys.Add skpObj
Next lngCounter
For Each skpObj In m_spiderKeys
Debug.Print "-----------------"
Debug.Print skpObj.IsComplete
Debug.Print skpObj.Key
Debug.Print "-----------------"
Next skpObj
End Sub
In a class, named SpiderKeyPair write this:
Option Explicit
Private m_bIsComplete As Boolean
Private m_sKey As String
Public Property Get IsComplete() As Boolean
IsComplete = m_bIsComplete
End Property
Public Property Get Key() As String
Key = m_sKey
End Property
Public Property Let Key(ByVal sNewValue As String)
m_sKey = sNewValue
End Property
Public Property Let IsComplete(ByVal bNewValue As Boolean)
m_bIsComplete = bNewValue
End Property
When you run the Test Sub in the module you get this:
Falsch
test1
-----------------
-----------------
Wahr
test2
Pay attention to how you initialize new objects. It happens with the word New. Collections are objects and should be initialized as well with New.

3 Dimentional Dictionary

I'm trying make to a 3 Dimension Dictionary to store the data in the form of tools(material)(part)(attribute), and I have managed to create the Dictionary like this:
Dim Tools As New Dictionary(Of String, Dictionary(Of String, Dictionary(Of String, Decimal)))
And what I basically want to do is have some subs that manage that for me instead of dealing with that mess, and I want it to be like this like this:
Add_Attribute("Iron", "Pickaxe Head", "Durability", 204)
Get_Attribute("Stone", "Pickaxe Head", "Mining Speed")
Any answers would be greatly be appreciated.
My comment was not worded properly.
Create a class with add/get attributes function that accepts 3 parameters.
Concatenate the parameters and use it as dictionary key.
Option Explicit
Dim oDict As Dictionary
Public Function Add_Attribute(psParam1 As String, psParam2 As String, psParam3 As String, psValue As String)
Dim sKey As String
sKey = BuildKey(psParam1, psParam2, psParam3)
If oDict.Exists(sKey) Then
oDict.Item(sKey) = psValue
Else
oDict.Add sKey, psValue
End If
End Function
Public Function Get_Attribute(psParam1 As String, psParam2 As String, psParam3 As String) As String
Dim sKey As String
sKey = BuildKey(psParam1, psParam2, psParam3)
If oDict.Exists(sKey) Then
Get_Attribute = oDict.Item(sKey)
Else
Get_Attribute = ""
End If
End Function
Private Sub Class_Initialize()
Set oDict = New Dictionary
End Sub
Private Function BuildKey(psParam1 As String, psParam2 As String, psParam3 As String) As String
BuildKey = Join(Array(psParam1, psParam2, psParam3), "#")
End Function
Private Sub Class_Terminate()
Set oDict = Nothing
End Sub
Jules' answer of a custom class and concatenation of the three strings as a key will work very nicely for you and is a neat solution to your problem.
I'm posting another answer here for anyone who wants more of a dot notation style of solution. So one of the lines in your example could look something like:
mTools("Pickaxe Head").Attr("Durability").Material("Iron") = 204
I'm guessing you're deriving the values from a comboxbox or something similar, so working with strings might serve you fine. However, if you wished, you could go one stage further and create objects for the Attributes and Material parameters to achieve true dot notation (I didn't do the Parts parameter but you could do that one too):
mTools("Pickaxe Head").Durability.OnIron = 204
From a development point of view, the time consuming part would be to create all the parameter objects and keys, but if you are intending to manipulate the data anything more than trivially, it could make your life easier further down the track.
For your own project, are you certain that the data is genuinely 3 dimensional? Perhaps it's just the variable names that you've picked, but it seems as though you have one main object, ie the part (Pickaxe Head) which has some attributes (Durability and Mining Speed) which themselves have values based on the material they're operating on (Stone and Iron). Structurally, could it look like this?:
In terms of the code for this solution, create three classes. I've called them clsKeys, clsMaterials and clsPart.
For your clsKeys, the code is simply your field names:
Public Durability As String
Public MiningSpeed As String
Public Iron As String
Public Stone As String
For clsPart, the code contains the object names and a means of accessing them by string:
Public Name As String
Public Durability As New clsMaterials
Public MiningSpeed As New clsMaterials
Private mProperties As New Collection
Public Property Get Attr(field As String) As clsMaterials
Set Attr = mProperties(field)
End Property
Private Sub Class_Initialize()
With Keys
mProperties.Add Durability, .Durability
mProperties.Add MiningSpeed, .MiningSpeed
End With
End Sub
clsMaterials is similar:
Public OnStone As Integer
Public OnIron As Integer
Private mProperties As New Collection
Public Property Let Material(field As String, value As Variant)
mProperties.Remove field
mProperties.Add value, field
End Property
Public Property Get Material(field As String) As Variant
Material = mProperties(field)
End Property
Private Sub Class_Initialize()
With Keys
mProperties.Add OnStone, .Stone
mProperties.Add OnIron, .Iron
End With
End Sub
These classes can take as many objects as you like. You'll note I've instantiated the objects in the declaration which isn't best form but I've done it in the interest of space.
Finally, in a Module you need 3 routines: one to create the field keys, one to populate the data and one to retrieve it.
For the keys:
Option Explicit
Public Keys As clsKeys
Private mTools As Collection
Sub CreateKeys()
Set Keys = New clsKeys
With Keys
.Durability = "Durability"
.MiningSpeed = "Mining Speed"
.Iron = "Iron"
.Stone = "Stone"
End With
End Sub
For data population:
Sub PopulateData()
Dim oPart As clsPart
Set mTools = New Collection
Set oPart = New clsPart
With oPart
.Name = "Pickaxe Head"
'You could use dot notation
.Durability.OnIron = 204
.Durability.OnStone = 100
'Or plain strings
.Attr("Mining Speed").Material("Stone") = 50
.Attr("Mining Speed").Material("Iron") = 200
mTools.Add oPart, .Name
End With
End Sub
and for data retrieval:
Sub RetrieveValue()
Dim oPart As clsPart
Dim v As Variant
Set oPart = mTools("Pickaxe Head")
With oPart
'Using dot notation
v = oPart.Durability.OnIron
Debug.Print v
'Using plain strings
v = oPart.Attr("Durability").Material("Stone")
Debug.Print v
End With
'Or even without assigning the oPart variable
v = mTools("Pickaxe Head").Attr("Mining Speed").Material("Iron")
Debug.Print v
End Sub

Update Object Properties Through Reflection Based On Property Name

I have a custom class with approximately 30 properties. (I.E. headername, headersize...)
I would like to be able to update a property by the name of the property as a string.
I am looking at using reflection (not 100% sure if this is the best method) instead of a large case statement that would continuously need to be updated if I add to the main object.
Through things I have found I have created the following Sub. I have put this in another location than the class of the object.
Public Sub UpdateValue(ByRef MainData As FilesMainData, ByVal SearchForObjectPartName As String, ByVal NewValue As Object)
Dim ObjectPartTypes() As System.Reflection.PropertyInfo = MainData.GetType().GetProperties()
Dim ObjectPartNames() As System.Reflection.FieldInfo = MainData.GetType().GetFields()
For I As Integer = 0 To ObjectPartNames.Count - 1
If ObjectPartTypes(I).Name.Equals(SearchForObjectPartName) Then
ObjectPartTypes(I).SetValue(ObjectPartTypes(I).GetType, NewValue,CType(I, Object) )
End If
Next
End Sub
1) I am getting some errors and know the code isn't right, but am unsure how to fix
2) I am unsure it's in the correct location.
Any help would be appreciated, thank you.
There are a few problems with the code:
You want to update the properties, but when iterating through the loop you are using the wrong count
Also you are using SetValue incorrectly, the first parameter must be the instance of the object containing the property
Here's the update method:
Public Sub UpdateValue(ByRef MainData As FilesMainData, ByVal SearchForObjectPartName As String, ByVal NewValue As Object)
Dim ObjectPartTypes() As System.Reflection.PropertyInfo = MainData.GetType().GetProperties()
For I As Integer = 0 To ObjectPartTypes.Count - 1
If ObjectPartTypes(I).Name.Equals(SearchForObjectPartName) Then
ObjectPartTypes(I).SetValue(MainData, NewValue)
End If
Next
End Sub

Variable in Array Variable

if i generate a label dynamically i can change the text as followed, variable is a string:
Form.Controls(variable).text = "test"
I now have a sub that will create some and will get some information out of arrays. I have a lot of them. I only want the function for tat specific array. I do not want a case or if. Thats why i was wondering if i can use a variable in the array variable. Sounds strange, here is what i mean:
Public TestArray() as String
Public Sub BuildStructure(ByVal Shelf As String)
Dim XMax as Integer
XMax = TestArray.GetLength(1)
End Sub
But instead of TestArray beeing hardcoded i want it to be replaced with Shelf. So whatever gets send into the sub will change the array that i'm using.
Is that possible some how or is the way totally wrong?
Thanks
You can use an arraylist object that you can easily add and remove items from at runtime. Here's the MSDN page: http://msdn.microsoft.com/en-us/library/7x4b0a97.aspx
Public TestArray As New ArrayList
Public Sub BuildStructure(ByVal Shelf As String)
TestArray.Add(Shelf)
End Sub
If you need an array, you can always call the .ToArray method on the ArrayList