How to Use For Each cnt Loop to Target Specific Tags on Controls - vb.net

I'm making a form that has a few hundred labels, and when a Clear button is clicked, I need to reset the text of specific labels to 0 while leaving other label's text alone. I don't want to use a group box because it will not look good with my current layout.
I'm trying to use the code:
For Each cnt In Me.Controls
If TypeOf cnt Is Label Then
CType(cnt, Label).Text = ""
End If
Which works fine for clearing every Label, but I want to specify a specific Tag as well. I tried
For Each cnt In Me.Controls
If TypeOf cnt Is Label And CType(cnt, Label).Tag = "ResetTo0" Then
CType(cnt, Label).Text = ""
End If
When I try to use this code, I get a cast exception error.
Does anyone know how I can add in my tag as well without getting a cast error, or a better way to do this?

Just use the extension OfType to get only labels and already of the right type
For Each cnt In Me.Controls.OfType(Of Label)
If cnt.Tag = "ResetTo0" Then
cnt.Text = ""
End If
Next
And if not all labels have the Tag property set then add also a check for Nothing
if cnt.Tag IsNot Nothing AndAlso cnt.Tag = "ResetTo0" Then
.....
End if
You can even try with a single line (albeit I suspect that this approach is not the best for clarity and performance)
Me.Controls.OfType(Of Label).
Where(Function(x) x.Tag = "ResetTo0").
ToList().
ForEach(Function(k) k.Text = "")

Related

Change Label Text with Controls in VB

I'm trying to change a Label Text with Controls command, using this line
Controls("C_" & 0).Text = "Conta:"
But I get this error
"System.NullReferenceException"
If I delete this label and change it for a textbox (with same name "C_0"), it works! But I need to do this with a Label not a textbox...
This is because you do not have a control named C_0. I would suggest using ControlCollection.Find to get your control and then use a conditional If statement to check if the returned control exists:
Dim desiredControls() As Control = Me.Controls.Find("C_" & 0, True)
If desiredControls.Count = 0 Then
'No controls named C_0 found
ElseIf desiredControls.Count > 1 Then
'Multiple controls named C_0 found
Else
desiredControls(0).Text = "Conta:"
End If
Or if you simply wanted a one-liner then you would use:
Me.Controls.Find("C_" & 0, True).First().Text = "Conta:"
However, I would highly recommend that you use the conditional If statements so that you an exception isn't thrown if 0 controls are found.
Ok, I find the problem... This command wasn't working because it was inside a GroupBox.
Then the right code is
Me.Controls("GroupBox1").Controls("C_" & 0).Text = "123"
Thanks for everyone help!
Your problem is control.Controls only returns the controls directly inside the control. So you can use these extension methods. Put this in a Module:
<Extension>
Public Function ChildControls(parent As Control) As IEnumerable(Of Control)
Return ChildControls(Of Control)(parent)
End Function
<Extension>
Public Function ChildControls(Of TControl As Control)(parent As Control) As IEnumerable(Of TControl)
Dim result As New List(Of TControl)
For Each ctrl As Control In parent.Controls
If TypeOf ctrl Is TControl Then result.Add(CType(ctrl, TControl))
result.AddRange(ctrl.ChildControls(Of TControl)())
Next
Return result
End Function
Here is how you would use it:
' general option to return all controls, filter on name
Me.ChildControls().Single(Function(c) c.Name = "C_" & 0)).Text = "Conta:"
' generic option to return only Labels, filter on name
Me.ChildControls(Of Label)().Single(Function(c) c.Name = "C_" & 0)).Text = "Conta:"
This will work whether the Label is in the GroupBox or not, and if you move it to a different GroupBox, Panel, or back to the Form, without needing to change your code.

Select Textbox using For Loop

Sorry this is weird and silly question but I need a solution of this...
I have 30 textboxes named as Txt1,Txt2,Txt3,...,Txt30
I have to fill textbox as Txt1.text = 0 on button click..
Is there any way like On Button Click
Dim i as Integer
For i = 1 to 30 '----- Possible in string but don't know if possible in textboxes
Txt(i).text = 0
Next
Or I have to write all 30 lines like
Txt1.text = 0
...
Txt30.text = 0
I don't know how this question asked, maybe Question improper.
Thanx in Advance...
If the textboxes are all contained in Controls collection of the Form, then it is really easy to loop over them
For Each(t in Me.Controls.OfType(Of TextBox)())
t.Text = "0"
Of course the advantage of this approach is that you don't have to worry if you add other textboxes to your form. They will be found using the foreach loop without having a fixed top limit.
And, if not all of your textboxes should be included in the loop, then you can simply use the Tag property of the textboxes that you want to use. For example, if you set the Tag property to the string "Y", then you can change the foreach loop to find only the controls with the matching Tag property
For Each t in Me.Controls.OfType(Of TextBox)() _
.Where(Function(x) x.Tag = "Y")
t.Text = "0"
The two solutions above works well if all your textboxes are contained in the same container (Form, GroupBox or Panel), instead, if these textboxes are dispersed in different containers (some in a groupbox, others in a panel etc) then you can build a List(Of TextBox) variable filling it with the textboxes instances and use it when the need arise
Dim myTexts = New List(Of TextBox)() From { Txt1, Txt2, Txt3, ....}
And loop over this variable
You can do as Steve said, but if you have other textboxes that you don't want to edit then there is another way:
For i = 1 to 30
dim found = Me.Controls.Find("Txt" & i, True) '<- the True argument is for recursive search
If Not IsNothing(found) AndAlso found.Length > 0 Then
found(0).Text = "0"
End If
Next
If your textboxes are all in the same control, for example GroupBox, you can do GroupBox.Controls.Find saving CPU resources

