Why are controls missing from Me.Controls() - vb.net

Hey guys. I must be missing something. I am trying to cycle throught the lables on my form but it would appear that I am missing quite a few labels... I have a total of 69 lables on my form and I only get 5 hits on the msgbox. All controls were placed on design time on the form and not on panels or tabs. Also upon inspecting the me.controls. the count is incorrect as it is missing exactly 64 controls. (The missing lables).
Dim ctl As Control
For Each ctl In Me.Controls
If TypeOf ctl Is Label Then
MsgBox(ctl.Name)
End If
Next ctl
Any ideas why they would not show up?
Brad Swindell

The Controls collection is a heirarchy. You are only getting top level controls. If you want to get all controls then you will need to recursively dig into each child controls Control collection.
All controls were placed on design
time on the form and not on panels or
tabs.
Remember that GroupBox is also a control, with it's own Controls property as well.
This function should give you what you want, but my VB.Net is very, very rusty so if it doesn't compile I apologize.
Private Sub PrintAllControlsRecursive(col As Control.ControlCollection, ctrlType As Type)
If col Is Nothing OrElse col.Count = 0 Then
Return
End If
For Each c As Control In col
If c.GetType() = ctrlType Then
MessageBox.Show(c.Name)
End If
If c.HasChildren Then
PrintAllControlsRecursive(c.Controls, ctrlType)
End If
Next
End Sub

Sub PrintAllControls(ByVal ParentCtl As Control)
Dim ctl As Control
MsgBox(ParentCtl.Name + " start", MsgBoxStyle.Exclamation)
For Each ctl In ParentCtl.Controls
MsgBox(ctl.Name)
If ctl.HasChildren = True Then
PrintAllControls(ctl)
End If
Next
MsgBox(ParentCtl.Name + " End", MsgBoxStyle.Information)
End Sub

Flatten.
Just use LINQ and a recursive lambda with selectmany to flatten the hierarchy:
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim act As Func(Of Control, IEnumerable(Of Control)) =
Function(ctl) ctl.Controls.Cast(Of Control)().SelectMany(
Function(ctl2) ctl2.Controls.Cast(Of Control)().
Union(act(ctl2))).Union(ctl.Controls.Cast(Of Control))
MsgBox(Join((From c In act(Me).Distinct Order By c.Name
Select c.Name & "--" & c.GetType.ToString).ToArray, vbCrLf))
End Sub
Not barnyard programming at all nor chance of repeated items or obscure bugs...

be sure that you're finding controls AFTER the form has been completelly charged, otherwise if you try to list controls during load process the me.controls.count will be zero.

Related

Get the names of the datagridview present in the form

How can I check the number of DataGridViews on a form, and then display present their names in VB.NET?
I have tried this:
For Each dgv As DataGridView In Me.Controls
MsgBox(dgv.Name)
Next
But my guess is that Me.Controls consists of every other form controls except DataGridView?
For Each _control In Me.Controls
If TypeOf _control Is DataGridView Then
MsgBox(_control.Name)
End If
Next
I know this question is posted long time ago. In answer posted by #AadityaDengle You'll get DataGridView controls placed in form but, if DataGridView is nested in some other control (for example Panel, TableLayoutPanel, GroupBox, ...) it will not find it.
You have to search in all controls using "recursive search" way. Below is example :
'there will be stored names(id) of all DataGridView controls
Private allGrids As String = ""
Private Sub getAllGrids()
'loop through all controls on a form
For Each c As Control In Me.Controls
If TypeOf c Is DataGridView Then
'if control is DataGridView, then collect her name(id)
allGrids += c.Name.ToString + ","
Else
'if control isn't type of DataGridView and have child control(s), loop through that control
'(for example Panel, TableLayoutPanel,GroupBox, ...)
If c.HasChildren = True Then getAllGrids(c)
End If
Next
End Sub
Private Sub getAllGrids(cnt As Control)
'loop through all controls on a "container" control
'the search principle is the same like in getAllGrids on a form
For Each c As Control In cnt.Controls
If TypeOf c Is DataGridView Then
'collect DataGridView name(id)
allGrids += c.Name.ToString + ","
Else
'subroutine call hisself again with new control
If c.HasChildren = True Then getAllGrids(c)
End If
Next
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
MsgBox(allGrids)
End Sub
Even though both answers by #AadityaDengle and #nelek are right I will leave my code here that I think it's a little more structured.
Note that this code does work with or without your DataGridView is placed inside a Panel or a GroupBox.
'Loop throught all DataGridViews in your Form
Private Sub FormDGV(ByVal Controlo As Control)
If Controlo.HasChildren Then
If TypeOf Controlo Is DataGridView Then
MessageBox.Show(Controlo.Name)
End If
For Each Control As Control In Controlo.Controls
FormDGV(Control)
Next
End If
End SubĀ“
If your DataGridViews are nested in a Panel or something like that you need to send the specifc Panel through parameter like this -
FormDGV(YourPanel)
But if they are not, you just need to send the form itselt.

