Is it possible to dimension a new variable based on array value? - arrays

I have a lot of fields for which I would need to define a new variable. The list of fields is likely to change, so I'd like to put the listing of fields in a string to populate an array. From there, I want to loop through the array and dimension the variable. When I run code similar to below, I get the compile error "Constant expression required." If something like this isn't possible, is there a better way to achieve a similar result?
Public Sub Test()
Dim strFields as String
Dim arrFields() as String
strFields = "Field1|Field2|Field3"
arrFields = split(strFields,"|")
For Each x In arrFields()
Dim arrFields(x) As String
Next x
End Sub

Related

How can I store an array in my.settings in vb.net

I am trying to store a user input array in a my.settings variable in vb.net. I would like for the user to enter an array in the form {1,2,3}, store that as a string in the setting and then be able to use the setting value later to create a new array. The code will be something like:
Dim inputarray()
Dim outputarray()
inputarray=textbox1.text
my.settings.inputstoredarray.add(inputarray)
outputarray=my.settings.inputstoredarray
textbox2.text=outputarray(0)
'If the user types "{1,2,3}' in textbox1, textbox2 should show "1"
I have tried multiple versions of this but there seem to always be type conversion errors. I don't understand why it works if I hardcode:
inputarray={1,2,3}
and yet the below code does not work:
inputarray=my.settings.inputstoredarray
How can I store a user provided array in my.settings and retrieve it for use later?
does not work even if I go into settings and set the string value for the setting to {1,2,3}
Set up your setting in Project Properties, Settings tab as follows.
Then save the setting as follows.
Private Sub SaveStringToSettings()
My.Settings.StringOfInts = TextBox1.Text 'User types in {1, 2, 3}
End Sub
To retrieve the setting and turn it into an array
Private Sub CreateArrayFromSettings()
Dim SettingValue = My.Settings.StringOfInts
Debug.Print(SettingValue) 'View this in the Immediate window
'Get rid of the braces
Dim TrimmedString = SettingValue.Trim({"{"c, "}"c})
'Split the string by the commas into an array
Dim Splits = TrimmedString.Split(","c)
'Get rid of the spaces
For i = 0 To Splits.Length - 1
Splits(i) = Splits(i).Trim
Next
TextBox1.Text = Splits(0) 'Displays 1
End Sub
You can browse for additional setting types in your Project properties/Settings tab but you'll find it fairly limited with the most common message being that you can't use that type. Array is one you can't use, I'm afraid.
But you can use the fully-supported StringCollection:
Dim saveTest As New StringCollection() From {"1", "2", "3", "4"}
My.Settings.MySetting = saveTest
My.Settings.Save()
Dim loadTest As StringCollection = My.Settings.MySetting
Another option would be to use, say, and XML or JSON string that you de-serialize but that's getting a bit involved.
You can't store an array in application settings. What you can do is create a setting of type StringCollection. You can then use it as a StringCollection in code or you can transfer the data back and forth between the StringCollection and an array if you really need an array.
Start by opening the Settings page of the project properties and creating a new setting of type System.Collections.Specialized.StringCollection. For this example, I'll name it MyStringCollection but you should name it appropriately for your app. When you do this, notice that the Value field is empty by default. This means that the setting is Nothing by default. That's OK but it means that you need to actually create the collection object in code before using it for the first time, e.g.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If My.Settings.MyStringCollection Is Nothing Then
My.Settings.MyStringCollection = New StringCollection
End If
'Use My.Settings.MyStringCollection here.
End Sub
Alternatively, you can force the settings UI to create the object for you. To do that, select the Value field for that setting and click the browse (...) button. Add any arbitrary character and click the OK button. Notice that the Value field is populated with an XML snippet that contains the text you entered. Click the browse (...) button again, delete the text and click OK again. Notice that the XML remains, even though the text you had entered is removed. That XML will now create the StringCollection object automatically, so you don't need code to do so.
If you're happy wiith a collection of String values then you can use the setting directly in code wherever you like. It works, for the most part, like a List(Of String), allowing you to add and remove items at will. If you specifically need an array or you need a type other than String then you will have to do some translation, e.g.
'Load the collection items into a String array.
Dim myStringArray = My.Settings.MyStringCollection.Cast(Of String)().ToArray()
'Load the collection items into an Integer array.
Dim myIntegerArray = My.Settings.MyStringCollection.Cast(Of String)().Select(Function(s) CInt(s)).ToArray()
'Repopulate the collection from a String array.
My.Settings.MyStringCollection.Clear()
My.Settings.MyStringCollection.AddRange(myStringArray)
'Repopulate the collection from an Integer array.
My.Settings.MyStringCollection.Clear()
My.Settings.MyStringCollection.AddRange(myIntegerArray.Select(Function(n) n.ToString()).ToArray())
If you want to display the contents of your collection in a TextBox than you might do something like this:
TextBox1.Text = String.Join(",", My.Settings.MyStringCollection.Cast(Of String)())
That will create a single, comma-delimited String containing all the items. To repopulate the collection from a TextBox containing such text, do this:
My.Settings.MyStringCollection.Clear()
My.Settings.MyStringCollection.AddRange(TextBox1.Text.Split(","c))

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

