Concatenate Variable Names in VB - vb.net

I have a large window with a little under 200 controls/variables to worry about. Many of them are similar, so I am wondering if instead of repeatedly calling each one individually I can concatenate their names.
I'll make an example:
I have 5 pieces of data that I'm worried about: Red, Orange, Yellow, Green, Blue.
Each one of these will have a label that needs to be made visible, a textbox that needs to be made visible, and a string that contains the text in the textbox:
lblRed.Visible = True
txtRed.Visible = True
strRed = txtRed.Text
Instead of listing this for every one of those 5 pieces of data, is there a way that I could make some sort of array to loop through that could concatenate these variable names?
Dim list As New List(Of String)(New String() {"Red", "Orange", "Yellow", "Green", "Blue"})
Dim i As Integer = 0
Do while i < list.count
lbl + list(i) + .Visible = True
txt + list(i) + .Visible = True
str + list(i) = txt + list(i) + .Text
i = i+1
Loop
I know that the above code doesn't work, but I wanted to give you the basic idea of what I wanted to do. Does this look feasible?

http://msdn.microsoft.com/en-us/library/7e4daa9c(v=vs.71).aspx
Using the controls collection:
Dim i As Integer
i = 1
Me.Controls("Textbox" & i).Text = "TEST"
so
Me.controls("lbl" & list(i)).Visible = true
Keep in mind that when concatenating items, '+' will work on strings and not integers. You might want to always use '&' when concatenating

Another way is to use a select-case block for each type of control. Something like this:
Private Sub EnableControls()
For Each c As Control In Me.Controls
Select Case c.GetType
Case GetType(TextBox)
c.Visible = True
Select Case c.Name.Substring(3)
Case "Red"
strRed = c.Text
Case "Orange"
strOrange = c.Text
Case "Yellow"
'and so on
End Select
Case GetType(Label)
c.Visible = True
End Select
Next
End Sub

Check out the information here:
http://msdn.microsoft.com/en-us/library/axt1ctd9.aspx
I'm sure someone may find something slightly more eloquent than this, but this should achieve your desired result. You'll need the below import:
Imports System.Reflection
If you use properties to access your variables, you can use reflection to grab them by name:
'Define these with getters/setters/private vars
Private Property strRed as String
Private Property strOrange as String
Private Property strYellow as String
Private Property strGreen as String
Private Property strBlue as String
For each color as String in list
If Me.Controls.Count > 1 Then
'Should really check for existence here, but it's just an example.
Me.Controls("lbl" & color).Visible = True
Dim tbx as TextBox = Me.Controls("txt" & color)
tbx.Visible = True
Dim propInfo as PropertyInfo = Me.GetType.GetProperty("str" & color)
propInfo.SetValue(Me, tbx.Text, Nothing)
End If
Next

Related

Dynamically manage arrays to combobox'es

I am trying to make my code compact and easily expendable and now I have bumped into a problem.
I have 18 comboxes which might be more in the future, these should be initialized with an array:
Array_MyFirstDataSet ...
So if I name all my comboboxes with this naming pattern
ComboBox_MyFirstDataSet ...
but I can not figure out how to dynamically assign
ComboBox_MyFirstDataSet.List=Array_MyFirstDataSet
So should I add all the arrays to a dictonary instead of keeping them as independent arrays?
My non working code is below,
Create arrays:
Public Sub initPartslist()
If CPearson.IsArrayEmpty(Settings.Array_Type) Then
Settings.Array_Type = Split("NA,PRT,ASM,SHEET,WELD PRT,WELD ASS,WELD MRG", ",")
End If
If CPearson.IsArrayEmpty(Settings.Array_SpeedSelect) Then
Settings.Array_SpeedSelect = Split("NA,Fresing,Dreiing,Vannskjæring,Dreiing og Fresing,Fresing og Dreiing,Sveising,Montering", ",")
End If
And here I try to add the arrays to the comboboxes
Public Sub initComboBox() 'rowNumber As Integer
Dim cCont As control
Dim cName As String
'All members in Frame_MOM
For Each cCont In MOM.Frame_MOM.Controls
If TypeName(cCont) = "ComboBox" Then
cName = replace(cCont.name, "ComboBox", "Array")
cCont.List = Settings.cName
End If
Next cCont
You are nearly there. It will work if Settings.cName is an array. Like this:
Dim cName(2, 0 to 2)
cName(0, 0) = "John"
cName(0, 1) = 1982
...
cCont.List = Settings.cName

