For Loop with data Binding - vb.net

I am trying to databind position 0 as "" in a number of dropdownlists, my code below:
For Each ctrl As Control In Me.Controls
If TypeOf ctrl Is DropDownList Then
ctrl.DataBind()
ctrl.items.insert(0, "")
End If
Next
I get error: Error BC30456 'items' is not a member of 'Control'
I am a bit at a loss... Please help!
Thanks Tim, what would the code then be to add the "" to the dropboxes..

DataBind() is not necessary if the dropDownList is already bounded. you can insert values to required index as like the following code, You are getting the error because the Sysetem.web.UI.Controls doesn't have a items. so you can achieve this by casting the ctrl as a DropDownList
For Each ctrl As Control In Me.Controls
If TypeOf ctrl Is DropDownList Then
Dim tempDropDown As DropDownList = DirectCast(ctrl, DropDownList)
tempDropDown.Items.Insert(0, "")
End If
Next

You have to cast the Control to DropDownList,otherwise you cant use properties or methods of the child-class DropDownList but only of Control.
For Each ctrl As Control In Me.Controls
If TypeOf ctrl Is DropDownList Then ' <--- Type-Check
Dim ddl = DirectCast(ctrl, DropDownList) ' <--- Cast
ddl.DataBind()
ddl.items.insert(0, "")
End If
Next
Here's another way using Enumerable.OfType (add Imports System.Linq at the top of the file):
For Each ddl In Me.Controls.OfType(Of DropDownList)()
ddl.DataBind()
ddl.items.insert(0, "")
Next
Control.Controls does not return controls recursively, so not nested child controls. Maybe your DropDownList is in another container control like a GridView, then you wont find it in this way. In this case the correct way would be to handle the RowDataBound-event and use GridViewRow.FindControl("drpSite") to get the reference. If you can't do that or you want to use a recursive way to find all DropDownLists you can use this extension method:
Public Module Extensions
<Runtime.CompilerServices.Extension()>
Public Function OfTypeRecursive(Of T As Control)(ByVal root As Control) As IEnumerable(Of T)
Dim allControls = New List(Of T)
Dim queue = New Queue(Of Control)
queue.Enqueue(root)
While queue.Count > 0
Dim c As Control = queue.Dequeue()
For Each child As Control In c.Controls
queue.Enqueue(child)
If TypeOf child Is T Then allControls.Add(DirectCast(child, T))
Next
End While
Return allControls
End Function
End Module
Now it's simple:
Dim allDdls As IEnumerable(Of DropDownList) = Me.OfTypeRecursive(Of DropDownList)()

Related

Iterating between methods of controls in a form

I've got a number of panels, each of which with a number of groupboxes containing various type of controls.
Once in a while, I need to update the texts and values of the controls with fresh data, but I would not like to fire up events connected to every control.
So I'd like to removehandlers for each of the involved controls, change the texts and values, and then add the handlers once again.
I know I have to use CallByName, but its syntax is somewhat obscure to me.
Anyone would like to have a look at the provided meta-code below (example for selectedindexchanged methods) and provide the necessary correction, I'd appreciate.
For Each mpanel In Me.Controls
If TypeOf (mpanel) Is MetroPanel Then
For Each gbox In mpanel.controls
If TypeOf gbox Is GroupBox Then
For Each ctrl In gbox.controls
If TypeOf (ctrl) Is MetroComboBox Then
Dim methodName As String = ctrl.name.ToString & "_Selectedindexchanged"
Dim method = CallByName(ctrl, methodName, CallType.Method)
RemoveHandler DirectCast(ctrl, MetroComboBox).SelectedIndexChanged, method
End If
Next
End If
Next
End If
Next
It is possible to remove eventhandler by name by using the following syntax:
For Each mpanel In Me.Controls
If TypeOf (mpanel) Is MetroPanel Then
For Each gbox In mpanel.controls
If TypeOf gbox Is GroupBox Then
For Each ctrl In gbox.controls
If TypeOf (ctrl) Is MetroComboBox Then
Dim methodName As String = ctrl.name.ToString & "_SelectedIndexChanged"
Dim method As System.Reflection.MethodInfo = Type.GetType("MyClass").GetMethod(methodName)
RemoveHandler DirectCast(ctrl, MetroComboBox).SelectedIndexChanged, Addressof method
End If
Next
End If
Next
End If
Next

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)

Loop controls in form and tabpages

I have form with tabcontrol and few tab pages which contain many settings in textboxes and checkboxes.
When user press Exit from this form I have to check if data was changed.
For that I thought to make a string on Enter of all values on form and compare it with string of all values on exit:
Private Function getsetupstring() As String
Dim setupstring As String = ""
For Each oControl As Control In Me.Controls
If TypeOf oControl Is CheckBox Then
Dim chk As CheckBox = CType(oControl, CheckBox)
setupstring &= chk.Checked.ToString
End If
If TypeOf oControl Is TextBox Then
setupstring &= oControl.Text.Trim.ToString
End If
Next
Return setupstring
End Function
But that code don't loop through controls which are on tab pages, only TabControl and few buttons which are on top of form.
What to do to get all controls listed so I can pick values?
Controls only contains the parent controls, not the respective children. If you want to get all the controls (parents and respective children), you can rely on a code on these lines:
Dim allControls As List(Of Control) = New List(Of Control)
For Each ctr As Control In Me.Controls
allControls = getAllControls(ctr, allControls)
Next
Where getAllControls is defined by:
Private Function getAllControls(mainControl As Control, allControls As List(Of Control)) As List(Of Control)
If (Not allControls.Contains(mainControl)) Then allControls.Add(mainControl)
If mainControl.HasChildren Then
For Each child In mainControl.Controls
If (Not allControls.Contains(DirectCast(child, Control))) Then allControls.Add(DirectCast(child, Control))
If DirectCast(child, Control).HasChildren Then getAllControls(DirectCast(child, Control), allControls)
Next
End If
Return allControls
End Function
Other alternative you have is relying on the Controls.Find method with the searchAllChildren property set to True.

