Merge two Array Properties in Class - vb.net

I have a Class (TestClass) with the following three properties:
Private myArr1() As String
Private myArr2() As String
Private myFinalArray() As String
Private Sub Class_Initialize()
ReDim myArr1(0 To 3)
ReDim myArr2(0 To 2)
ReDim myFinalArray(0 To 6)
End Sub
Public Property Get arr1(ByVal index As Long) As Double
arr1 = myArr1(index)
End Property
Public Property Let arr1(ByVal index As Long, ByVal myvalue As Double)
myArr1(index) = myvalue
End Property
Public Property Get arr2(ByVal index As Long) As Double
...
Public Property Let arr2(ByVal index As Long, ByVal myvalue As Double)
...
Public Property Get FinalArray(ByVal index As Long) As Double
...
Public Property Let FinalArray(ByVal index As Long, ByVal myvalue As Double)
...
Here we have just two arrays that I fill with data:
Sub test()
Dim t As TestClass
Set t = New TestClass
For i = 0 To 3
t.arr1(i) = i
Next
For i = 0 To 2
t.arr2(i) = i
Next
t.GetFinalValues (t)
End Sub
My Problem now is that these array elements must be rearranged according to a confused pattern I want to write a property for that but it is not working. My idea was to add the following function to my class:
Public Function GetFinalValues(ByRef t As TestClass) As Double()
'Imput parameter arrX can ben the Value as well as the Bench arrays.
Dim arr1(2) As Double
Dim arr2(3) As Double
Dim i As Integer
Dim arrCollection(6) As Double
arrCollection(0) = t.arr1(0)
arrCollection(1) = t.arr2(0)
arrCollection(2) = t.arr2(1)
arrCollection(3) = t.arr1(1)
arrCollection(4) = t.arr2(2)
arrCollection(6) = t.arr1(2)
arrCollection(5) = t.arr2(3)
'Assign return object
For i = 0 To 6
FinalArray(i) = arrCollection(i)
Next i
GetFinalValues
End Function
If I run this the code stops at t.GetFinalValues(t) giving me the Errormessage: Object doesent support property or method. Anyone who can help me get this working? Or has a rebuild idea for a even better solution to that problem?
EDIT: I added vb.net since this might be a construction problem that is not specified to vba

You have two issues:
First you should remove the parentheses from this line:
t.GetFinalValues (t)
so it's just:
t.GetFinalValues t
Then your function needs to return a String array not Double, since that's the type of the private variable. Your class code becomes something like this:
Private Sub Class_Initialize()
ReDim myArr1(0 To 3)
ReDim myArr2(0 To 2)
ReDim myFinalArray(0 To 6)
End Sub
Public Property Get arr1(ByVal index As Long) As Double
arr1 = myArr1(index)
End Property
Public Property Let arr1(ByVal index As Long, ByVal myvalue As Double)
myArr1(index) = myvalue
End Property
Public Property Get arr2(ByVal index As Long) As Double
arr2 = myArr1(index)
End Property
Public Property Let arr2(ByVal index As Long, ByVal myvalue As Double)
myArr2(index) = myvalue
End Property
Public Property Get FinalArray(ByVal index As Long) As Double
FinalArray = myArr1(index)
End Property
Public Property Let FinalArray(ByVal index As Long, ByVal myvalue As Double)
myFinalArray(index) = myvalue
End Property
Public Function GetFinalValues(ByRef t As TestClass) As String()
'Imput parameter arrX can ben the Value as well as the Bench arrays.
Dim arr1(2) As Double
Dim arr2(3) As Double
Dim i As Integer
Dim arrCollection(6) As Double
arrCollection(0) = t.arr1(0)
arrCollection(1) = t.arr2(0)
arrCollection(2) = t.arr2(1)
arrCollection(3) = t.arr1(1)
arrCollection(4) = t.arr2(2)
arrCollection(6) = t.arr1(2)
arrCollection(5) = t.arr1(3)
'Assign return object
For i = 0 To 6
FinalArray(i) = arrCollection(i)
Next i
GetFinalValues = myFinalArray
End Function
Note that you were also trying to use t.arr2(3) which exceeds the number of elements in arr2 so I assumed you meant t.arr1(3)

Related

ContainsValue with Dictionary(Of String, Items)