For each loop, compare to next item on the list

I am writing an application where I bring change history of items from the database and place them on the table using For Each loop. I would, however, like to show in table what information has changed in each edit. Is it possible to compare variables of each item to the variables of next loop in For Each loop?
Something like:
For Each k As Examplemodel In Model
'Find next item on the loop after current one somehow
Dim nextItem = Model.Item(k+1) 'something like this
If k.ItemsName <> nextItem.Itemsname 'if the name has changed in edit
'show result in bold
Else
'show result in normal font weight
End If
Next
Is this possible and if not, what's the best way to achieve this?
You can't do it in a foreach loop directly.
If your Model class have indexers you can easily convert it into a for loop:
If Model.Count > 1 Then
For i as Integer = 0 to Model.Count - 2 ' Note the -2 here !!!
Dim Item As Examplemodel = Model(i)
Dim NextItem As Examplemodel = Model(i + 1)
if Item.ItemsName <> NextItem.ItemsName then
'show result in bold
else
'show result in normal font weight
end if
Next
'show result of NextItem here, since the last item doesn't get shown in the loop
Else
'show result of only item here
End If
If not, you can use a workaround like this:
Dim PrevItem as Examplemodel = Nothing ' Assuming a reference type
For Each k As Examplemodel In Model
If Not IsNothing(PrevItem) AndAlso k.ItemsName <> Prev.Itemsname 'if the name has changed in edit
'show result (of PrevItem!!!) in bold
Else
'show result (of PrevItem!!!) in normal font weight
End If
PrevItem = k
Next
'show result (of PrevItem (currently the last item in Model) in normal font weight
You should use a normal for loop:
Dim numbers() As Integer = New Integer() {1, 2, 4, 8}
Sub Main()
For index As Integer = 0 To numbers.Length - 2
Dim currentInt As Integer = numbers(index)
Dim nextInt As Integer = numbers(index + 1)
Console.WriteLine(currentInt)
Console.WriteLine(nextInt)
Next
End Sub
Another approach to use LINQ Aggregate extension method, which use first item of collection as initial value. So every item will have access to previous one.
Public Class ItemChanges
Public Property Item As ExampleModel
Public Property Changes As New Hash(Of String)
End Class
Public Function Check(previous As ItemChanges, current As ItemChanges) As ItemChanges
If current.Item.Name <> previous.Item.Name Then
current.Changes.Add(Nameof(current.Name))
End
Return current
End Function
' assume model is collection of items
Dim itemWithChanges =
model.Select(Function(m) New ItemChanges With { .Item = m })
.Aggregate(AddressOf(Check))
.ToList()
Then you can use calculated result as you want - every item will have a hash of property names which had changed
If checkedItem.Changes.Contains(Nameof(checkedItem.Item.Name)) Then
' use bold font or different color
End

Visual Basic excel, How to ask for letter colors

