How does this work? - vb.net

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.

Related

How to set an event for MDI child forms without adding code to each form?

I would like to set the background color for a certain type of control on all child forms that open. I have an MdiParent form that is used to open the other forms within itself. I don't want to add code to each child form as this would be very extensive. This would be used as a theme feature for the application so I would like to have it automatically change the background colors based on logic in the main form. Is there something like a global event that could trigger for all Form.Load events?
So far I have created an event in the Parent form but it doesn't work for nested controls
Private Sub frmMain_MdiChildActivate(sender As Object, e As EventArgs) Handles Me.MdiChildActivate
Dim ParentControl As frmMain = sender
Dim ChildControl = ParentControl.ActiveControl
If ChildControl IsNot Nothing Then
For Each FormControl As Control In ChildControl.Controls
If FormControl.GetType = GetType(GroupBox) Then
RemoveHandler FormControl.Paint, AddressOf PaintBorderlessGroupbox
AddHandler FormControl.Paint, AddressOf PaintBorderlessGroupbox
End If
Next
End If
End Sub
I was able to accomplish this by using Form.MdiChildActivate and adding the event to the appropriate controls based on the Event and EventHandler.
Private Sub frmMain_MdiChildActivate(sender As Object, e As EventArgs) Handles Me.MdiChildActivate
Dim ParentForm As frmMain = sender
Dim ChildForm = ParentForm.ActiveMdiChild
Dim EventName = "Paint"
Dim EventHandlerName = "PaintBorderlessGroupBox"
If ChildForm IsNot Nothing Then
AddEventToControls(ChildForm, GetType(GroupBox), EventName, EventHandlerName)
End If
End Sub
Private Sub AddEventToControls(Control As Control, ControlType As Type, ControlEventName As String, ControlEventMethod As String)
For Each ChildControl In Control.Controls
If ChildControl.GetType = ControlType Then
If ChildControl.Controls.Count > 0 Then
AddEventToControls(ChildControl, ControlType, ControlEventName, ControlEventMethod)
End If
Dim EventMethod = Me.GetType().GetMethod(ControlEventMethod, BindingFlags.NonPublic Or BindingFlags.Instance)
Dim ControlEvent As EventInfo = ControlType.GetEvent(ControlEventName)
Dim del = [Delegate].CreateDelegate(ControlEvent.EventHandlerType, Me, EventMethod)
ControlEvent.RemoveEventHandler(ChildControl, del)
ControlEvent.AddEventHandler(ChildControl, del)
End If
Next
End Sub
The call to AddEventToControls() assigns the handler to the Control and any child controls that it would also apply to. In this case I am setting the Control.Paint event to paint a GroupBox a specific way. This may not be the cleanest method to accomplish this but I was able to create a "Global Event" for all child forms without ever touching the code on each form.
In your parent form, keep a collection of all Child Forms that have been activated. You can then traverse that collection and change the relevant control property for each one.
For Each ChildForm in MyCollection
ChildForm.TextBox.BackColor = Red
Next
Of course:
The ChildForm control has to be accessible by the parent form
The ChildForm has to still exist (i.e. not been closed in the mean
time)
You can't check for ChildForm closure because you are not adding any
code to the ChildForm to signal such an event.
You have to handle the exceptions when you try to change a form that
has been closed.
Much easier to include a method in your ChildForm design for ChangeColour(), whether that method is fired by event or direct call is your design decision. And to include a method to tell the parent form when a ChildForm dies so that it stops looking for it.

How to exclude some controls in a FOR EACH CONTROL Loop?

So I have a neat little function that I use to make sure only one checkbox can be checked inside a GroupBox.
This is what it looks like...
Private Sub ToggleCheckBoxOnEntry(sender As Object, e As EventArgs)
'This handles mutually exclusivity for the check boxes so that only one is ever allowed to be checked
Static CurrentlySelectedbox As CheckBox
If CType(sender, CheckBox).Checked Then
CurrentlySelectedbox = sender
End If
For Each cntrl As CheckBox In gbxReports.Controls
If cntrl.Checked AndAlso cntrl.Name <> CurrentlySelectedbox.Name Then
cntrl.Checked = False
End If
Next
End Sub
And for each checkBox_CheckChanged I include this line...
Private Sub chkReports_CheckedChanged(sender As Object, e As EventArgs)
ToggleCheckBoxOnEntry(sender, e)
End Sub
So I have this great looking groupbox with about 10 reports and it works great. The problem comes when I try to include a combobox for one of the extracts where I let the user select something from a drop down and use it as a parameter. I do not want to include it outside of the GroupBox (unless there's no way to fix my issue), however, if I include it inside of it I get an error..
Unable to cast object of type 'System.Windows.Forms.ComboBox' to type
'System.Windows.Forms.CheckBox'.
Is there a way for me to exclude some controls from the loop such as
For each cntrl as CheckBox in gbxReports.controls // except comboboxes/ or only checkboxes??
The only controls that I would potentially have in the group box are checkboxes and comboboxes.
Why don't you be specific to what you want to loop rather than the mix of Controls:
For Each chexkb As Checkbox In Controls.OfType(Of Checkbox)()
'do the loop just on the check boxes
Next

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.

How to notice any change on a form

I have a form with many different radiobuttons, checkboxes and textboxes. Depending on their values I start my calculations. The results are shown on the same form and panel. If any of my controls (checkboxes, ...) changes, I want to immediatly update the results without a need to press any update-button.
I could define a statsChanged-sub for every single control on the form but there are so many. Isn't there a way/event of the form starting whenever a control is changed? It should be something like controlOnFormChanged. How can I get a sub that starts whenever a any control on the form changed?
Thank you in advance!
You could wire up the events that correspond to the desired change manually to a specific event handler:
Public Class Form1
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
'Iterate through all controls and handle them according to their type
For Each c As Control In Me.Controls
If TypeOf (c) Is CheckBox Then
AddHandler CType(c, CheckBox).CheckedChanged, AddressOf SomethingChanged
ElseIf TypeOf (c) Is RadioButton Then
AddHandler CType(c, RadioButton).CheckedChanged, AddressOf SomethingChanged
ElseIf TypeOf (c) Is TextBox Then
AddHandler CType(c, TextBox).TextChanged, AddressOf SomethingChanged
ElseIf ......
......
End If
Next
End Sub
Private Sub SomethingChanged(sender As Object, e As EventArgs)
'Whatever it is you do
End Sub
End Class
Whenever one of the events on a control fires the sub SomethingChanged is called, allowing you to update your results.
Please be aware: If you have controls in subcontainers like Panels you need to modify this method and iteratively get all controls in all containers.
Here is, for example, a solution to this:
http://kon-phum.com/tutors/pascal/programming_cs_getcontrolsonform.html
Public Shared Function GetAllControls(ctrls As IList) As List(Of Control)
Dim RetCtrls As New List(Of Control)()
For Each ctl As Control In ctrls
RetCtrls.Add(ctl)
Dim SubCtrls As List(Of Control) = GetAllControls(ctl.Controls)
RetCtrls.AddRange(SubCtrls)
Next
Return RetCtrls
End Function

Why are controls missing from Me.Controls()

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.