vb.net find controls that begins with specified string in a form

I want to list all names of my buttons that begins with "btn" but these buttons are place in different panels. I have this in my mind
dim obj() as object in frmForm.Controls.Find(True,"btn*")
but I think it might be wrong..
First, the first parameter is the name and the second a bool which indicates whether you want to search recursively or not.
Second, there is no builtin way for this. I would use your own method, one like this:
Public Function FindControlStartsWith(root As Control, name As String, recursive As Boolean, comparison As StringComparison) As Control()
If root Is Nothing Then
Throw New ArgumentNullException("root")
End If
Dim controls = New List(Of Control)
Dim stack = New Stack(Of Control)()
stack.Push(root)
While stack.Count > 0
Dim c As Control = stack.Pop()
If c.Name.StartsWith(name, comparison) Then
controls.Add(c)
End If
If recursive Then
For Each child As Control In root.Controls
stack.Push(child)
Next
End If
End While
Return controls.ToArray()
End Function
Use it in this way:
Dim obj() As Control = FindControlStartsWith(Me, "BUT", True, StringComparison.OrdinalIgnoreCase)
I do something similar with the type of control, but it can easily be modified for name. Try the code below:
Private Sub findcontrols(ByVal root As Control)
For Each cntrl As Control In root.Controls
findcontrols(cntrl)
If cntrl.name.startswith("btn") Then
msgbox(cntrl.name)
End If
End Sub
You can make this even more complex by adding parameters for stuff like controlling recursion and such. Keep in mind that if you want to do any control type-specific stuff with it (ie. anything that is in the control that is not inherited from the Control class), you need to CType that object as the appropriate control. So, if .name was only in the Button class, and did not exist in the Control class, I would have to do the following for this to work:
msgbox(ctype(cntrl, Button).name)
My own personal version of it looks more like this:
Private Sub clearcontrols(ByVal root As Control, ByVal ClearLists As Boolean, Optional ByVal ClearTabPages As Boolean = False)
For Each cntrl As Control In root.Controls
clearcontrols(cntrl, ClearLists, ClearTabPages)
If TypeOf cntrl Is TextBox Then
CType(cntrl, TextBox).Clear()
End If
If TypeOf cntrl Is DataGridView Then
CType(cntrl, DataGridView).Rows.Clear()
End If
If TypeOf cntrl Is ListBox And ClearLists = True Then
CType(cntrl, ListBox).Items.Clear()
End If
If TypeOf cntrl Is TabControl And ClearTabPages = True Then
For Each tp As TabPage In CType(cntrl, TabControl).TabPages
If DynTPList.Contains(tp.Name) Then
tp.Dispose()
End If
Next
End If
Next
End Sub

How to check if a checkbox is checked when iterating through form controls

I'm trying to set a registry key for every checkbox on a form, but in the following block of code, I'm receiving the error 'Checked' is not a member of 'System.Windows.Forms.Control'
Can somebody please help me find out why I'm getting this error?
' Create the data for the 'Servers' subkey
Dim SingleControl As Control ' Dummy to hold a form control
For Each SingleControl In Me.Controls
If TypeOf SingleControl Is CheckBox Then
Servers.SetValue(SingleControl.Name, SingleControl.Checked) ' Error happening here
End If
Next SingleControl
You should convert your control to a CheckBox before using the Checked property.
You use directly the Control variable and this type (Control) doesn't have a Checked property
Dim SingleControl As Control ' Dummy to hold a form control
For Each SingleControl In Me.Controls
Dim chk as CheckBox = TryCast(SingleControl, CheckBox)
If chk IsNot Nothing Then
Servers.SetValue(chk.Name, chk.Checked)
End If
Next
A better approach could be using Enumerable.OfType
Dim chk As CheckBox
For Each chk In Me.Controls.OfType(Of CheckBox)()
Servers.SetValue(chk.Name, chk.Checked)
Next
this removes the need to convert the generic control to a correct type and test if the conversion was successfully
Try this code,
Dim SingleControl As Control
For Each SingleControl In Me.Controls
If TypeOf SingleControl Is CheckBox Then
'control does not have property called checked, so we have to cast it into a check box.
Servers.SetValue(CType(SingleControl, CheckBox).Name, CType(SingleControl, CheckBox).Checked) End If
Next SingleControl
Checked is a property of the CheckBox class, not its Control parent.
You either have to downcast the Control into a Checkbox in order to access the property Checked or you have to store your checkboxes as a CheckBox collection not a Control collection.
Try this:
For Each SingleControl As Control In Me.Controls
If TypeOf SingleControl Is CheckBox Then
Dim auxChk As CheckBox = CType(SingleControl, CheckBox)
Servers.SetValue(auxChk.Name, auxChk.Checked)
End If
Next SingleControl
Use my extension method to get all controls on the form, including controls inside other containers in the form i.e. panels, groupboxes, etc.
<Extension()> _
Public Function ChildControls(Of T As Control)(ByVal parent As Control) As List(Of T)
Dim result As New ArrayList()
For Each ctrl As Control In parent.Controls
If TypeOf ctrl Is T Then result.Add(ctrl)
result.AddRange(ChildControls(Of T)(ctrl))
Next
Return result.ToArray().Select(Of T)(Function(arg1) CType(arg1, T)).ToList()
End Function
Usage:
Me.ChildControls(Of CheckBox). _
ForEach( _
Sub(chk As CheckBox)
Servers.SetValue(chk.Name, chk.Checked)
End Sub)