How to check empty controls within tabcontrol pages? - vb.net

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!

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

Clear values of controls dynamically

I would like to know if there is a algoritm for clearing all the values dynamically of a Panel without the need to do it manually. And recursive if it 's possible. I have this idea in my mind but it doesn 't compile obviously.
Private Sub ClearAll(ByRef panel As Control)
For Each objControl As Control In panel.Controls
If (TypeOf objControl Is Panel) Then
ClearAll(objControl)
End If
If (TypeOf objControl Is CheckBox Or TypeOf objControl Is RadioButton) Then
objControl.Checked = False
End If
If (TypeOf objControl Is TextBox) Then
objControl.Clear()
End If
Next
End Sub
You're close!
when you recurse, you don't have to check the type, all Control objects have .controls and it doesn't hurt to check them (speed gain isn't worth code bloat)
In VB use CType.
Then you're done! (for clarity: rename the variable 'panel' to 'ctl' or the like; it now won't always be a panel, could be a groupbox)
Private Sub ClearAll(ByRef panel As Control)
For Each objControl As Control In panel.Controls
ClearAll(objControl)
If TypeOf objControl Is Checkbox then
ctype(objControl,Checkbox).Checked = False
End If
If TypeOf objControl Is RadioButton then
ctype(objControl,RadioButton).Checked = False
End If
If TypeOf objControl Is TextBox Then
ctype(objControl,textbox).Clear()
End If
' etc ... e.g., if Listbox, ctype(...).SelectedIndex=-1, then if Dropdown...
Next
End Sub
Ideally, winforms controls would implement simple interfaces that allowed controlling stuff common to them.
all controls - implement ISetToDefault with .Reset method which clears textbox, checkbox, listbox, etc.
list/combo: IHaveListItems with all properties of .List on combo/listbox to allow operating lists as single type (in wf, the two are frustratingly different types!)
text/combo: IHaveUserEnteredText with a .Text property
all controls - IHaveValue with a .Value property as string (would be .ToString of Boolean or etc).
groupbox/check/option - IHaveCaption with .Caption, you get the idea.
But we don't get to rewrite winforms now do we...
You can try this so you can add many region for exemple if you have TabControl or GroupeControle :
Public Shared Sub ClearChamps(ByVal myObject As Control)
'#Region "Controls XtraTabControl"
'#End Region
'#Region "Controls XtraTabPage"
' #Region "Controls GroupControl"
'****les controles de la GroupControl
If TypeOf myObject Is Control Then
Dim cont2 As Control = DirectCast(myObject, Control)
For Each myObject1 As [Object] In cont2.Controls
'If TypeOf myObject1 Is LookUpEdit Then
' '(myObject1 as LookUpEdit).ClosePopup() ;
' TryCast(myObject1, LookUpEdit).EditValue = Nothing
If TypeOf myObject1 Is TextBox Then
TryCast(myObject1, TextBox).Text = String.Empty
End If
If TypeOf myObject1 Is DataGridView Then
myObject1.Rows.Clear()
End If
If (TypeOf myObject1 Is CheckBox Or TypeOf myObject1 Is RadioButton) Then
myObject1.Checked = False
End If
Next
End If
' #End Region
'#Region "Groupbox"
'****les controles de la GroupControl
End Sub
you can use ClearChamps(ByVal myObject As Form) for target all controls in your form

how to compare control name to text value

I am trying to loop through all controls and assign a text_changed handler on all textboxes aside from two but the following code does not work it keeps getting inside the if and assigning the handler to these textboxes. Using IsNot in replacement of <> does not work either
Dim textboxes = Me.GetAllControls(Me).OfType(Of TextBox)().Where(Function(x) x.Enabled = True).ToList()
For Each item As TextBox In textboxes
If item.Name <> "txtSearchName" AndAlso item.Name <> "txtSearchEmpNum" Then
Dim TextBox As TextBox = DirectCast(item, TextBox)
AddHandler TextBox.TextChanged, AddressOf TextBoxTextChanged_Handler
MsgBox(item.Name & " Has textchanged event handler")
End If
Next
Dim textboxes = Me.GetAllControls(Me).OfType(Of TextBox)().Where(Function(x) x.Enabled = True).ToList()
Private Function GetAllControls(control As Control) As IEnumerable(Of Control)
'This function gets all controls of specified type
Dim controls = control.Controls.Cast(Of Control)()
Return controls.SelectMany(Function(ctrl) GetAllControls(ctrl)).Concat(controls)
End Function

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