Set a group of controls visible with one line of code?

Is it possible to clump a group of controls together and be able to set it visible with one line rather than having to do each individual control's .visible property? I know it doesn't hurt anything but would like to keep it looking neat and not clump up a function with a page full of .visible control calls.
Just group your controls in a List(Of Control) or an array and set the Visible property using either the ForEach-method or a simple For Each-loop.
e.g.:
Dim toToggle = {OkButton, CancelButton, ControlPanel, SelectionComboBox}
For Each ctrl in toToggle
ctrl.Visible = False
Next
or
Dim toToggle = {OkButton, CancelButton, ControlPanel}.ToList()
toToggle.ForEach(Sub(c) c.Visible = False)
I like Dominic's solution. Another approach (and this depends on how your Winform is laid out) would be to group the controls into a panel:
For Each ctrl as Control in MyPanel.Controls
c.Visible = False
Next
Really all this approach does is keeps you from having to create a new list, but maybe that would be better so you can choose precisely which controls to add.

Looping through Controls in VB.NET

I am creating a chess program. And it is composed of sixty four picture boxes with alternating black and white background colours.
I have named them pba1, pba2, pbb1, pbb2, pbc1 and so on.
Now, I want to loop through only the black ones, for example, I want to loop through only, pba1, pbb2, pbc3 and so on.
How do I create a loop for this in VB.NET?
I know of the way to loop through similarly named controls, but I am not able to adapt that method for my problem. Can you tell me a solution?
EDIT: In pba1, pb stands for picture box, and a1 stands for the square. Just in case, you wonder why such a name.
EDIT: Check out this answer
Loop through the PictureBox's in your ControlCollection and test for BackColor. I used the Form's ControlCollection, if they are in some other type of container control use that.
For Each cntrl As Control In Me.Controls
If TypeOf cntrl Is PictureBox Then
If cntrl.BackColor = Color.Black Then
'Do Something
End If
End If
Next
Base on the additional information that you gave in your answer, the reason your example is not working is that the Controls Name is a String and you are comparing it to the PictureBox Control not the Name of the Control.
You can try using the Tag Property instead of the Name of the Control, it will be cleaner and easier to read. I just put a 1 in the PictureBox's Tag Property for Black and a 0 for White.
Private Sub OriginalColour()
For Each cntrl As Control In Me.Controls
Dim result As Integer
If TypeOf cntrl Is PictureBox Then
If Integer.TryParse(cntrl.Tag.ToString, result) Then
If result = 1 Then
cntrl.BackColor = Color.Gray
Else
cntrl.BackColor = Color.White
End If
End If
End If
Next
End Sub
Generating controls at design time via the Forms Designer only makes sense for layouts which benefit from the forms designer.
In your case, you just have 64 uniform boxes in 8 rows of 8. Don’t use the Forms Designer for this, create the controls at runtime, and don’t give them names like pba1, just put them into an appropriate data structure (such as an 8x8 array):
Private chessFields As PictureBox(8, 8)
' In Form_Load:
For i = 0 To 7
For j = 0 To 7
chessFields(i, j) = New PictureBox
' Set size, position … then, finally,
Controls.Add(chessFields(i, j))
Next
Next
That way, you can access the fields in an orderly fashion without having to go via the Form.Controls collection.
Put all the pictureboxes in an 8x8 tableLayoutPanel (also useful for scaling etc). Then
For Each pb As PictureBox In TableLayoutPanel1.Controls
Dim col As Integer = TableLayoutPanel1.GetCellPosition(pb).Column
Dim row As Integer = TableLayoutPanel1.GetCellPosition(pb).Row
If col Mod 2 = 0 Xor row Mod 2 = 0 Then
pb.BackColor = Color.Black
Else
pb.BackColor = Color.White
End If
Next
Of course you could also use an array of the squares if you have that available.
This will not affect the events (pba1.click etc).
This is fairly simple and it may be resource heavy, but it works. I have a form with 36 CheckBoxes. This takes advantage of the fact that when you copy a checkbox it just increases the number of the name. I ended up with 36 checkboxes named CheckBox1 thru Checkbox36. The Function returns a checkbox, which may be used to set or read any property.
Private Function GetCheckBox(ByVal Index As Integer) As CheckBox
Dim CKBox As checkbox
For Each cntrl As Control In Me.Controls
If TypeOf cntrl Is CheckBox Then
CKBox = cntrl
If CKBox.Name = "CheckBox" & Index Then
Exit For
End If
End If
Next
Return ckbox
End Function

VB.NET Controls Not Visible

I am trying to see all the labels in Me.Controls and when I use:
For Each Control As Label In Me.Controls.OfType(Of Label)()
MsgBox(Control.Name.ToString)
Next
it only shows the labels that have NOT been renamed. Am I doing something wrong here?
For the most part, your code looks right, unless you have labels inside other container controls like Panels and GroupBoxes. In which case, you would need to loop through those containers, too.
Here is an example:
Dim allContainers As New Stack(Of Control)
allContainers.Push(Me)
While allContainers.Count > 0
For Each item As Control In allContainers.Pop.Controls
If item.Controls.Count > 0 Then
allContainers.Push(item)
End If
If TypeOf item Is Label Then
MessageBox.Show("Label.Name = " + item.Name)
End If
Next
End While