I want to ask for a letter color in an If conditional:
string="asdfghjkl"
for i=1 to len(string)
letter = mid(string, i, 1)
input_letter = inputbox("Write a letter")
if letter = input_letter 'and letter.Font.Color = RGB(31,78,120)
'my code here
endif
next
The and letter.Font.Color = RGB(31,78,120) is not working. It says i need an object.
Is there any similar way to ask this? This RGB color is blue, and I am using this code to transform the entire sentence to blue (with the record macro excel setting)
With Selection.Font
.ThemeColor = xlThemeColorAccent1
.TintAndShade = -0.499984740745262
End With
Thanks
Regarding your question's problem:
The .Font.Color is a property of the class Range, but in your line of code:
if letter = input_letter 'and letter.Font.Color = RGB(31,78,120)
... you're trying to access this property in the variable letter, which is a String (you don't explicitly declare it as such, but it gets automatically declared when you execute letter = mid(string, i, 1) just above).
That is why you get an Object required exception: you're trying to access the property .Font.Color on something that is not a Range object (actually, not an Object at all).
Regarding your real need:
I'm not sure to understand what you're trying to do. Are you trying to reach a multi-colored text into a single cell in Excel? If I've got it right, you'll have a string:
string="asdfghjkl"
(please note: you can't call your variable String, that's a reserved keyword for the code. Think of calling it something else, though I guess you already do that in your real code or you wouldn't be able to execute it at all).
... and, for each letter of that string,
for i=1 to len(string)
... you want the user to give you a color. In that case, you can't do it in Excel. If not that, could you please express better your real need?
The code below comes closest to your OP logic and comment using the .Characters property of a cell Range (B11) containing your string value:
Code
Option Explicit
Sub test()
Dim blue As Long: blue = RGB(31, 78, 120)
Dim s As String: s = "asdfgh"
Dim letter As String
Dim input_letter As String
Dim i As Integer
Dim rng As Range
Set rng = ThisWorkbook.Worksheets("MySheet").Range("B11")
With rng
.Value = s
' color whole string
.Characters(1, Len(s)).Font.Color = blue
For i = 1 To Len(s)
letter = Mid(s, i, 1)
input_letter = InputBox("Write a letter")
If letter = input_letter And .Characters(i, 1).Font.Color = blue Then
'color found character
.Characters(i, 1).Font.Color = vbWhite
ElseIf input_letter = "" Then Exit For
End If
Next
End With
End Sub
Notes
Always use Option Explicitin your modules declaration head. So you would see that String isn't allowed as variable name as it's a function.
The extra color check in the If condition seems redundant, as characters so long a r e blue.
You seem to prefer repeated InputBoxes within the For - Next loop, could be reduced to a single call.

How can i check for a character after certain text within a listbox?

How can i check for a character after other text within a listbox?
e.g
Listbox contents:
Key1: V
Key2: F
Key3: S
Key4: H
How do I find what comes after Key1-4:?
Key1-4 will always be the same however what comes after that will be user defined.
I figured out how to save checkboxes as theres only 2 values to choose from, although user defined textboxes is what im struggling with. (I have searched for solutions but none seemed to work for me)
Usage:
Form1_Load
If ListBox1.Items.Contains("Key1: " & UsersKey) Then
TextBox1.Text = UsersKey
End If
Which textbox1.text would then contain V / whatever the user defined.
I did try something that kind of worked:
Form1_Load
Dim UsersKey as string = "V"
If ListBox1.Items.Contains("Key1: " & UsersKey) Then
TextBox1.Text = UsersKey
End If
but i'm not sure how to add additional letters / numbers to "V", then output that specific number/letter to the textbox. (I have special characters blocked)
Reasoning I need this is because I have created a custom save settings which saves on exit and loads with form1 as the built in save settings doesn't have much customization.
e.g Can't choose save path, when filename is changed a new user.config is generated along with old settings lost.
Look at regular expressions for this.
Using the keys from your sample:
Dim keys As String = "VFSH"
Dim exp As New RegEx("Key[1-4]: ([" & keys& "])")
For Each item As String in ListBox1.Items
Dim result = exp.Match(item)
If result.Success Then
TextBox1.Text = result.Groups(1).Value
End If
Next
It's not clear to me how your ListBoxes work. If you might find, for example, "Key 2:" inside ListBox1 that you need to ignore, you will want to change the [1-4] part of the expression to be more specific.
Additionally, if you're just trying to exclude unicode or punctuation, you could also go with ranges:
Dim keys As String = "A-Za-z0-9"
If you are supporting a broader set of characters, there are some you must be careful with: ], \, ^, and - can all have special meanings inside of a regular expression character class.
You have multiple keys, I assume you have multiple textboxes to display the results?
Then something like this would work. Loop thru the total number of keys, inside that you loop thru the alphabet. When you find a match, output to the correct textbox:
Dim UsersKey As String
For i As Integer = 1 To 4
For Each c In "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray()
UsersKey = c
If ListBox1.Items.Contains("Key" & i & ": " & UsersKey) Then
Select Case i
Case 1
TextBox1.Text = UsersKey
Case 2
TextBox2.Text = UsersKey
Case 3
TextBox3.Text = UsersKey
Case 4
TextBox4.Text = UsersKey
End Select
Exit For 'match found so exit inner loop
End If
Next
Next
Also, you say your settings are lost when the filename is changed. I assume when the version changes? The Settings has an upgrade method to read from a previous version. If you add an UpgradeSettings boolean option and set it to True and then do this at the start of your app, it will load the settings from a previous version:
If My.Settings.UpgradeSettings = True Then
My.Settings.Upgrade()
My.Settings.Reload()
My.Settings.UpgradeSettings = False
My.Settings.Save()
End If
Updated Answer:
Instead of using a listtbox, read the settings file line by line and output the results to the correct textbox based on the key...something like this:
Dim settingsFile As String = "C:\settings.txt"
If IO.File.Exists(settingsFile) Then
For Each line As String In IO.File.ReadLines(settingsFile)
Dim params() As String = Split(line, ":")
If params.Length = 2 Then
params(0) = params(0).Trim
params(1) = params(1).Trim
Select Case params(0)
Case "Key1"
Textbox1.Text = params(1)
Case "Key2"
Textbox2.Text = params(1)
End Select
End If
Next line
End If
You can associate text box with a key via its Name or Tag property. Lets say you use Name. In this case TextBox2 is associated with key2. TextBox[N] <-> Key[N]
Using this principle the code will look like this [considering that your list item is string]
Sub Test()
If ListBox1.SelectedIndex = -1 Then Return
Dim data[] As String = DirectCast(ListBox1.SelectedItem, string).Split(new char(){":"})
Dim key As String = data(0).Substring(3)
Dim val As String = data(1).Trim()
' you can use one of the known techniques to get control on which your texbox sits.
' I omit this step and assume "Surface1" being a control on which your text boxes sit
DirectCast(
(From ctrl In Surface1.Controls
Where ctrl.Name = "TextBox" & key
Select ctrl).First()), TextBox).Text = val
End Sub
As you can see, using principle I just explained, you have little parsing and what is important, there is no growing Select case if, lets say, you get 20 text boxes. You can add as many text boxes and as many corresponding list items as you wish, the code need not change.