Looping through all Combo boxes on a VB form

I am making a grade application for a school project and I am wondering how I can loop through and check a value in all combo boxes on a certain form, I have 19 units to check; trying to be efficient without making 19 case statements. I have tried an array and me.controls.
Try this :
For Each c As Control In Me.Controls.OfType(Of ComboBox)()
'You cann acces to ComboBox her by c
Next
Note that if you have controls host by containers (TabControl, SPlitPanel, etc.) you may not find all of your controls.
Here is a recursive call that should return all of your controls:
Sub GetControlList(container As Control, ByVal ctlList As List(Of Control))
For Each child As Control In container.Controls
ctlList.Add(child)
If (child.HasChildren) Then
GetControlList(child, ctlList)
End If
Next
End Sub
I have on occasion had problems with the controls on SplitContainer panels so if you use a splitter make sure you are getting all your controls.
Once you have a complete list of controls you can operate on them. This sample is working with DataGridView controls:
Dim ctrls As New List(Of Control)
GetControlList(Me, ctrls)
For Each dgv As DataGridView In ctrls.OfType(Of DataGridView)()
AddHandler dgv.DataError, AddressOf DataGridView_DataError
Debug.Print(dgv.Name)
Next
FYI the generic data error code:
Private Sub DataGridView_DataError(sender As Object, e As DataGridViewDataErrorEventArgs)
Dim dgv As DataGridView = sender
Dim sGridName As String = dgv.Name.Replace("DataGridView", "")
Dim col As DataGridViewColumn = dgv.Columns(e.ColumnIndex)
Dim sColName As String = col.HeaderText
MsgBox(sGridName & vbNewLine & "Column " & sColName & vbNewLine & e.Exception.Message, MsgBoxStyle.Exclamation)
End Sub
You already have the OfType(OF T) method. You use it like this:
ForEach box As ComboBox In MyForm.Controls.OfType(Of ComboBox)()
Next
But this only checks the direct children of your control. If you have container controls like GroupBox, Panels, FlowControlLayoutPanel, etc, you'll miss the controls nested inside them. But we can build a new OfType() method to search these recursively:
Public Module ControlExtensions
<Extension()>
Public Iterator Function OfTypeRecursive(Of T As Control)(ByVal Controls As ControlCollection) As IEnumerable(Of T)
For Each parent As Control In Controls
If parent.HasChildren Then
For Each child As Control In OfTypeRecursive(Of T)(parent.Controls)
Yield child
Next child
End If
Next parent
For Each item As Control In Controls.OfType(Of T)()
Yield item
Next item
End Function
End Module
And you use it the same way:
ForEach box As ComboBox In MyForm.Controls.OfTypeRecursive(Of ComboBox)()
Next
You'll probably want to check containers for controls of the type you're looking for, here's a little function that should do the trick for you.
Private Function GetControls(Of T)(container As Control, searchChildren As Boolean) As T()
Dim Controls As New List(Of T)
For Each Child As Control In container.Controls
If TypeOf Child Is T Then
DirectCast(Controls, IList).Add(Child)
End If
If searchChildren AndAlso Child.HasChildren Then
Controls.AddRange(GetControls(Of T)(Child, True))
End If
Next
Return Controls.ToArray()
End Function
Here's the usage, if search children is True then all child containers will be searched for the control you're looking for. Also, for the top most container we'll just pass in Me assuming you're calling the code from within your Form, otherwise you could pass the Form instance or a specific Panel, GroupBox, etc.
Dim ComboBoxes As ComboBox() = GetControls(Of ComboBox)(Me, True)

Finding control in a Panel within a panel