How to know if a dictionary with multiple values contains specific value?
'Create dictionary
Dim testDictionary As New Dictionary(Of String, Items)
'Code to fill dictionary
'.......................
'.......................
'.......................
'Test if a specific value is contained in dictionary
Dim testValue as String = "TEST"
testDictionary.ContainsValue(testValue) 'This doesn't work
Public Class Items
Public Property Property1 As String
Public Property Property2 As String
Public Sub New()
End Sub
End Class
If you can define how to determine whether the dictionary contains that string, pass that logic into Enumerable.Any
Dim testValue As String = "TEST"
Dim contains = testDictionary.Any(Function(kvp) kvp.Value.Property1 = testValue OrElse kvp.Value.Property2 = testValue)
If contains Then
Dim containsEntries = testDictionary.Where(Function(kvp) kvp.Value.Property1 = testValue OrElse kvp.Value.Property2 = testValue)
End If
Since you reuse it for Any and Where, you can declare the predicate once
Dim predicate =
Function(kvp As KeyValuePair(Of String, Items))
Return kvp.Value.Property1 = testValue OrElse kvp.Value.Property2 = testValue
End Function
Dim contains = testDictionary.Any(predicate)
If contains Then
Dim containsEntries = testDictionary.Where(predicate)
End If
This is hard-coded to just these properties Property1 and Property2.
(you really don't need the Any if you want the entities; I just figured the Any answered your question "How to know if..." with a boolean)
If you want to check all public instance string properties, you can use reflection
Dim predicate =
Function(kvp As KeyValuePair(Of String, Items))
Return GetType(Items).
GetProperties(Reflection.BindingFlags.Public Or Reflection.BindingFlags.Instance).
Where(Function(pi) pi.PropertyType Is GetType(String)).
Aggregate(False, Function(pi1, pi2) pi1 Or (pi2.GetValue(kvp.Value) = testValue))
End Function
Dim containsWith = testDictionary.Any(predicate)
If containsWith Then
Dim containsEntries = testDictionary.Where(predicate)
End If

Determine the length of a generic type?

How can I at run-time determine the length of a generic type (with constraint Structure).
I need the byte count to create a view accessor of a MemoryMappedFile (which is initialized in the constructor).
Private goVirtualFile As MemoryMappedFile
Private glLength As Long = 0
Public Class CTest(Of T As Structure)
Public Sub Append(tData As T())
Dim iNumStruct As Integer = tData.Length 'Number of structures.
Dim iStructLen As Integer = ... 'Length of 1 structure in bytes.
Dim iBytes As Integer = iNumStruct * iStructLen 'Total length in bytes.
Using goViewStream = goVirtualFile.CreateViewAccessor(
glLength, iBytes)
...
End Using
End Sub
Public Function Read(lOffset As Long, iNumStruct As Integer) As T()
...
End Sub
End Class

Casting from class to interface in Excel VBA

In Excel 2013, I have two classes: LoadCase and LoadCombination, which implement interface ILoadCase.
The declaration for ILoadCase is:
Option Explicit
'' Public properties
Public Property Get Name() As String
End Property
Public Property Let Name(ByVal value As String)
End Property
Public Property Get ID() As Long
End Property
Public Property Let ID(ByVal valus As Long)
End Property
And the (partial) implementations for both LoadCase and LoadCombination are:
Option Explicit
Implements ILoadCase
'' Public properties
Public Property Get ILoadCase_Name() As String
ILoadCase_Name = pName
End Property
Private Property Let ILoadCase_Name(ByVal value As String)
pName = value
End Property
Public Property Get ILoadCase_ID() As Long
ILoadCase_ID = pID
End Property
Private Property Let ILoadCase_ID(ByVal value As Long)
pID = value
End Property
I've omitted code which is irrelevant to the implementation of the interface.
I then have a class BeamForces, which contains results for a particular ILoadCase object:
Option Explicit
Public Fx As Double
Public Fy As Double
Public Fz As Double
Public Mx As Double
Public My As Double
Public Mz As Double
Public ParentLoadCase As ILoadCase
I thought that with this I'd be able to do something like this:
Set currentBeamForces = New BeamForces
With currentBeamForces
.Fx = forces(0)
.Fy = forces(1)
.Fz = forces(2)
.Mx = forces(3)
.My = forces(4)
.Mz = forces(5)
Set .ParentLoadCase = TargetLoadCase
End With
Where TargetLoadCase is either a LoadCase or a LoadCombination, but this gives me an error every time.
I've coded this like I would in .NET and just expected that it would work, but does casting to an interface not work in VBA? Or am I going wrong here somewhere?
EDIT
More details. I first call the following method:
Public Function LoadBeamForcesAtNode(ByVal TargetBeam As Beam, ByVal TargetNode As Node, Optional ByVal TargetLoadCases As Collection = Nothing) As Boolean
Dim i As Integer
Dim currentLoadCase As Variant
Dim targetBeamForces As BeamForces
If TargetLoadCases Is Nothing Then
For Each currentLoadCase In Me.LoadCases.Items
Call TargetLoadCases.Add(currentLoadCase)
Next
For Each currentLoadCase In Me.LoadCombinations.Items
Call TargetLoadCases.Add(currentLoadCase)
Next
End If
'On Error GoTo ExitPoint
For Each currentLoadCase In TargetLoadCases
Set targetBeamForces = InstantiateBeamForces(TargetBeam, TargetNode, currentLoadCase)
If TargetNode Is TargetBeam.Node1 Then
Set TargetBeam.Forces1 = targetBeamForces
Else
Set TargetBeam.Forces2 = targetBeamForces
End If
Next
LoadBeamForcesAtNode = True
ExitPoint:
End Function
Where TargetLoadCases is a collection which can contain both LoadCase and LoadCombination objects.
The problem occurs in InstantiateBeamForces, the code for which is
Private Function InstantiateBeamForces(ByVal TargetBeam As Beam, ByVal TargetNode As Node, ByVal TargetLoadCase As Variant) As BeamForces
Dim forces(5) As Double
Dim currentBeamForces As BeamForces
Call Me.output.GetMemberEndForces(TargetBeam.ID, IIf(TargetNode Is TargetBeam.Node1, 0, 1), TargetLoadCase.ILoadCase_ID, forces, 0)
Set currentBeamForces = New BeamForces
With currentBeamForces
.Fx = forces(0)
.Fy = forces(1)
.Fz = forces(2)
.Mx = forces(3)
.My = forces(4)
.Mz = forces(5)
Set .ParentLoadCase = TargetLoadCase
End With
Set InstantiateBeamForces = currentBeamForces
End Function
Which creates a new BeamForces object and populates it with the values returned by the ...GetMemberEndForces(...) API COM call.
The problem is that the .ParentLoadCase property is nothing after the assignment, so I'm assuming an invalid cast...
** EDIT 2 **
Here is a screenshot of TargetLoadCase when I put a breakpoint in InstantiateBeamForces.
The ILoadCase member is Nothing, but I don't get why. Could this be the cause of the problem?

Add Class objects to Collection using a Function in VBA

I have this class:
Option Explicit
Public Code As String
Public ArticleType As String
Public Division As String
Public Devise As String
Public GroupePrix As String
Property Get CodeOnly() As String
CodeOnly = Replace(Code, "*", "")
End Property
And this function:
Function addFabricant(cod As String, art As String, div As String, dev As String, grp As String) As Fabricant
Dim Fab As New Fabricant
Fab.Code = cod
Fab.ArticleType = art
Fab.Division = div
Fab.Devise = dev
Fab.GroupePrix = grp
Set addFabricant = Fab
End Function
And I want to do something like this:
Set Fabricants = New Collection
'This is where the code fail with error 438
Fabricants.Add (addFabricant("Code", "Sample", " ", "DogeCoin", "420"))
...
But it does not work. I'm new to VBA so I may be missing something.
I know that :
Set test = addFabricant("Code", "Sample", " ", "DogeCoin", "420")
Fabricants.Add (test)
is working, but it would add double the code to add all the Fabricant this way.
I think you want "user defined types"
Public Type MyType
MyInt As Integer
MyString As String
MyDoubleArr(2) As Double
End Type
Then use it as a collection or an array.
Dim MyArr(2) As MyType
MyArr(0).MyInt = 31
MyArr(0).MyString = "VBA"
MyArr(0).MyDoubleArr(0) = 1
MyArr(0).MyDoubleArr(1) = 2
MyArr(0).MyDoubleArr(2) = 3
MyArr(1).MyInt = 32
MyArr(1).MyString = "is"
MyArr(1).MyDoubleArr(0) = 11
MyArr(1).MyDoubleArr(1) = 22
MyArr(1).MyDoubleArr(2) = 33
MyArr(2).MyInt = 33
MyArr(2).MyString = "cool"
MyArr(2).MyDoubleArr(0) = 111
MyArr(2).MyDoubleArr(1) = 222
MyArr(2).MyDoubleArr(2) = 333
I would create a wrapper class for your collection and add methods to the FabricantCollection to do what you want. Here is a FabricantCollection started for you:
Option Compare Database
Option Explicit
Private Const MODULE_NAME As String = "FabricantCollection"
Private m_oCollection As Collection
Private Sub Class_Initialize()
Set m_oCollection = New Collection
End Sub
Private Sub Class_Terminate()
If Not m_oCollection Is Nothing Then
Set m_oCollection = Nothing
End If
End Sub
Public Function Add(oFabricant As Fabricant) As Long
m_oCollection.Add oFabricant, oFabricant.Code
Add = m_oCollection.Count
End Function
Public Sub Clear()
Set m_oCollection = New BaseCollection
End Sub
Public Property Get Count() As Long
Count = m_oCollection.Count
End Property
Public Function Item(ByVal vKey As Variant) As Fabricant
Set Item = m_oCollection.Item(vKey)
End Function
Public Function Remove(ByVal vKey As Variant) As Fabricant
Set Remove = m_oCollection.Item(vKey)
m_oCollection.Remove vKey
End Function
Public Function AddNew( _
ByVal sCode As String, _
ByVal sArt As String, _
ByVal sDiv As String, _
ByVal sDev As String, _
ByVal sGrp As String) As Fabricant
Dim oFabricant As Fabricant
If Not oFabricant Is Nothing Then
Err.Raise vbObjectError + 3334, MODULE_NAME & ".AddNew", "Item already exists with that key: " & sCode
Exit Function
End If
Set oFabricant = New Fabricant
With oFabricant
.Code = sCode
.ArticleType = sArt
.Division = sDiv
.Devise = sDev
.GroupePrix = sGrp
End With
Add oFabricant
Set AddNew = oFabricant
End Function
Public Property Get Fabricant( _
ByVal sCode As String) As Variant
Set Fabricant = m_oCollection.Item(sCode)
End Property

VB Assignment of member field programmatically

This question is a follow-on to VB ReDim of member field programmatically. After the arrays are dimensioned appropriately, I try to set the values of the elements, but I get an exception at run time when I try to assign the first value (MySB.AssignValues(0, "B", 0, 7.6))
System.InvalidCastException was unhandled
HResult=-2147467262
Message=Object cannot be stored in an array of this type.
Source=mscorlib
Module TestSetArray
Public Class BS
Public A As String
Public B() As Double
Public C() As Double
End Class
Public Class SB
Public MyBS() As BS
'ReadFieldString is a function that returns a string of the field name of Class BS,
'i.e., A, B or C. For test purpose, retun a constant
Public Function ReadFieldString() As String
Return "B"
End Function
'GetArrayDim is a function that returns an integer, which is the size of the array
'of that field name. For test purpose, retun a constant
Public Function GetArrayDim() As Integer
Return 2
End Function
Public Sub DimArrays()
ReDim MyBS(3)
Dim i As Integer
For i = 0 To MyBS.Length - 1
MyBS(i) = New BS()
Dim f = GetType(BS).GetField(ReadFieldString())
f.SetValue(MyBS(i), Array.CreateInstance(f.FieldType.GetElementType(), GetArrayDim()))
Next
End Sub
Public Sub AssignValues(MainIndex As Integer, TheName As String, TheIndex As Integer, TheValue As Double)
Dim f = MyBS(MainIndex).GetType.GetMember(TheName)
f.SetValue(TheValue, TheIndex)
End Sub
End Class
Sub Main()
Dim MySB As SB = New SB
MySB.DimArrays()
MySB.AssignValues(0, "B", 0, 7.6)
MySB.AssignValues(0, "B", 1, 8.2)
End Sub
End Module
Thanks in advance.
The problem is that the GetMember method returns an array of type MemberInfo, not the double array of the class. You'd probably have an easier time if you used GetField instead. You have to call GetValue and cast its result to an Array in order to use SetValue to set the value.
Public Sub AssignValues(MainIndex As Integer, TheName As String, TheIndex As Integer, TheValue As Double)
Dim f = MyBS(MainIndex).GetType().GetField(TheName)
Dim doubleArray = DirectCast(f.GetValue(MyBS(MainIndex)), Array)
doubleArray.SetValue(TheValue, TheIndex)
End Sub
or if you know that the array will always be an array of Double, you can cast it directly to that:
Public Sub AssignValues(MainIndex As Integer, TheName As String, TheIndex As Integer, TheValue As Double)
Dim f = MyBS(MainIndex).GetType().GetField(TheName)
Dim doubleArray = DirectCast(f.GetValue(MyBS(MainIndex)), Double())
doubleArray(TheIndex) = TheValue
End Sub