Excel vba : assign all properties to a cell from an array of range

My goal is to save a Cell and all its properties and value in an array of range, and then write back all properties and values of this cell to another cell from the array (like a copy paste function but pasting from the array).
Here is a simple test procedure :
Sub Test()
Dim Range_Grid(1) As Range
Dim CellAdress As String
Dim i As Long
Set Range_Grid(1) = ActiveSheet.Cells(2, 3)
ActiveSheet.Cells(4, 1) = Range_Grid(1)
End Sub
So here in the first element of the array Range_Grid(1), I really get the full range saved and I can access every property the original range ActiveSheet.Cells(2, 3) had, like font style, format, color, comment, etc..
But when I try to write this range from array to another empty cell, it only write the value...
Any idea how to write all the properties from the array like if it was a copy/paste from sheet to sheet?
There is no way of moving all of the formatting from a range in an array back into excel at once. This leaves you with two options: either you copy within excel as Scott Craner suggested, or you copy each type of formatting individually. For example, if you wanted to copy the value (which is the default and hence is what is copied in your original code) and the cell background colour, then you could use the following code:
Sub Test()
Dim Range_Grid(1) As Range
Set Range_Grid(1) = ActiveSheet.Cells(2, 3)
ActiveSheet.Cells(4, 1) = Range_Grid(1)
ActiveSheet.Cells(4, 1).Interior.ColorIndex = Range_Grid(1).Interior.ColorIndex
End Sub
Unfortunately there are probably over a dozen types of formatting that you may want to deal with at any given time...
There's an alternative: use Copy and PasteSpecial functions.
Sub Test()
ActiveSheet.Cells(2, 3).Copy
ActiveSheet.Cells(4, 1).PasteSpecial(xlPasteAll)
End Sub

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...

Reading comma-separated lines from textbox

I've been reading and reading tons of answers, modifying the code, and still i cant figure out how to solve this problem.
I have a textbox that receives multiline comma-separated information from a .txt or .csv file. Example:
Pearl Harbour;Ticonderoga CG-45;300;1000 Everett;Ticonderoga
CG-46;310;1200 Pearl Harbour;Burke DDG-110;215;800
Now there will be a combobox to chose a port (in this example the options will be Pearl Harbour and Everett). After choosing "Pearl Harbour", another multiline textbox will show only the lines which have "Pearl Harbour" as the first element.
Now goes what I was able to write:
Public Sub Readfile()
TextBox1.Text = System.IO.File.ReadAllText("libro1.csv")<br>
Dim lines() As String<br>
lines = Split(TextBox1.Text, vbCrLf)<br>
Dim strline0 As String = lines(0)<br>
Dim strArray0() As String = strline0.Split(";")<br>
Dim strline1 As String = lines(1)<br>
Dim strArray1() As String = strline1.Split(";")<br>
...
End Sub
The first problem I find is that for every line the .csv has, I must write that two lines of code to have an array with all the information. But I cant do that because I cant know how many lines the .csv is going to have.
Im kind of lost here. Im not asking anyone to do magic and give me a code I can copy and paste, but I would be grateful if someone can guide me through this.
First off, you'd do better to use a List than an array. Particularly for a collection of strings, they're much easier to work with. With that, you're correct that you can't go individually naming your lines because you don't know how many there will be. That's why you need to create a list of lines and loop through them, like ...
Public Sub Readfile()
TextBox1.Text = System.IO.File.ReadAllText("libro1.csv")
Dim lines As List(of String)
Dim allResults As New List(of List(of String))
lines = Split(TextBox1.Text, vbCrLf)
For Each line In lines
Dim result As List(Of String) = line.Split(CChar(";"))
allResults.Add(result)
Next
End Sub
This will allow you to essentially say, "For each line in the file, take each semi-colon-separated part and put it into a list called 'result'. Then put 'result' into another list of results called 'allResults'."
Behold! The power of loops!

Resources