Datagridview Control modifies another - vb.net

I'm trying to add values to my second combobox based on the choice of my first combobox in my datagridview (which contains textbox columns as well).
Here's my code:
Private Sub DataGridView1_EditingControlShowing(sender As Object, e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
Try
'Sub Recurrence combobox is selected
If DataGridView1.CurrentCell.ColumnIndex = 4 Then
'Verify what is chosen in the Recurrence textbox
Dim rowIndex = DataGridView1.CurrentCell.RowIndex
Dim FirstComboboxValue = DataGridView1(3, rowIndex).Value
If Not IsNothing(FirstComboboxValue) Then
Dim cmb As ComboBox = TryCast(e.Control, ComboBox)
RemoveHandler cmb.SelectedIndexChanged, New EventHandler(AddressOf ComboBox_SelectedIndexChanged)
AddHandler cmb.SelectedIndexChanged, New EventHandler(AddressOf ComboBox_SelectedIndexChanged)
End If
End If
Catch ex As Exception
MsgBox(ex.ToString())
End Try
End Sub
My handlers fire the trigger towards the ComboBox_SelectedIndexChanged event:
Private Sub ComboBox_SelectedIndexChanged(ByVal sender As Object, ByVal e As EventArgs)
If DataGridView1.CurrentCell.ColumnIndex = 4 Then
Dim comboBox As ComboBox = CType(sender, ComboBox)
Dim cbCell As DataGridViewComboBoxCell = DirectCast(DataGridView1.Rows(DataGridView1.CurrentCell.RowIndex).Cells(4), DataGridViewComboBoxCell)
Dim rowIndex = DataGridView1.CurrentCell.RowIndex
Dim FirstComboboxValue = DataGridView1(3, rowIndex).Value
'Clear items so they don't keep adding on each other
cbCell.Items.Clear()
Select Case FirstComboboxValue
Case "Choice1"
cbCell.Items.Add("Item1")
cbCell.Items.Add("Item2")
Case "Choice2"
cbCell.Items.Add("Item3")
cbCell.Items.Add("Item4")
Case "Choice3"
cbCell.Items.Add("Item5")
Case "Choice4"
cbCell.Items.Add("Item6")
cbCell.Items.Add("Item7")
End Select
'Remove event handler to prevent memory leak
RemoveHandler comboBox.SelectedIndexChanged, AddressOf ComboBox_SelectedIndexChanged
End If
End Sub
For some reason, when I use a break-point and step through the program, the items get added in the second combobox only on the 2nd time that I try ty open the "item" combobox.
If I try to run the program without debugging, nothing is displayed. Any idea what I'm doing wrong?

If you are using SelectedIndexChanged in your ComboBox may be you encounter a problem with events being fired even if you did not click the ComboBox.
This happens when you open and close your Windows Form.
A better one for this by using the SelectionChangeCommitted event.
SelectionChangeCommitted event occurs when the selected item has changed and that change is displayed in the ComboBox.
By using SelectionChangeCommitted event you do not need to declare RemoveHandler and AddHandler statement.
Another possibility:
Try it with Handles cmb.SelectedIndexChanged
There are several ways to declare events in VB.NET. The usual one is using the handles keyword.
Private Sub ComboBox_SelectedIndexChanged(ByVal sender As Object,
ByVal e As EventArgs) Handles cmb.SelectedIndexChanged

Related

VB.net - How to send multiple senders to event handler

I have MonthlyCalendar created and shown once a text box is clicked "e.g. Date of Birth". I do have multiple dates text boxes in my Form and I would like to send the actual text box as Object "clicked" along with the MonthlyCalendar as Sender as well , 3 arguments.
Private Sub TextBox1_Click(sender As Object, e As EventArgs) Handles txtDOB.Click
Dim mc = New MonthCalendar()
AddHandler mc.DateSelected, AddressOf DateSelected
Me.Controls.Add(mc)
mc.BringToFront()
mc.Show()
End Sub
Private Sub DateSelected(sender As Object, e As DateRangeEventArgs) Handles MonthCalendar1.DateSelected
txtDOB.Text = DirectCast(sender, MonthCalendar).SelectionRange.Start.Date.ToString("yyyyMMdd")
End Sub
I would like the handler to be like this:
1st argument the actual textbox clicked 2nd argument the Monthly Calendar 3rd argument the DateRangeEventArgs
AddHandler mc.DateSelected, AddressOf DateSelected(sender 'Textbox as Object, sender 'MontlyCalendar As Object, e As DateRangeEventArgs)
Appreciate guidance and a better way of doing this
Here's the way I'd do this. I'd avoid all of that casting you're doing.
Private Sub AnyTextBox_Click(sender As Object, e As EventArgs) Handles txtDOB.Click, txt2.Click, txt3.Click
Dim tb = CType(sender, TextBox)
Dim mc = New MonthCalendar()
AddHandler mc.DateSelected, Sub (s2, e2) mc.SelectionRange.Start.Date.ToString("yyyyMMdd")
Me.Controls.Add(mc)
mc.BringToFront()
mc.Show()
End Sub
This also avoids the separate DateSelected handler. Much cleaner and even more strongly typed.
The next step in making the code cleaner is to handle the closing down of your MonthCalendar control. Your current code and my code above doesn't do it.
Here's how:
Private Sub AnyTextBox_Click(sender As Object, e As EventArgs) Handles txtDOB.Click, txt2.Click, txt3.Click
Dim tb = CType(sender, TextBox)
Dim mc = New MonthCalendar()
Dim handler As EventHandler(Of DateRangeEventArgs) = _
Sub(s2, e2)
mc.SelectionRange.Start.Date.ToString("yyyyMMdd")
Me.Controls.Remove(mc)
RemoveHandler mc.DateSelected, handler
End Sub
AddHandler mc.DateSelected, handler
Me.Controls.Add(mc)
mc.BringToFront()
mc.Show()
End Sub
You don't send anything to an event handler. The object itself raises the event in response to something done to it and it sends the arguments to the event handler. You can't change the signature and you can't choose what arguments it receives.
Probably your best bet would be to, when you create the MonthCalendar, assign the corresponding TextBox to its Tag property.
Dim mc = New MonthCalendar With {.Tag = sender}
Tag is a general-purpose data property of type Object, so there's no need to cast sender at this point. You know that the actual object is a TextBox, because that's all that will raise that event.
In the event handler, you can then get the MonthCalendar from the sender and the TextBox from its Tag.
Dim mc = DirectCast(sender, MonthCalendar)
Dim tb = DirectCast(mc.Tag, TextBox)

Clear focused textbox in multiple textboxes in vb.net using delete key

I want a good example of clearing focused textbox among multiple textboxes using delete key. Please help me providing vb.net code.
The public sub is as such :
Public Sub EmptyTxt(ByVal Frm As Form)
Dim Ctl As Control
For Each Ctl In Frm.Controls
'If TypeOf Ctl Is TextBox Then Ctl.Text = ""
Next
End Sub
Then I call this sub from delete keydown event. But it clears all textboxes rather than clearing the focused one.
There's an easier way to do that.
Simply assign all the textbox keyDown events to the same handler. In that handler, cast sender to a textbox and clear it.
Private Sub Form1_Load (sender As Object, e As EventArgs) Handles MyBase.Load
For Each tb As TextBox In Controls.OfType(Of TextBox)
AddHandler tb.KeyDown, AddressOf TbKeyDown
Next
End Sub
Private Sub TbKeyDown (sender As Object, e As KeyEventArgs)
Dim tb = CType(sender, TextBox)
If e.KeyCode = Keys.Delete Then
tb.Clear()
End If
End Sub

How to AddHandler to Multiple Items and Properly Reference Said Handler

I'm writing a program wherein I need to create GotFocus and LostFocus event handlers for every text box.
What I have now creates an error whenever any text box in the form is clicked on:
Private Sub lblTotalWeight_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each cnt In Me.Controls.OfType(Of TextBox)
AddHandler cnt.GotFocus, AddressOf txtBox_GotFocus
Next
End Sub
Private Sub txtBox_GotFocus(sender As System.Object, e As System.EventArgs)
Dim textBox As TextBox = Me.Controls.OfType(Of TextBox)
textBox.ForeColor = Color.White
textBox.BackColor = Color.LightGray
End Sub
I'm getting an error with the Dim textBox as TextBox = Me.Controls.OfType(Of TextBox), and I have no idea why or how to fix it.
There will be another Private Sub for a lost focus event that will be nearly identical, if it helps.
Any help would be appreciated
Me.Controls.OfType(Of TextBox) returns a list of controls that are textboxes. It works great in your AddHandler call, but in your event handler you need to use sender as Textbox instead.
Dim textBox As TextBox = DirectCast(sender, Textbox)
This code:
Me.Controls.OfType(Of TextBox)
returns an IEnumerable(Of TextBox). It's not a single Textbox so you can't assign it to a TextBox variable. You need to access the TextBox that raised the event:
Dim textBox As TextBox = DirectCast(sender, TextBox)
In an event handler, the sender parameter is always the object that raised the event.
Me.Controls.OfType(Of TextBox) with replace CType(sender, TextBox) for event txtBox_GotFocus

Detect if a combobox is clicked without using SelectedIndexChange

I would like to ask if it is possible for VB.net applications to detect if a combobox has been changed without using SelectedIndexChange.
For Example my application has 4 comboboxes and this comboboxes was dynamically created from the user configuration. (the tool autocreates the comboboxes when the application was launched). Then once the application was launched, I want to run a SUB everytime those comboboxes was changed. Badly needed help. TIA
If you are creating controls dynamically, you should add the event handlers dynamically as well. There is nothing wrong with using the SelectedIndexChanged event.
You can test this by making a new project and pasting this code inside Public Class Form1.
Private myComboBox1 As ComboBox
Private myComboBox2 As ComboBox
Private Shared selectedIndexChanged As EventHandler =
Sub(sender As Object, e As EventArgs)
Dim myComboBox = DirectCast(sender, ComboBox)
' alert the user as to what was selected
MessageBox.Show(String.Format("{0} value: {1}, index: {2}",
myComboBox.Name, myComboBox.Text, myComboBox.SelectedIndex))
' you can do something different on each one by name in a case statement
Select Case myComboBox.Name
Case "myComboBox1"
' do something for 1
Case "myComboBox2"
' do something for 2
End Select
End Sub
Private Sub addHandlers()
AddHandler myComboBox1.SelectedIndexChanged, selectedIndexChanged
AddHandler myComboBox2.SelectedIndexChanged, selectedIndexChanged
End Sub
Private Sub removeHandlers()
RemoveHandler myComboBox1.SelectedIndexChanged, selectedIndexChanged
RemoveHandler myComboBox2.SelectedIndexChanged, selectedIndexChanged
End Sub
Form eventhandlers
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
' dynamically generate combo boxes
myComboBox1 = New ComboBox() With {.Name = "myComboBox1",
.Left = 30,
.Top = 30}
myComboBox2 = New ComboBox() With {.Name = "myComboBox2",
.Left = 30,
.Top = 60}
' add some items
myComboBox1.Items.AddRange({1, 2, 3})
myComboBox2.Items.AddRange({"four", "five", "six"})
' add the combo boxes to the form
Me.Controls.Add(myComboBox1)
Me.Controls.Add(myComboBox2)
' add event handlers
addHandlers()
End Sub
Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
removeHandlers()
End Sub

Detecting the ID of checkbox that was checked

I am making a windows form application. I want to detect which checkbox is being selected by the user. One way to check this would be to loop through all the controls everytime a checkbox checkchanged event is fired. But I do not want to do that because multiple checkboxes could be checked. I want to get the ID of checkbox as it is selected or on a mousedown event. How can I do so?
You can add the event handlers for the checkboxes you want at runtime. Use the Where clause to filter by name if applicable. This code does it in form_load.
Inside the handler, you can cast sender to a local variable which represents the checkbox which was checked, if you want.
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
For Each checkBox In Me.Controls.OfType(Of CheckBox)().Where(Function(cb As CheckBox) cb.Name.Contains("CheckBox"))
AddHandler checkBox.CheckedChanged, AddressOf checkboxCheckedChanged
Next
End Sub
Private Sub checkboxCheckedChanged(sender As Object, e As EventArgs)
Dim myCheckbox As CheckBox = DirectCast(sender, CheckBox)
Dim c As Boolean = myCheckbox.Checked
Dim n As String = myCheckbox.Name
' etc.
End Sub
EDIT
As Neolisk pointed out, this doesn't account for nested controls, i.e. controls inside containers on the form. This extension method returns all those controls:
<Extension()> _
Public Function ChildControls(Of T As Control)(ByVal parent As Control) As List(Of T)
Dim result As New List(Of Control)
For Each ctrl As Control In parent.Controls
If TypeOf ctrl Is T Then result.Add(ctrl)
result.AddRange(ctrl.ChildControls(Of T)())
Next
Return result.ToArray().Select(Of T)(Function(arg1) CType(arg1, T)).ToList()
End Function
And make this the loop in form_load instead:
For Each checkBox In Me.ChildControls(Of CheckBox).Where(Function(cb As CheckBox) cb.Name.Contains("CheckBox"))
AddHandler checkBox.CheckedChanged, AddressOf checkboxCheckedChanged
Next
Use sender argument in the event handler.
Private Sub CheckBox1_CheckedChanged(sender As Object, e As EventArgs) _
Handles CheckBox1.CheckedChanged
'DirectCast(sender, CheckBox).Name ?
End Sub