How can I change a toolbox item property by referring to the item through a variable? - vb.net

Sorry for the confusing title - here is the code I'm using.
Example code -
If bolCorrect = False Then
intIncorrect += 1
temp3 = "picture" + CStr(intIncorrect)
temp3.Visible = True
I've got several images all, with names of picture[number-from-0-to-10], and I want them to show depending on the count of a variable.
The error it throws up is that 'Visible' is not part of 'String'. How can I get the interpreter to look at 'temp3' in this instance, and refer to the toolbox item rather than the type of the variable (e.g. string)?

You need to refer to the actual name property you have set for the picturebox control (if you are using the picturebox control)
So if your picture box control is named pb1
pb1.Image = System.Drawing.Image.FromFile("picture" + counter + ".jpg")
pb1.Visible = True

You should generally try to avoid addressing controls via strings, that’s usually just a hack around a proper solution. Instead, maintain a variable to that control, or, in your case, maintain an array of the relevant controls and access them via an index.
That said, it is possible to get a control given its name via the Form.Controls collection:
Dim ctl = Me.Controls("picture" + CStr(intIncorrect))

Related

Saving Custom Document Properties in a Loop

I'm trying to save the values of data that have been input into my form. There are a total of about 50 different fields to save across 5 different agents, so I loaded the data into arrays.
I've tried saving the fields in a loop, but it doesn't seem to work in a loop, only if each field has a separate line, which is a lot of code and messy. The Ag1Name, Ag2Name and Ag3Name are the names of my textboxes that the user enters to populate the form.
Sub LoadAndSaveData()
NumberofAgents = 3
Dim AgentName(3) as String
AgentName(1) = Ag1Name.Value
AgentName(2) = Ag2Name.Value
AgentName(3) = Ag3Name.Value
For Count = 1 To NumberOfAgents
With ActiveDocument.CustomDocumentProperties
.Add Name:="AgentName" & Count, LinkToContent:=False, Value:=AgentName(Count), Type:=msoPropertyTypeString
End With
Next Count
End Sub
The data doesn't get saved to the Custom Document Properties when the code is set up in a loop like the above. Since there are so many values to save and all the data is already in arrays, I would much prefer to use a loop rather than write out a separate line of code for all ~50 of the values. It does seem to work when each field is saved in a separate line of code.
I think this would probably get what you want. You don't really need to count the document properties first, only increment with the ones you want to update. Hopefully the only document properties you want contain the name AgentName in it.
ReDim AgentName(0) As String
Dim P As Long
For Each c In ThisDocument.CustomDocumentProperties
If InStr(1, c.Name, "AgentName", vbTextCompare) > 0 Then
ReDim Preserve AgentName(P)
AgentName(P) = c.Value
P = P + 1
End If
Next c
As a guest I cannot post a comment here, but the code you gave works OK here.
However, there is a problem with creating legacy custom document properties programmatically, because doing that does not mark the document as "changed". When you close the document, Word does not necessarily save it and you lose the Properties and their values.
However, if you actually open up the Custom Document Property dialog, Word does then mark the document as "changed" and the Properties are saved.
So it is possible that the difference between your two scenarios is not the code, but that in one scenario you have actually opened the dialog box to check the values before closing the document and in the other you have not.
If that is the case, here, I was able to change this behaviour by adding the line
ActiveDocument.Saved = False
after setting the property values.
If you do not actually need the values to be Document Properties, it might be better either to use Document Variables, which are slightly easier to use since you can add them and modify them with exactly the same code, or perhaps by storing them in A Custom XML Part, which is harder work but can be useful if you need to extract the values somewhere where Word is not available.
You can make this even easier by looping the controls on the UserForm, testing whether the control name contains "Ag" and, if it does, create the Custom Document Property with the control's value - all in one step.
For example, the following code sample loops the controls in the UserForm. It tests whether the controls Name starts with "Ag". If it does, the CustomDocumentProperty is added with that control's value.
Sub LoadAndSaveData()
Dim ctl As MSForms.control
Dim controlName As String
For Each ctl In Me.Controls
controlName = ctl.Name
If Left(controlName, 2) = "Ag" Then
With ActiveDocument.CustomDocumentProperties
.Add Name:=controlName, LinkToContent:=False, value:=ctl.value, Type:=msoPropertyTypeString
End With
End If
Next
End Sub
I feel a little stupid... I just realized that the reason that the code wasn't working was that the variable NumberofAgents was not being calculated correctly elsewhere in my code. I've got it working now. Thanks for your thoughts!

VB.NET - Dynamically created controls and how to manipulate them

