How do I iterate through Components on a Windows Form? - vb.net

I'm using VisualStudio 2008 on a system using .NET Compact Framework 3.5 in VB.NET and have a form that contains a couple of Components, namely Timers and Messageboxes. Part of my application is a sub that iterates through all the Controls in the form and adjusting properties like the Front or BackColor so that we can reskin the entire application on demand.
While I'm iterating through these Controls I'd also like to iterate through the Components and set some properties of the Messageboxes on the form. I've tried using a For Each to access Me.components.Components but that collection appears to be private.
For Each comp As Object in Me.components
<do something>
Next comp
Is there a way to iterate through the components?
EDIT:
I was wrong thinking that me.components.Component is private. Using Me.components.Components I get the following error:
'Components' is not a member of 'System.ComponentModel.IContainer".

You can loop throught timers with this: ....
For Each c As Object In Me.components.Components
If TypeOf c Is Timer Then
Dim tim As Timer = CType(c, System.Windows.Forms.Timer)
tim.Interval = 12345
End If
Next
Change the .Interval = 12345 with anithing you want to do to the timers.
.... And through other controls with:
For Each ctrl As Control In Me.Controls
If (ctrl.GetType() Is GetType(TextBox)) Then
Dim txt As TextBox = CType(ctrl, TextBox)
txt.BackColor = Color.LightYellow
End If
Next
Change the TextBox with the type of control
And .BackColor = Color.LightYellow with anything you want to do with the control.
Unfortunatly i don't know how to do is with MesseageBoxes :(

Related

Unchecking all RadioButtons in a For Each Control loop

