I am trying to use Ctype on a few textboxes I have so that I can put them in an array like so
dim textboxes(12) as textbox
for i=0 to 11
textboxes(i) = Ctype(form1.controls("textbox" & i+1), textbox)
next
This works fine for most of my controls. It does not work however, for any control that is within a TabControl. I thought that maybe doing something like
Ctype(form1.tabcontrol.control("textbox" & i+1), textbox)
might work, but it does not seem to help either.
Additional info: This is a winforms project.
TabControls have a collection of TabPage, which have a collection of Control. You'll need to scan all of these. Just scan them all and check if they are indeed checkboxes.
Also, I strongly suggest you use iterators (For Each is syntactic sugar for iterators), indexes get messy at some point.
dim myBoxes as new List(of TextBox)
For each tab as TabPage in form1.tabcontrol.TabPages
For each ctrl as Control in tab.Controls
If ctrl.GetType() Is GetType(TextBox)
myBoxes.Add(Ctype(ctrl, TextBox))
End If
Next
Next
'If you do need an array
return myBoxes.ToArray()
I found the answer to my initial question. As Hans points out in his comment,
The controls on a TabControl are located on one its tab pages, not on the TabControl itself.
So the correct code is something like:
dim textboxes(12) as textbox
for i=0 to 11
textboxes(i) = Ctype(form1.tabcontrol.tabpages(0).controls("textbox" & i+1), textbox)
next
After doing a bit of research and reading the various comments and warnings from other users, I realized that this is not the best way of approaching this problem, and I do not need to initialize an array for my textboxes. It is better not to put them in an array to make it easier to call them, but rather just call them like so
for i=0 to 11
form1.tabcontrol.tabpages(0).controls("textbox" & i+1).text = "sometext"
next
Related
As newbie in VBA.net i want to solve following.
I have a form with 38 text controls in 4 groupboxes. These get filled by clicking a row in a datagridview. I have 38 corresponding NUD's where i want the maximum to be equal to its corresponding text.
A 'pair' is always in one of the 4 groupboxes. Besides that there are also textboxes on the form itself to control the DGV.
I have a naming convention that makes it possible to match them easily . NUDGeel corresponds with txtGeel , NUDRood with TxtRood, NUDGroen with txtGroen etc et
Now that update is easily done if you do them one by one
NUDGeel.maximum = txtGeel.text
NUDRood.maximum = txtRood.text
etc
What i want to achieve is that this gets done in a for each loop. (Order to prevent me typing this 38 times (and also just to 'understand' it)
I can figure out how to start the loop
Dim c As Control
For Each c In Me.Controls
If TypeName(c) = "NumericUpDown" Then
'do some magic
End If
Next
I have tried to search for the magic code, and i guess from research it is pretty simple, i just donot get it .
Anyone with an idea how to fix ?
For Each tb In Controls.OfType(Of TextBox)
Dim nud = DirectCast(Controls(tb.Name.Replace("txt", "NUD")), NumericUpDown)
If nud IsNot Nothing Then
nud.Maximum = CDec(tb.Text)
End If
Next
You don't need the If statement if there are no other TextBoxes on the form besides those with corresponding NumericUpDowns.
Private Sub SetMaximum()
Dim controlNames() As String = {
"Geel", "Rood", "Name1", "Name2", ' list all the names of your controls here excluding NUD/txt
}
For Each controlName As String In controlNames
CType(Me.Controls("NUD" & controlName),NumericUpDown).Maximum = CType(Me.Controls("txt" & controlName),TextBox).Text
Next
End Sub
This assumes that all the controls are not inside any containers. If they are, you must use the container (panel, groupbox, etc) instead of Me.
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!
I have a vb project of an event ticket selling stuff, and at the end I need to save a text file for each purchase.
I have tabbed controls, and at the very end, all the data that needs to go in the text file (event and customer) are in one tab.
I have this code, that will read the text from each textbox, and for now for testing purposes it throws a message box with the value. It is working, the only thing is, that it displays the values in an odd order and I don't know how to have them read in the required order.
(also it wouldn't hurt, if I could add the labels before the textbox.text but I'm not that greedy :) )
For Each GenericControl In TabPurchaseTickets.Controls
If TypeOf GenericControl Is System.Windows.Forms.TextBox Then
Dim tb As TextBox = DirectCast(GenericControl, TextBox)
MsgBox(tb.Text)
End If
Next
You need to have something that helps you identify these textboxes and their values before writing them on a file.
Relying on the order of the textboxes on the tabcontrol will be a great mistake in future revision of your application. If you need to change that order older file will cause a 'versioning' problem.
You could define the Tag property at design time for each textbox with a value that helps you identify them and write your file with the tag value, a separator and the textbox value
Dim sb = new StringBuilder()
For Each GenericControl In TabPurchaseTickets.Controls.OfType(Of TextBox)
Dim tb As TextBox = DirectCast(GenericControl, TextBox)
sb.AppendFormat("{0};{1}", tb.Tag, tb.Text)
sb.AppendLine()
Next
And now write the StringBuilder.ToString to your textfile, you will end up with something like this
Name;John
Surname;McInroe
Sport;Tennis
....
In this way you could change the order of your textboxes as you like becase every value is associated to the Tag property and you could easily reload it.
Of course this is just an example and I suggest you to investigate the use of a proper database system instead of a simple file.
It is a really ugly solution, but it works. Unfortunately in this project this is plenty! :D
Thanks for the tag idea, it's nice to find out that there is another field where I can "store" text in a textbox.
anyway, here is the bodge code *note the msgbox is just for testing the output, this will be modified to save in a textfile later:
Dim labels(15) As String
Dim fields(15) As String
For Each GenericControl In TabPurchaseTickets.Controls.OfType(Of TextBox)()
Dim tb As TextBox = DirectCast(GenericControl, TextBox)
fields(tb.TabIndex) = tb.Text
labels(tb.TabIndex) = tb.Tag
Next
For i As Integer = 0 To 15
MsgBox(labels(i) & ": " & fields(i))
Next
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.
In a Visual Basic 2010 form application I have the below code snippet:
For Each ctlControl In Me.Panel1.Controls
If TypeName(ctlControl) = "PictureBox" Then
ctlControl.image = Nothing
End If
Next ctlControl
My problem is when it loops through the controls it does not start with the top left control and it seems to go over each picture box in random order.
How do I control the order of which picture box is updated next. Is there a property similar to tab index(in VB 6) which I can manipulate to control the order in which picture boxes are updated by my loop?
As a more proper and sure way, I would get each picture box, keep handles and locations of them, then sort them according to their location. Now they are ready to use. Here is an example:
Public Class Form1
Structure Pbox
Dim handle As IntPtr
Dim top As Integer
Dim left As Integer
End Structure
Dim pboxlist As New List(Of Pbox)
Sub ClearImages()
pboxlist.Clear()
For Each c As Control In Me.Controls
If TypeName(c) = "PictureBox" Then
Dim x As New Pbox
x.top = c.Top
x.left = c.Left
x.handle = c.Handle
End If
Next
pboxlist.OrderByDescending(Function(a) a.top).ThenByDescending(Function(a) a.left)
For Each item In pboxlist
Dim x As PictureBox = PictureBox.FromHandle(item.handle)
x.Image = Nothing
Next
End Sub
End Class
Another approach is using a good naming, so that you can use their names to sort them. For instance, PictureBox1 will come before PictureBox2 if you sort. So you should use PictureBox1 for the very top and left one and PictureBox2 for the next one and so on...
EDIT: Using Tag property, as John Bustos suggested, instead of names is an easier and better idea. So without getting lost in names, you can sort picture boxes according to their Tags which are defined by you.
As some of the other guys have said you could use the TAG property which is probably the BEST shot, whenyou are dynamically creating the picture boxes use a counter and add the counter value to the TAG property. if you added the picture boxes manually then simply start with the top left and work towards the right and add a value in the TAG property field of each one starting with 1 and increasing by one each time and continue until the row is completed then carry on with the next row.
Finally when your ready to loop through the picture boxes simply follow the pattern below..
'Calc number of picture boxes
For Each ctlControl In Me.Panel1.Controls
If TypeName(ctlControl) = "PictureBox" Then
Counter = Counter + 1
End If
Next ctlControl
ThisBox = 1
Do
For Each ctlControl In Me.Panel1.Controls
If TypeName(ctlControl) = "PictureBox" Then
If CInt(ctlControl.Tag) = ThisBox Then
CLEAR-YOUR-IMAGE-HERE
ThisBox = ThisBox + 1
End If
End If
Next ctlControl
Loop Until ThisBox = Counter
Note:
Its IMPORTANT your numbers that you place in the TAG property are consecutive or you will become forver stuck in the DO-LOOP!!!
The order of the controls was determined by the order they were added to the panel and not tabstop index. You can change that by carefully reorganizing the order they were added to the panel in the form's designer file, though I'd not recommend it.
The PictureBox control has a Text property you can use instead of Tag.
It doesn't come up in Intellisense because it's an infrastructure property, but it's there.
http://msdn.microsoft.com/en-us/library/hc9k45f4(v=vs.110).aspx
(I wanted to comment on Zaf Khan's answer but I don't have the rep, yet.)