Array as an argument in a procedure vb6 vs vb.net - arrays

Here it is a procedure in vb6 and it is working fine like the example included:
' Check_UnCheck
' check an array of some checkboxes, uncheck an array of another checkboxes
' Example of usage :
CheckBox.Check_UnCheck Array(chkCheck3, chkCheck5), Array(chkCheck1, chkCheck4)
Public Sub Check_UnCheck(ByRef CheckArray As Variant, ByRef UnCheckArray As Variant)
Dim i As Integer
Dim conControl As Control
For i = LBound(CheckArray) To UBound(CheckArray)
Set conControl = CheckArray(i)
conControl.Value = 1
Next
For i = LBound(UnCheckArray) To UBound(UnCheckArray)
Set conControl = UnCheckArray(i)
conControl.Value = 0
Next
End Sub
what is the equivalent in vb.net for the above procedure, the MSDN Documentation says :
We cannot use more than one parameter array in a procedure, and it must be the last argument in the procedure definition.

Try below code.
Look into comments for detailed description.
'DECLARE YOUR ARRAYS.
Dim array1 = New CheckBox() {CheckBox3, CheckBox5}
Dim array2 = New CheckBox() {CheckBox1, CheckBox4}
'CALL CHECK AND UNCHECK FUNCTION.
Check_UnCheck(array1, array2)
'YOUR FUNCTION DEFINITION.
Public Sub Check_UnCheck(ByRef CheckArray As CheckBox(), ByRef UnCheckArray As CheckBox())
'LOOP FIRST ARRAY AND CHECK THEM.
For index = 0 To CheckArray.GetUpperBound(0)
CheckArray(index).Checked = True
Next
'LOOP SECOND ARRAY AND UNCHECK THEM.
For index = 0 To UnCheckArray.GetUpperBound(0)
UnCheckArray(index).Checked = False
Next
End Sub

First you are confusing "parameter array" with an array of controls, they are not the same thing. A parameter array is when a method takes a variable number of parameters (all of the same type), the compiler then bundles those up into an array and passes that to the method. In order for that to happen the method has to use the keyword ParamArray.
VB.net does not have conrtol arrays in the sense that vb6 did, but that isn't what you example is using. You example is using a simple array of controls. As an aside, your example is using ByRef which was the default in VB6, but it is unnecessary in this case for both VB6 and VB.net. Given that it's usage in VB.net is no longer the default, using it unnecessarily is misleading.
You are passing in two arrays, the second could be a ParamArray but there is little point in doing so.
The basic and minimum change to get your code to work is simple, change the "Variant" to "CheckBox()". But that is not what I would recommend. There are a couple of other minor changes that make it more flexible and more readable.
Public Sub Check_UnCheck(CheckArray As IEnumerable(Of CheckBox),
UnCheckArray As IEnumerable(Of CheckBox))
' Here I have replaced the Variant, which is not supported in
' .net, with the generic IEnumerable of checkbox. I used an
' Ienumerable, instead of an array because it will allow making
' just a minor change to the call site.
' here I have eliminated the index variable, and moved the
' declaration of the conControl variable into the for each.
' Option Infer On statically types the variable as a checkbox
For Each conControl In CheckArray
' Checkbox controls do not have a value property.
' 1 is not true, true is true.
conControl.Checked = True
Next
For Each conControl in UnCheckArray
conControl.Checked = False
Next
End Sub
This would then be called like so:
Check_UnCheck({chkCheck3, chkCheck}, {chkCheck1, chkCheck4})

Related

VBA Array Public Method cant be modified by components

