I have a .KeyPress
event handler which is supposed to limit/control the keys which can be entered in a specific TextBox
(more precisely, any of the textboxes in a specific DataGridViewTextBoxColumn
)
Private Sub dgv_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgv.EditingControlShowing
If dgv.CurrentCell.ColumnIndex = myColumn.Index And Not e.Control Is Nothing Then
DirectCast(e.Control, TextBox).CharacterCasing = CharacterCasing.Upper
DirectCast(e.Control, TextBox).MaxLength = 10
AddHandler DirectCast(e.Control, TextBox).KeyPress, AddressOf controlKeyPress
End If
End Sub
Private Sub controlKeyPress(ByVal sender As System.Object, ByVal e As System.Windows.Forms.KeyPressEventArgs)
Dim charEnum As Integer = CUInt(Microsoft.VisualBasic.Asc(e.KeyChar))
Dim tb As TextBox = DirectCast(sender, TextBox)
Select Case charEnum
Case 8
' Always permit the keying of backspace (no suppression)
Case 42
' Permit the keying of asterisk (42) but only if it is the first character (otherwise, suppress the key press)
If Not tb.SelectionStart = 0 Then e.KeyChar = ""
Case 46
' Permit the keying of period (46) but only if it is not the first character and the first character is not an asterisk (otherwise, suppress the key press)
If tb.SelectionStart = 0 OrElse tb.Text.FirstOrDefault = "*" Then e.KeyChar = ""
Case 65 To 90, 97 To 122
' Permit the keying of upper-case alpha (65-90) and lower-case alpha (97-122) as long as the first character is not an asterisk (otherwise, suppress the key press)
If tb.Text.FirstOrDefault = "*" Then e.KeyChar = ""
Case Else
' All other characters, suppress the key press (set the KeyChar to nothing)
e.KeyChar = ""
End Select
End Sub
What's weird is, the same handler seems to be getting applied to other TextBox
controls in the DataGridView
, but in a different column (i.e. not in myColumn
) Which is strange because I have a specific condition in the EditingControlShowing
event that specifies that the handler should only be applied if the .ColumnIndex
of the control matches that of the column to which it should apply (i.e. If dgv.CurrentCell.ColumnIndex = myColumn.Index
) So I'm not sure why the same handler is being applied to a TextBox
that's not in myColumn
?
Also, it doesn't appear to be consistent - when I initially load the DGV, the other textboxes have no restrictions on them (as expected); when I go to edit a row, and the handler is applied to myColumn
(as expected), the same handler also seems to be applied immediately to any other textboxes in the same row (but in debugging, I can't seem to trap where this happens, I can only trap the application of the event handler to the correct control)
I'm not sure if I should have a RemoveHandler
call somewhere - and if so, where, because I can't find the point at which the handler is being applied erroneously in the first place?
I tried this but it doesn't seem to have any effect (again, while debugging, when I click in a TextBox
in myOtherColumn
, it does hit that line, but the restriction is still imposed anyway?)
Private Sub dgv_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles dgv.EditingControlShowing
If dgv.CurrentCell.ColumnIndex = myColumn.Index Then
DirectCast(e.Control, TextBox).CharacterCasing = CharacterCasing.Upper
DirectCast(e.Control, TextBox).MaxLength = 10
AddHandler DirectCast(e.Control, TextBox).KeyPress, AddressOf controlKeyPress
ElseIf dgv.CurrentCell.ColumnIndex = myOtherColumn.Index Then
RemoveHandler DirectCast(e.Control, TextBox).KeyPress, AddressOf controlKeyPress
End If
End Sub
All suggestions welcome!
CodePudding user response:
The DataGridView
control will reuse an editing control if it can to improve performance. You should keep a reference to the editing control from the EditingControlShowing
event handler and use a RemoveHandler
statement in the CellEndEdit
event handler.
Actually, you may not need to keep the reference. You may be able to use the EditingControl
property of the grid. Try that first.
EDIT:
I have just tested for myself and the EditingControl
property of the grid is Nothing
when the CellEndEdit
event is raised, so my second suggestion above is out. That means that you need to retain a reference to the editing control from the EditingControlShowing
event handler. If you're going to do that though, you may as well not use AddHandler
and RemoveHandler
. It's simpler to declare the field WithEvents
and then use a Handles
clause on the event handler, e.g.
Private WithEvents editingControl As TextBox
Private Sub DataGridView1_EditingControlShowing(sender As Object, e As DataGridViewEditingControlShowingEventArgs) Handles DataGridView1.EditingControlShowing
If DataGridView1.CurrentCell.ColumnIndex = 0 Then
editingControl = DirectCast(e.Control, TextBox)
End If
End Sub
Private Sub DataGridView1_CellEndEdit(sender As Object, e As DataGridViewCellEventArgs) Handles DataGridView1.CellEndEdit
editingControl = Nothing
End Sub
Private Sub EditingControl_KeyPress(sender As Object, e As KeyPressEventArgs) Handles editingControl.KeyPress
Console.WriteLine(e.KeyChar)
End Sub
That code will assign the editing control to the field if and only if the cell being edited is in the first column. Any control assigned to that field will have its events handled and the field is always reset when an editing session ends.