none
Capture Tab key in datagridview

    Question

  • First thing I tried was using the keypress events, unfortunately these don't work well with the tab - I guess something going on behind the scenes with this particular key.  At any rate I am now trying to roll my own custom control which will inherit the dgv. 

    My objective that whenever the tab key is pressed the currentcell will be set to the next cell on the dgv which is _not_ readonly - if at the end of a row then jump down to next row.
    To test out my custom control I set up a dgv with 3 columns, the 3rd is readonly - When I hit this tab to this cell I have hard-coded set current cell as row 1 - column 1

      What is happening is it's jumping to column 2 - almost like the tab is being processed twice

    I tried placing logic in the event for the control as well as directly in the class itself (see below) with the same results

    Public Class dgvTab 
        Inherits DataGridView 
     
        Const WM_KEYDOWN As Integer = &H100 
        Const WM_SYSKEYDOWN As Integer = &H104 
     
        Public Event TabKeyDown(ByVal CtrlPressed As Boolean, _ 
        ByVal ShiftPressed As Boolean
     
        Protected Overrides Function ProcessCmdKey(ByRef msg As Message, _ 
        ByVal keyData As Keys) As Boolean 
     
            ' If a key or system key is pressed... 
            If ((msg.Msg = WM_KEYDOWN) Or (msg.Msg = WM_SYSKEYDOWN)) Then 
     
                ' If the tab key is pressed... 
                If keyData = Keys.Tab Then 
     
                    ' The user pressed the tab key (without holding ctrl, 
                    ' shift, or alt) 
                    'RaiseEvent TabKeyDown((keyData And Keys.Control) = _ 
                    'Keys.Control, (keyData And Keys.Shift) = Keys.Shift) 
     
     
                    Dim cell As DataGridViewCell = Me.CurrentCell 
                    If Me.Item(cell.ColumnIndex + 1, cell.RowIndex).ReadOnly = True Then 
            
                        Me.CurrentCell = Me.Rows(1).Cells(1) 
                    End If 
     
                End If 
     
            End If 
     
            ' Let base class handle the key press 
            Return MyBase.ProcessCmdKey(msg, keyData) 
     'tried commented this out too still same results



        End Function 
     
    End Class 

    • Edited by jriggs Friday, November 14, 2008 3:32 PM typo
    Friday, November 14, 2008 3:30 PM

Answers

  • Yes you are right, grid does not know about tab keys,

    what about leaving those function intact with your overridden datagridview and raise event of datagrid when tab is pressed




    Public Class Mydatagrid 
        Inherits DataGridView 
     
        Protected Overrides Function ProcessDialogKey(ByVal keyData As Keys) As Boolean 
            Dim key As Keys = keyData And Keys.KeyCode 
     
            If key = Keys.Tab Then 
                MyBase.OnKeyDown(New KeyEventArgs(keyData)) 
                Return True 
            Else 
                Return MyBase.ProcessDialogKey(keyData) 
            End If 
        End Function 
     
     
    End Class 




    Arjun Paudel
    • Edited by Arjun Paudel Friday, November 14, 2008 5:52 PM
    • Marked as answer by jriggs Friday, November 14, 2008 6:19 PM
    Friday, November 14, 2008 5:46 PM

All replies

  • Does not this kind of thing work? Edit: No Hardcoding, let code handle to find next writable cell

    Private Sub DataGridView1_KeyDown(ByVal sender As System.ObjectByVal e As System.Windows.Forms.KeyEventArgs) Handles DataGridView1.KeyDown 
            If e.KeyCode = Keys.Tab Then 
                Dim ri As Integer = DataGridView1.CurrentCell.RowIndex 
                Dim ci As Integer = DataGridView1.CurrentCell.ColumnIndex 
                e.SuppressKeyPress = True 
                FindNextCell(ri, ci + 1)  'checking from Next 
            End If 
        End Sub 
        Sub FindNextCell(ByVal rowindex As IntegerByVal columnindex As Integer
            Dim found As Boolean = False 
     
            While DataGridView1.RowCount > rowindex 
                While DataGridView1.Columns.Count > columnindex 
                    If Not (DataGridView1.Rows(rowindex).Cells(columnindex)).ReadOnly Then 
                        DataGridView1.CurrentCell = DataGridView1.Rows(rowindex).Cells(columnindex) 
                        Exit Sub 
                    Else 
                        columnindex += 1 
                    End If 
                End While 
                rowindex += 1 
                columnindex = 0 
            End While 
     
        End Sub 


    Arjun Paudel
    Friday, November 14, 2008 4:10 PM
  • Hi Arjun,

    Yes, It works, but not quite as you might expect.  The big problem is that if the cell is in edit mode - the tab is handled differently, or ignored all together I can't really tell.  If you have a test case set-up : try entering some text on the last cell before a read-only cell and hit {TAB} and see how it doesn't fire the key down event.

    Thanks!
    Friday, November 14, 2008 4:36 PM
  • ok, so you want a new row in that case?
    edit What you want in the case of last tab and no cell left?

    Arjun Paudel
    Friday, November 14, 2008 4:37 PM
  • No, the dgv is already populated with all the rows I will need.  I want to skip down to the next row, next cell which is not readonly.  Guess I forgot to mention that part - Some of the cells will already have data in them -  I just need the user to fill in the 'blanks' as long as they are not readonly.  Hitting tab should skip ahead to the next available cell.  Does that make sense?

    Doesn't really matter what happens if we're at the last cell, because the user will either save at this point or maybe edit some values.  It would probably be ideal if at the last available cell and tab is hit - move on to the next control.  If this isn't feasible then go back to the beginning of the dgv.
    • Edited by jriggs Friday, November 14, 2008 4:59 PM
    Friday, November 14, 2008 4:42 PM
  • Yes you are right, grid does not know about tab keys,

    what about leaving those function intact with your overridden datagridview and raise event of datagrid when tab is pressed




    Public Class Mydatagrid 
        Inherits DataGridView 
     
        Protected Overrides Function ProcessDialogKey(ByVal keyData As Keys) As Boolean 
            Dim key As Keys = keyData And Keys.KeyCode 
     
            If key = Keys.Tab Then 
                MyBase.OnKeyDown(New KeyEventArgs(keyData)) 
                Return True 
            Else 
                Return MyBase.ProcessDialogKey(keyData) 
            End If 
        End Function 
     
     
    End Class 




    Arjun Paudel
    • Edited by Arjun Paudel Friday, November 14, 2008 5:52 PM
    • Marked as answer by jriggs Friday, November 14, 2008 6:19 PM
    Friday, November 14, 2008 5:46 PM
  • Just leave my code as it is and override your datagridview like the code above
    Arjun Paudel
    Friday, November 14, 2008 5:53 PM
  • This seems to work -will definitely need some testing before production, but thanks all the same.

    One last thing, do you have any thoughts as to the best way set this up so that the enter key will have the same effect as pressing tab with this code?

    This seems to work, but not sure:

    Private Sub DataGridView1_KeyDown(ByVal sender As System.ObjectByVal e As System.Windows.Forms.KeyEventArgs) Handles DataGridView1.KeyDown 
            If e.KeyCode = Keys.Tab Or e.KeyCode = Keys.Enter Then 
                Dim ri As Integer = DataGridView1.CurrentCell.RowIndex 
                Dim ci As Integer = DataGridView1.CurrentCell.ColumnIndex 
                e.SuppressKeyPress = True 
                FindNextCell(ri, ci + 1)  'checking from Next 
            End If 
        End Sub 

    • Edited by jriggs Friday, November 14, 2008 6:26 PM
    Friday, November 14, 2008 6:21 PM
  • it might not, add this also in overridden method

    Dim key As Keys = keyData And Keys.KeyCode

            If key = Keys.Tab Or key = Keys.Enter Then

                MyBase.OnKeyDown(New KeyEventArgs(keyData))
                Return True
            Else
                Return MyBase.ProcessDialogKey(keyData)
            End If

    Arjun Paudel
    Friday, November 14, 2008 6:29 PM