MS Access 2007 & SQL: SELECT ... IN ('get', 'list', 'of', 'variables', 'from', 'form'")

In a form a user makes choices using checkboxes.
Then VBA commands collect these choices into a string with comma separated numbers:
Dim choices As String
If (Me!choice1) Then
choices = "1,"
End If
If (Me!choice2) Then
choices = choices & "2,"
End If
Then these choices are saved in an invisible text field:
Me!choices.Value = choices (e.g. "1, 2, 5")
Then a report is open and it's source is a query:
SELECT ... FROM ... WHERE MyTable.MyVariable In (Forms!MyForm!choices) doesn't work.
If I manually write "1, 2, 5" in the query instead of Forms!MyPage!choices then it works.
Have also tried changing the string to other formats '1','2','5' and '1,2,5'
The reference to the variable is correctly spelled. So, how do I send this string to such a query?
Are you sure you removed the last comma in choices variable? It seems, based on the code you provided, that you have 1,2,5, instead of 1,2,5
Try to change this line:
Me!choices.Value = choices
into this solution:
Me!choices.Value = left(choices, len(choices)-1)
At the module level, Dim is equivalent to Private. As such, the expression Forms!MyForm!choices cannot see it. The 'quick fix' would be to change Dim to Public. However, a better solution would be to add a public accessor method. Further, there is no need to use an invisible text box - either just use a private string variable as the backing store, or don't cache the value at all. E.g.,
Option Explicit
Const MaxChoiceCount = 4 ' or whatever it actually is
Const ChoiceControlPrefix = "chkChoice" ' ditto
Private mChoices As String
Property Get Choices() As String
Choices = mChoices
End Property
Private Sub UpdateChoices()
Dim S As String, I As Integer
For I = 1 To MaxChoiceCount
If Me.Controls(ChoiceControlPrefix & I).Value Then S = S & I & ","
Next I
mChoices = Left$(S, Len(S) - 1)
End Sub
or
Option Explicit
Const MaxChoiceCount = 4 ' or whatever it actually is
Const ChoiceControlPrefix = "chkChoice" ' ditto
Function Choices() As String
Dim S As String, I As Integer
For I = 1 To MaxChoiceCount
If Me.Controls(ChoiceControlPrefix & I).Value Then S = S & I & ","
Next I
Choices = Left$(S, Len(S) - 1)
End Property