Home > Blockchain >  KeyPress handler being applied to wrong control
KeyPress handler being applied to wrong control

Time:10-22

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.

  • Related