If I create a control like so:
Dim fb As New Label
With fb
.Name = "newLabel"
.text = "some text"
<etc.>
Me.Controls.Add(fb)
End With
Now, if I wanted to change the text on that label during run time, I would normally do:
newLabel.text = "some other text"
Unfortunately, Visual Studio won't let me do that as 'newLabel' isn't defined until run time.
So, my question is: How do I reference a control created in such a way? (The only way I can think of is to loop through all controls until I find the one I'm looking for, but that seems a tad inefficient to me.)
'newLabel' isn't defined until run time"
That isn't really accurate. You are confusing the object with a variable used to reference the object. When you add a control to a form in the designer, VS generates code to create and configure that control. It's much the same as the code you wrote and posted. You can see it in the designer code file, which you can access if you click the 'Shoe All Files' button in the Solution Explorer. That code includes a member variable to which the created object is assigned. You then use that member variable to refer to that object in code.
If you're creating controls at run time then you generally can't declare a member variable for each one to be assigned to because you don't know how many there will be. If you do know how many there will be then you probably ought to be adding them at design time. That means that you have two options:
Declare a single member variable that will refer to a collection of controls created at run time and then access then via name or index from that.
Access them by name from the Controls collection of the form or other container control that you must add them to in order for them to be displayed.
Option 2 requires that you provide a unique name for each control when you create it. Option 1 doesn't require a name at all, although it doesn't preclude one.
Option 1 might look like this:
At the class level:
Private labels As New List(Of Label)
In a method somewhere:
For i = 0 To 9
Dim lbl As New Label
labels.Add(lbl)
Controls.Add(lbl)
Next
Later:
Dim lbl = labels(recordIndex)
Option 2 might look like this:
In a method somewhere:
For i = 0 To 9
Dim lbl As New Label With {.Name = "titleLabel" & i}
Controls.Add(lbl)
Next
Later:
Dim lbl = DirectCast(Controls("titleLabel" & recordIndex), Label)

Reading checkbox name from text file

I'm trying to read a checkbox name from a txt file. I'm not sure if this is posible but i tried this:
Me.(My.Computer.FileSystem.ReadAllText(Application.StartupPath & "\Settings\language.txt")).Checked = 1
Sadly it didnt work, i get the error "identifier expected" and it underlines the dot after the Me.
The basic problem here is understanding what things are resolved at compile time vs what things are resolved at run time.
A checkbox name is an identifier that must resolve at compile time. You see a nice name like CheckBox1 in your source code, but when the program actually runs all you really have is a reference consisting mainly of a number representing an offset into your program's memory address space. The CheckBox1 name as a variable no longer exists; only the object reference remains.
On the other hand, StartupPath and the contents of the text file are only strings. They are not identifiers, and their values are not known until much later, when the program is already running.
The good news is, in the case of WinForms controls the variable name is preserved as data in the Control object, and you can search for it. You just need to use a method that will look at your Control objects, like this:
Dim language As String = My.Computer.FileSystem.ReadAllText(Path.Combine(Application.StartupPath, "\Settings\language.txt"))
Dim languageBox as CheckBox = DirectCast(Me.Controls(language), CheckBox)
Or maybe this:
Dim languageBox as CheckBox = Me.Controls.OfType(Of CheckBox)().FirstOrDefault(Function(box) box.Name = language)
Or maybe your checkbox is nested within a GroupBox or Panel. In that case, you need to change Me for the name of the GroupBox, Panel, or other container control that directly holds the checkbox.
what is the name of checkbox in form ?...
Dim _chkbxName$ = My.Computer.FileSystem.ReadAllText(Application.StartupPath & "\Settings\language.txt")
For Each cnt As Control In Me.Controls
If TypeOf cnt Is CheckBox Then
If cnt.Name = _chkbxName Then
CType(cnt, CheckBox).Checked = True
Exit For
End If
End If
Next

Is there a simpler way to process check boxes?

In vb.net, I have a form that has a set of four Check Boxes. Each Check Box signifies that (when checked) the user wants to add a special instruction to their order. The code looks like this:
If SpecialInstruction1CheckBox.Checked Then
AddSpecialInstruction(SPECIAL_INSTRUCTION_1_String)
End If
If SpecialInstruction2CheckBox.Checked Then
AddSpecialInstruction(SPECIAL_INSTRUCTION_2_String)
End If
If SpecialInstruction3CheckBox.Checked Then
AddSpecialInstruction(SPECIAL_INSTRUCTION_3_String)
End If
If SpecialInstruction4CheckBox.Checked Then
AddSpecialInstruction(SPECIAL_INSTRUCTION_4_String)
End If
I have a feeling that this code is unnecessarily verbose, feels repetitive, and could be simplified. How would I go about doing this, or is this not as "wrong" as it feels?
The first problem is that your special instructions should not be stored in separate variables. They should be stored in an array or some other kind of list. Then you could access them by index (e.g. specialInstructions(1)).
Then you can loop through the check boxes by index like this:
For i As Integer = 1 to 4
Dim box As CheckBox = DirectCast(Me.Controls("SpecialInstruction" & i.ToString() & "CheckBox"), CheckBox)
If box.Checked Then list.Add(specialInstructions(i))
Next
Alternatively, you could store references to your check boxes in an array and then loop through them more easily, for instance:
Dim checkBoxes() As CheckBox = {
SpecialInstruction1CheckBox,
SpecialInstruction2CheckBox,
SpecialInstruction3CheckBox,
SpecialInstruction4CheckBox}
' ...
For i As Integer = 0 to checkBoxes.Length - 1
If checkBoxes(i).Checked Then list.Add(specialInstructions(i))
Next
Another option would be to store the special instructions in the Tag property of each check box, then you could just retrieve the value from the control, like this:
For Each i As CheckBox In checkBoxes
If i.Checked Then list.Add(i.Tag)
Next
But that only makes sense if you don't need to reuse those special instructions values elsewhere in your code.
Actually the code isn't that bad in itself. It mainly depends on what AddSpecialInstruction does, exactly. Depending on your specifics it might be better to pass it a list of string instructions instead:
Dim list As New List(Of String)
If SpecialInstruction1CheckBox.Checked Then list.Add(SPECIAL_INSTRUCTION_1_String)
If SpecialInstruction2CheckBox.Checked Then list.Add(SPECIAL_INSTRUCTION_2_String)
If SpecialInstruction3CheckBox.Checked Then list.Add(SPECIAL_INSTRUCTION_3_String)
If SpecialInstruction4CheckBox.Checked Then list.Add(SPECIAL_INSTRUCTION_4_String)
AddSpecialInstructions(list)
Since you also required code shrinking, I made If statements holding on one line. Shorter variable names would help on that too.

Identify and populate a listbox

It's a riddle for me: what is the syntax to populate a listbox? But first: how do you identify a listbox? In many forums I read: ListBox1.Additem... But how do they know it's 'ListBox1'?
That's the default name for a ListBox control when you add it to your form. VB and VBA automatically name new or unnamed controls with the name of the type of control, suffixed with an integer identifier.
It's completely irrelevant what your control is called. The point of the sample code is to demonstrate a concept. You should replace ListBox1 with whatever your control is named.
And you should definitely name your controls something other than the default, because as you observe here, it's not very descriptive.
It used to be recommended by everyone to name controls following some type of quasi-Hungarian notation with a 3-letter prefix indicating the type of control, followed by your regular descriptive name. Over the past few years, there's been a big backlash against anything that looks like Hungarian notation, but I think it's still quite useful with regards to naming GUI controls, so I still use it. For a ListBox control, I might call it something like: lstCustomers or lstItemsForSale. But it's completely optional: again, what you choose to name your controls is irrelevant to how the code works or how the application will behave.
So, to populate a listbox in VB 6 and/or VBA, you'd use the following code, where myListBox is the name of your ListBox control:
' Add an item to the end of the list
myListBox.AddItem "Peaches"
' Insert an item at the top of the list
myListBox.AddItem "Apples", 0
' Remove the first item in the list
myListBox.RemoveItem 0
ListBox1.AddItem is for loading a single column ListBox (CodyGrey's answer covers that).
If you are using a multi column ListBox (property .ColumnCount > 1), then you need to load an Array into .List. The following snippet loads 3 rows into a 2 column ListBox
Dim dat(0 To 2, 0 To 1) As Variant
dat(0, 0) = "A"
dat(0, 1) = "B"
dat(1, 0) = "C"
dat(1, 1) = "D"
dat(2, 0) = "E"
dat(2, 1) = "F"
ListBox1.List = dat
Accessing the List Box: (this will vary for different versions of Word, this is for 2003)
To access the ListBox properties:
Display the "Control Toolbox" toolbar
Go To Design Mode
Click the control, and select Properties on the "Control Toolbox"
The Name property should now be visible and editable
Other properties, such as ColumnCount etc can also be set
When using VBA in Access, a good way to fill in the whole listbox at once is to use the RowSource property when the ListBox is a ValueList. The AddItem method is a bit slow when adding many entries.
For example, to fill in a list of files from a directory, you could use:
Dim src As String
Dim S as String
S = Dir(FullPathToCurrentDirectory & "\*.*")
While Len(S) <> 0
src = src & """" & S & """;"
S = Dir
Wend
If Len(src) = 0 Then src = """"";"
Me.lstFiles.RowSource = Left(src, Len(src) - 1) ' leave off the last ;