I'm working with a rather complex problem in VBA and I decided to structure it in different classes.
One of the classes containd an array and I need to modify the component individualy outside the class.
The problem can de reduced to the definition of the class
Public a As Variant
Private Sub Class_Initialize()
ReDim a(5) As Double
' Set some value
a(1) = 20
End Sub
And a sub to access the data:
Sub Test()
Dim b As New DummyTest
b.a(4) = 7
Debug.Print (b.a(1))
Debug.Print (b.a(4))
End Sub
The result on the debug window is 20 0. I've checked this result with an inspection and confirm that the property a cannot be modified from the exterior of the class.
Funny part is that the property can be modified if replaced by other array, for example:
Sub Test2()
Dim b As New DummyTest
b.a = Array(1, 2, 3, 4, 5)
Debug.Print (b.a(1))
Debug.Print (b.a(4))
End Sub
Behaves as expected. I'm confused as element access is allowed inside the class context and in any other situation, but in this specific case it doesn't wotk. There is no error msg, it simply refuses to change the content of the array.
I'll find work around solutions but missing this way to access data is being a really PITA.
Hope you can help me with this issue.
Thank you in advance!
Your "property" is breaking encapsulation, which pretty much defeats its entire purpose.
Public a As Variant
From a public interface standpoint, this is a read-write property (if you added a new class module with Implements DummyTest you'd have to add Property Get and Property Let members for it). From the class' standpoint, it's a public instance field.
When a class encapsulates an array, a collection, a dictionary, or any other data structure, the last thing you want is for that data structure to be publicly exposed, with anyone anywhere being able to just overwrite the class' entire internal state as it pleases - you're not getting any of the advantages of classes that way.
First step to proper encapsulation is to make the instance field Private. Next step is to expose actual Property accessors. You want the array data to be writable? Expose an indexed Property Let member for it.
Private a As Variant
Private Sub Class_Initialize()
ReDim a(0 To 10)
End Sub
Public Property Get Value(ByVal index As Long) As Variant
Value = a(index)
End Property
Public Property Let Value(ByVal index As Long, ByVal newvalue As Variant)
a(index) = newvalue
End Property
This code behaves exactly as advertised:
Public Sub Test()
Dim b As DummyTest
Set b = New DummyTest
b.Value(4) = 7
Debug.Print b.Value(1)
Debug.Print b.Value(4)
End Sub
Note that because the array is encapsulated, that client code is absolutely unable to re-assign the array itself: the array is abstracted away and only accessible through the means exposed by the class.
Reference variable in VBA must be declared, so that we know what type of object we are referring to
i.e. Dim b As DummyTest ' No new required in this step
Before using a declared reference to store information create an instance of the Class.
i.e. Set b = New DummyTest
This might seem confusing but is actually very logical. Because b is a Class the Dim statement sets up b to hold the address of a DummyTest object. At the time of the Dim statement no object has been created (no memory allocated) so the address of b is nothing (no value or null). The set statement tells VBA to create an instance of your DummyTest class (allocate the memory it needs) and puts the address of the instance (memory) into variable b. Then because VBA knows that b points to an instance of a class you never see the address of b when you use it, just the data to which b is pointing. You can get the the address b is holding using the function Objptr but this is only something you would do in very special/specific circumstances

public array loaded from useform vba

Iam creating a macro, and I need to access the array that is created and filled in useform (button_click action) in module.
Private Sub CommandButton1_Click()
Dim tmojo As Worksheet
Dim mojocell As Range
Set tmojo = Sheets("table mojo")
colls = tmojo.Range("N1").End(xlToLeft).Column
i = 1
For Each cCont In Me.Controls
If TypeName(cCont) = "ComboBox" Then
If cCont.Enabled = True Then
If cCont.Value = Empty Then
MsgBox "you havent specified all the columns"
Exit Sub
End If
ReDim Preserve collname(i)
collname(i) = cCont.Value
i = i + 1
End If
End If
Next cCont
Call createregion
End Sub
I fill the array collname with values from multiple comboboxes (column names). Then I want to call createregion sub which is located in module and I want to access the values in collname().
im getting error saying that:
Constants, fixed-length strings, arrays, user-defined types, and Declare statements not allowed as Public members of an object module
I need to access this array in multiple subs, is there any workaround?
Thank you in forehand.
The proper way is to transfer your UserForm code into a regular module, and declare your array as public, on the top of the module, before every subs :
Public MyArr()
When you transfer your code, you will need to call the subs into your UserForm's events, and so change all the Me and Me. to the full name of your UserForm.
And if you are lacking time, you can simply declare on the top of the UserForm module :
Dim MyArr()

VBA - Adding a listbox to an array

I've been searching around online, and just want to know how to add a bunch of ListBoxes from my Userform to my array. Here's what I have so far:
Dim LBArray() As Variant
Private Sub UserForm_Initialize()
ReDim LBArray(7) As Variant
FirstNameTextBox.Clear
LastNameTextBox.Clear
HotelCIDate.Clear
HotelCODate.Clear
HotelNameBox.Clear
HotelConfStatus.Clear
HotelConfNumber.Clear
LBArray(0) = FirstNameTextBox
LBArray(1) = LastNameTextBox
LBArray(2) = HotelNameBox
LBArray(3) = HotelCIDate
LBArray(4) = HotelCODate
LBArray(5) = HotelConfStatus
LBArray(6) = HotelConfNumber
End Sub
This should work, but it doesn't actually add the listboxes to the array. All the elements are either "" or Null, and I can't modify the ListBox objects from the array. Anyone know why this is happening?
A listbox is an Object, so you need to use the Set keyword when assigning an object, otherwise it will return the object's default property (which in this case I think is the .Value property)
Set LBArray(2) = HotelNameBox
Etc.
I don't think it's really necessary to do this, and actually it seems unnecessary and confusing! Later in your code, you're going to need to refer to ListBox(i) or to the objects by name.
If you need to iterate over the listboxes and perform some operations on them, you can always do:
Dim ctrl as MSForms.Control
For each ctrl in UserForm1.Controls
If TypeName(ctrl) = "ListBox" Then
** Code to do something with each listbox
End If
Next

Excel VBA Object Variable Not Set issue, possible improper Redim usage?

I am running into an error with the below code. When I debug, I find the line causing my problem:
Options(a) = New Element
The error displayed is Object Variable or With Block Variable Not Set. With msg boxes I have found the value of a to be 0 at the time of the crash and the TotalItems to be 7. The Element object initialization is empty. I call the PopulateChildren method from another method within the same class. Am I using ReDim improperly? It seems like maybe it isn't increasing the size of my array... I have noticed examples of using it like this...
ReDim Preserve Options(0 to TotalItems)
...but it doesn't seem to do any different when I try it. Anyone have any idea what is going on?
Dim Options() As Element
Dim TotalItems As Integer
Dim Children(100) As Integer
Private Sub PopulateChildren()
ReDim Preserve Options(TotalItems)
For a = 0 To TotalItems - 1
Options(a) = New Element
Options(a).Populate (Children(a))
Next a
End Sub
Thanks
Since Element is Object, you should use Set:
Set Options(a) = New Element
easy, put your dim options inside the SUB, or ...
public dim options()
sub ...
'your code
end sub
of course options doesn't exit for your sub like you wrote it ! so the error showing...

VBS - Create object and pass that through functions and subs?

I'm trying something out. I've got this system where a bunch of scripts are triggered at certain event handlers. This is used during the creation of Users in Active Directory.
So, what I think I need is a function that basically collects the properties of the user being processed into - hopefully, an object. And then returning that object and using it in other functions and subs. So that I can perhaps reach the properties I want using only "myObject.firstname" and that's it.
How can I create an object, create and set values in it and returning the entire object? Is this possible in VBS?
Edit: What would be best if I could create an Object that holds these properties and just being able to access them whenever I need to.
I'm basically after something like this:
Public Sub Main()
Set userStream = New objUser
msgbox objUser.firstname
msgbox objUser.lastname
msgbox objUser.username
End Sub
Edit: To explain furter, I have this library script which is called 'libGetUserProperties'
It's in this I want the function and Class. Then I just import this library script in the actual script that is run at runtime. Looks like this:
libGetUserProperties:
Public Function getobjUser(ByRef Request, ByVal From)
Dim thisUserObject
Set thisUserObject = New objUser
'If type = 0, data comes from AD
If From = 0 Then
thisUserObject.firstname = "some string"
thisUserObject.lastname = "some string"
End If
'If type = 1, data comes from Request stream
If From = 1 Then
thisUserObject.firstname = "some string"
thisUserObject.lastname = "some string"
End If
Set getobjUser = thisUserObject
End Function
Class objUser
Public firstname
Public lastname
Public username
End Class'
This is what the actual runtime Script looks like:
Dim libGetUserProperties
Set libGetUserProperties = ScriptLib.Load("Script Modules/Library Scripts/libGetUserProperties")
Sub onPreCreate(Request)
Dim userStream
Set userStream = New objUser
'Do whatever with objUser which contains all of the properties I'm after
End Sub
That’s not possible using VBScript, unless you want to dynamically generate the code for a class, including the properties, save that to file, and then execute that file as a script.
This is so much easier to do using JScript. Since Windows Scripting can handle JScript as well as VBScript, I’d recommend going that way.
If you definitely need VBScript, the best you can go for is a Scripting.Dictionary that would contain the properties as keys:
Dim dicUser
dicUser = CreateObject("Scripting.Dictionary")
dicUser("FirstName") = objUser.FirstName
which would allow you to access the first name as follows:
dicUser("FirstName")
Update
If you want to write a function which creates a dictionary and returns that, this is how to do it in VBScript:
Function RememberTheUser(Request)
Dim dicResult
Set dicResult = CreateObject("Scripting.Dictionary")
dicResult("FirstName") = Request.GetTheFirstName
Set RememberTheUser = dicResult
End Function
Which can then be assigned to some other variable:
Dim dicUser ' in the main section of the script, this means it's a global variable
Sub onPreCreate(Request)
' ...
Set dicUser = RememberTheUser(Request)
' ...
End Sub
Sub onSomethingLater()
WScript.Echo dicUser("FirstName") ' It's still accessible here
End Sub
In JScript, it’s a lot easier: just create a new object, and tack on new properties.
function readObject(dataSource) {
var result = {};
result.source = dataSource;
result.firstName = dataSource.firstName;
result['lastName'] = dataSource.FunctionThatGetsLastName();
}
Note that in JScript, object properties can be accessed using the object.property notation, but also through the object['property'] notation, which is very useful when you have dynamically-named properties. ('property' in the second notation is a string, and can be replaced by any expression that returns a string).
Keeping with VBScript, a disconnected recordset would work as well and is possibly faster than a dictionary; see here for an example.

Resources