I have a form in vb.net like so...
There is an outside panel named "pnlResults", Within that panel i have a further 10 panels.
As shown there is label with the text as "name" in each of these panels.
I would like to access these labels through a loop however I have tried the following without success.
For Each ctrl As Control In Me.Controls
If TypeOf ctrl Is Label Then
If ctrl.Name.StartsWith("lblName") Then
'Found the labels
End If
End If
Next
The names of all the labels I want to find start with "lblName", they are then identified individually buy a number from 1 to 10 following "lblname" i.e. "lblName1" etc all the way to 10
I believe this is due to the fact that a panel is its own container thus excluded from the loop of 'me.controls'. How do I get around this problem?
Using recursion
Private Sub findingAcontrol(ByRef panelx As Panel)
For Each Control As Control In panelx.Controls
If TypeOf Control Is Panel Then
Me.findingAcontrol(Control)
Exit Sub
End If
If Control.Name = "Button3" Then
MessageBox.Show(Control.Text)
End If
Next
End Sub
Add a button and call your function. For example:
Private Sub Button4_Click(sender As System.Object, e As System.EventArgs) Handles Button4.Click
For Each Control As Control In Me.Controls
If TypeOf Control Is Panel Then
Me.findingAcontrol(Control)
Exit Sub
End If
Next
End Sub
Here is another solution. You are correct in thinking that since the inner panels are also containers that your code isn't finding controls within them.
This solution just simply goes one level and once it finds an inner panel, finds the label based on how many inner panels you've gone through.
Update: I had assumed this was a web application. Here is an example of something you could do with a windows form.
Dim i As New Integer
i = 0
For Each ctr As Control In pnlResults.Controls
If TypeOf ctr Is Panel Then
i += 1
Dim lblName As New Label
lblName = ctr.Controls.Find("lblName" + i.ToString(), False)(0)
'Do something
End If
Next
Leaving the code for a web application just in case:
Dim i As New Integer
i = 0
For Each ctr As Control In pnlResults.Controls
If TypeOf ctr Is Panel Then
i += 1
Dim lblName As New Label
lblName = ctr.FindControl("lblName" + i.ToString())
'Do something
End If
Next
Search the panel's control collection for panels only.
For Each pl As Panel In pnlResults.Controls.OfType(Of Panel)()
For i As Integer = 1 To 10
Dim lb As Label = pl.Controls("lblName" & i.ToString) 'is the label
Next
Next

How does this work?

Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) _
Handles Button1.Click
EmptyTextBoxValues(Me)
End Sub
Private Sub EmptyTextBoxValues(ByVal parent As Control)
For Each c As Control In parent.Controls
If (c.Controls.Count > 0) Then
EmptyTextBoxValues(c)
Else
If TypeOf c Is TextBox Then
CType(c, TextBox).Text = ""
End If
End If
Next
End Sub
This sub is for to clear all textboxes value, i just need to know how did it work ??
The EmptyTextBoxValues sub recursively calls all child controls (if they exists) - if non exist, it checks if they are a text box and if so, clears it.
To start - it loops through every child control belonging to the passed in control:
For Each c As Control In parent.Controls
It then tests whether the child control has any child controls of its own, and if so, calls itself with the child control:
If (c.Controls.Count > 0) Then
EmptyTextBoxValues(c)
If no such child controls exist on the child control, a test is made whether the type of the control is of TextBox and if so, it is cleared:
Else
If TypeOf c Is TextBox Then
CType(c, TextBox).Text = ""
End If
It loops through all the controls that are direct children of the form and checks to see if each one is a TextBox control. If so, it clears it out. Also, it checks to see if each control is a container control which contains yet more children controls. If so, it recursively calls itself to process all those child controls as well.

Check which checkbox is checked with loop

What would be the syntax to check inside a visual basic form panel for checkboxes and find which are checked? I understand how I Could use a for loop and an if statement, but I'm confused as to the syntax to check for each checkbox. for example:
Dim i As Integer
For i = 1 To 10
'Here is where my code would go.
'I could have ten checkboxes named in sequence (cb1, cb2, etc),
'but how could I put i inside the name to test each checkbox?
Next
You need to loop through the Controls collection of the control that has the Checkbox's added to it. Each Control object has a Controls collection. I would prefer a For Each loop in this scenario so I get the Control right away without having to get it using the Controls index If your CheckBoxes are added to the Panel directly, the easiest way to do it would be..
For Each ctrl As var In panel.Controls
If TypeOf ctrl Is CheckBox AndAlso DirectCast(ctrl, CheckBox).IsChecked Then
'Do Something
End If
Next
I'm not very familiar with the VB.Net syntax, but in psudo-code:
ForEach CheckBox in ControlContainer
DoSomething
Next
If you have all of your CheckBox controls in a single container - e.g. a Panel - then the above code would iterate each control that is a CheckBox.
Try this :
Protected Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
If CheckBoxList1.Text = "" Then
do/display something
Exit Sub
Else
For Each item As ListItem In CheckBoxList1.Items
If item.Selected Then
do/display something
End If
Next
End If
End Sub