I've got 7 RadioButtons on my form, 5 in one group, 2 in another. When they were in GroupBoxes, checking one of the RadioButtons didn't uncheck the other ones in the same GroupBox, for some reason - I think because the AutoCheck value was set to False, which it had to be because otherwise when the form loaded, one of the RadioButtons would be checked by default, and I don't want this to happen.
So, to uncheck the other RadioButtons within the same GroupBox, I'm trying to write a subroutine which is called, after setting the value to True in the RadioButton_Click event.
For Each c As Control In Me.Controls
If TypeOf c Is RadioButton Then
If c.Name <> "rbtnAllSuppliers" AndAlso c.Name <> "rbtnIndividual" Then
If c.Name <> rbtn.Name Then
For Each rbt As RadioButton In Me.Controls
If rbt.Name = c.Name Then
rbt.Checked = False
End If
Next
End If
End If
End If
Next
This code moreorless works. For each control on the form, if it's a RadioButton which isn't called rbtnAllSuppliers or rbtnIndividual (The 2 seperate RadioButtons, and it's not the RadioButton that is being set to True, then set it to False.
The issue being, it is counting the 2 Labels as RadioButtons, or at least casting them to be so, so it errors when trying to set a ``Checkedvalue of theLabel`.
I then tried something similar
For Each c As Control In Me.Controls
If c.GetType Is GetType(RadioButton) Then
If c.Name <> "rbtnAllSuppliers" AndAlso c.Name <> "rbtnIndividual" Then
If c.Name <> rbtn.Name Then
AddHandler DirectCast(c, RadioButton).Checked, AddressOf c.
End If
End If
End If
Next
But I'm not sure what I would put for the AddressOf code? What would go in here as the delegate? If I put AddressOf currentSubroutine(), surely an infinite loop will be created?
Is there a way to uncheck all RadioButtons using a similar method that I'm not aware of?
What's the best way to go about achieving this?
By using Me.Controls.OfType(Of RadioButton) you can avoid having to check the type inside your loop, and will ensure that every control is a RadioButton
To add to the suggested approach, you can also specify which group you want to loop through the controls.
For Each RadioButtonItem In TargetGroupBox.Controls.OfType(Of Radiobutton)
RadioButtonItem.Checked = False
Next

Loop - set Textboxes to ReadOnly in all Groupboxes

I have groupboxes that contain textboxes and I want to set all of them to ReadOnly=True. Here is what I tried (doesn't work):
EDIT (I forgot about Split container):
For Each SplitCon As Control In Me.Controls
If TypeOf SplitCon Is SplitContainer Then
For Each GBox As Control In SplitCon.Controls
If TypeOf GBox Is GroupBox Then
For Each ctrl As Control In GBox.Controls
If TypeOf (ctrl) Is TextBox Then
CType(ctrl, TextBox).ReadOnly = True
End If
Next
End If
Next
End If
Next
You can simplify things a great deal. Rather than looking thru all sorts of Control collections, create an array to act as a ToDo list. Then you can get rid of all the TypeOf and CType in the loop used.
' the ToDo list
Dim GrpBoxes = New GroupBox() {Groupbox1, Groupbox2,
Groupbox3, Groupbox4}
For Each grp In GrpBoxes
For Each tb As TextBox In grp.Controls.OfType(Of TextBox)()
tb.ReadOnly = True
Next
Next
Your code no longer depends on the form layout of the moment. The only thing you have to remember is to add any new GroupBox items to your list. You can also declare the array once ever for the whole form if you prefer (or even in the For Each statement)
Rather than working with Control objects, Controls.OfType(Of T) filters the collection and returns an object variable of that type, so there is no need to cast it or skip over controls you are not interested in. You can also tack on a Where method to further refine the list to include only do those with a certain name or Tag.

Handle containers inside containers in for each control vb 2008

I have created a function to translate my forms. I can loop through every control in a form to call this function, but I have made a situation, I cannot handle.
In one of my forms, I have groupbox in a groupbox.
This source works if I only have one groupbox.
Public Function translate_form(ByVal form As Form)
Dim control As Object
Dim controlname As String
form.Text = Get_Control_Name(form.Name, "Form")
Try
For i = 0 To form.Controls.Count - 1
control = form.Controls(i)
If TypeOf (control) Is MenuStrip Then
For j = 0 To control.items.count - 1
control.items(j).text = Get_Control_Name(form.Name, "MenuItem" & j)
Next
Else
controlname = Get_Control_Name(form.Name, control.Name)
control.Text = IIf(controlname Is Nothing, control.Text, controlname)
If TypeOf (control) Is GroupBox Then
For j = 0 To control.Controls.Count - 1
controlname = Get_Control_Name(form.Name, control.Controls(j).Name)
control.Controls(j).Text = IIf(controlname Is Nothing, control.Controls(j).Text, controlname)
If TypeOf (control.Controls(j)) Is Button Then
control.Controls(j).AutoSize = True
End If
Next
End If
If TypeOf (control) Is Button And UCase(control.Text) <> "X" Then
control.AutoSize = True
End If
End If
Next
Catch ex As Exception
MsgBox(ex.Message, MsgBoxStyle.Critical)
End Try
End Function
But in some cases I want to sperate controls inside a container. I could have one more loop if the
control.Controls(j)
is a groupbox but I want to make this function to handle any kind of "container pyramid", if you know what I mean. Maybe I will have a container which has one also and that one too etc... Or is there any control I can use as a groupbox but it doesn't count as a contaier, so I can see it with:
form.Controls
Any suggestions?
Thanks in advance.
The reason why your code does not deliver what you want is that you are not performing a recursive search of controls. Bear in mind that Form.Controls contains only the parent controls (not the children controls eventually contained by the parents; like the situation you refer of controls contained by a GroupBox). Additionally, I see various not-too-right issues (you should writeOption Strict On on the top of your file) and that's why this answer intends to provide you with a somehow better framework to work with (you have just to fill in the blanks with your code):
Public Sub translate_form2(ByVal form As Form)
Try
For Each ctrl As Control In form.Controls
actionsCurrentControl(ctrl)
recursiveControls(ctrl)
Next
Catch ex As Exception
End Try
End Sub
'Accounting for all the child controls (if any)
Public Sub recursiveControls(parentControl As Control)
If (parentControl.HasChildren) Then
For Each ctrl As Control In parentControl.Controls
actionsCurrentControl(ctrl)
recursiveControls(ctrl)
Next
End If
End Sub
Public Sub actionsCurrentControl(curControl As Control)
If TypeOf curControl Is MenuStrip Then
Else
If TypeOf (curControl) Is GroupBox Then
End If
If TypeOf (curControl) Is Button And UCase(curControl.Text) <> "X" Then
End If
End If
End Sub
translate_form2 iterates through all the parent controls as in your code (but by relying on a set of Subs (you are wrongly using a Function without returning any value, what is wrong), making the structure more adaptable); it also calls recursiveControls (which also calls itself for each control it analyses) to take care of any child control which might be present. I am also including actionsCurrentControl which contains all the actions to perform for each control (you have to populate it with your code).

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

Invalid Conversion error

I recently upgraded a VB 6 project to .net. I'm having a problem with this block of code:
Dim CtrlName As System.Windows.Forms.MenuItem
For Each CtrlName In Form1.Controls
'Some code here
Next CtrlName
Now this code compiles but throws the following runtime error:
Unable to cast object of type 'System.Windows.Forms.Panel' to type 'System.Windows.Forms.MenuItem.
I have a panel control on the subject form. How do I resolve this?
Thanks.
You are iterating over all controls that are directly inside the form, not just the MenuItems. However, your variable is of type MenuItem. This is causing the problem.
For normal controls (e.g. Buttons), you’d want to use the following, easy fix; test inside the loop whether the control type is correct:
For Each control As Control In Form1.Controls
Dim btt As Button = TryCast(control, Button)
If btt IsNot Nothing Then
' Perform action
End If
Next
However, this does not work for MenuItems since these aren’t controls at all in WinForms, and they aren’t stored in the form’s Controls collection.
You need to iterate over the form’s Menu.MenuItems property instead.
The items in the Controls property of a form, which may or may not be MenuItem. Assuming that you just want to iterate over MenuItem objects you can change your code to:
For Each menuControl As MenuItem In Me.Controls.OfType(Of MenuItem)
' Some code
Next
Note that the menuControl variable is declared in the For so is only accessible within the block and is disposed automatically.
for each ctrl as control in me.controls
if typeof ctrl is menuitem then
' do stuff here
end if
next
typeof keyword allows you to test the type of control being examined in the control collection.
Found the answer after a bit of research, you need to search for the menu strip first and then loop through the items collection.
For Each ctrl As Control In Me.Controls
If TypeOf ctrl Is MenuStrip Then
Dim mnu As MenuStrip = DirectCast(ctrl, MenuStrip)
For Each x As ToolStripMenuItem In mnu.Items
Debug.Print(x.Name)
Next
End If
Next