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.
Related
Some of my codes are from the answers I read here in stack-overflow, one difficulty I am facing is how to check empty controls inside Tab-control with 3 pages? I have created a function that validate if the input is empty:
VB.Net Function:
Public Shared Function ValidateInput(parent As Control)
Dim ctl As Control = Nothing
For Each ctl In parent.Controls
If TypeOf ctl Is TextBox And ctl.Text.Length = 0 Or TypeOf ctl Is ComboBox And ctl.Text.Length = 0 Then
Return ctl.Name
Exit For
'--------------------------------------------------------------------
'(1.) I want this statement to check if the control is type of Tabcontrol ang check for empty input. (I have 3 pages)
ElseIf TypeOf ctl Is TabControl Then
For Each tp As TabPage In frmClientInfo.TabControl1.TabPages
For Each ctr As Control In tp.Controls
If TypeOf ctr Is TextBox OrElse TypeOf ctr Is ComboBox Then
ctl.Name = ctr.Name
Return ctl.Name
Exit For
End If
Next
Next
'--------------------------------------------------------------------
Else
ctl = Nothing
End If
Next
Return ctl
End Function
Usage:
Dim x = Functions.ValidateInput(Me)
Dim ctrlinput As Object = Me.Controls.Find(x, True).FirstOrDefault()
If TypeOf ctrlinput Is TextBox Then
ctrlinput.focus()
ElseIf TypeOf ctrlinput Is ComboBox Then
ctrlinput.DroppedDown = True
ElseIf TypeOf ctrlinput Is TabControl Then 'If control is Tab-control
Dim y = Functions.ValidateInput(Me)
Dim xinput As Object = TabControl1.Find(y, True).FirstOrDefault()
If TypeOf xinput Is TextBox Then
xinput.focus()
ElseIf TypeOf xinput Is ComboBox Then
xinput.DroppedDown = True
End If
Else
MessageBox.Show("saveeee")
Call SaveTransaction()
End If
My condition that deals with the controls outside the tab-control works. But I cant get the controls inside the tab-control pages. I want to focus the control when the function detects an empty control inside the tab-control. Let say the empty control was detected on page 3 the system will automatically jump from page 1 to page 3 and focus the empty control. Any solution? Thanks.
A problem here is that a tab can have another container inside it, just like your tabcontainer contains... tabs. You can bypass this by going recursive, but then your function will return everything it finds, wherever it finds it.
Here's a recursive function which will return you a list of every textbox or combobox without text it finds where you call it:
Private Function GetEmptyControls(current As Control) As List(Of Control)
Dim list As New List(Of Control)
For Each ctrl As Control In current.Controls
If TypeOf ctrl Is ContainerControl Then
'if this control is a container, add every control this function might find inside it to your list
'this is what makes this function recursive
list.AddRange(GetEmptyControls(ctrl))
Else
'add this control if it's a textbox or combobox and there's no text
If ctrl.Text = "" AndAlso (TypeOf ctrl Is TextBox OrElse TypeOf ctrl Is ComboBox) Then
list.Add(ctrl)
End If
End If
Next
Return list
End Function
You can call it on your form or on a container inside your form, or adapt it to suit your needs. Have fun!
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)
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)()
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
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)