Created On:  30 January 2013

Problem:

Customer has a Windows Forms application that uses a dataGridView control. They would like to use the key to behave like the key but the key currently moves down the grid rather than across. How can the Enter key be used as a tab key in order to navigate through the columns of the dataGridView?

Resolution:

The best way to get the Enter key to behave like the Tab key is to subclass the dataGridView class and override the methods ProcessDialogKey and ProcessDataGridViewKey so that the Enter key can be trapped and processed as a Tab.

In the following example there is a second class near the bottom called CustomDataGridView which inherits from dataGridView and then overrides the two methods.

When this is compiled a new control called CustomDataGridView will appear in the Windows Forms Toolbox and then it can be selected and pasted to the form just like the standard dataGridView only the Enter key will behave like a Tab key.

This example also does validation using the cellValidating event:

class-id testdatagrid.Form1 is partial
inherits type System.Windows.Forms.Form.
working-storage section.
01 my-Table.
     03 my-Items occurs 10.
          05 Table-Code pic x(3).
          05 Table-Desc pic x(20).
          05 Table-Value pic S9(7)V99.
01 num-items binary-long.
01 sub-1 binary-long.
01 table-value-decimal decimal.

method-id NEW.
procedure division.
    invoke self::InitializeComponent
    invoke self::loadTable
    goback.
end method.

method-id dataGridView1_CellValidating final private.
01 rownum binary-long.
01 tempstring string.
procedure division using by value sender as object e as type System.Windows.Forms.DataGridViewCellValidatingEventArgs.
    if e::ColumnIndex = 0
       set rownum to e::RowIndex
       set tempstring to e::FormattedValue
       if tempstring::CompareTo("444") > 0
          set e::Cancel to true
          set datagridview1::Rows[rownum]::ErrorText to "The value must be <= 444"
       else
          invoke dataGridView1::CommitEdit(type DataGridViewDataErrorContexts::CurrentCellChange)
          set datagridview1::Rows[rownum]::ErrorText to ""
       end-if
    end-if
end method.

method-id loadTable public.
procedure division.
    move "111" to table-code(1)
    move "Table Item 1" to table-desc(1)
    move 1234.50 to table-value(1)
    move "222" to table-code(2)
    move "Table Item 2" to table-desc(2)
    move -9876.99 to table-value(2)
    move "333" to table-code(3)
    move "Table Item 3" to table-desc(3)
    move 1324.00 to table-value(3)
    move "444" to table-code(4)
    move "Table Item 4" to table-desc(4)
    move 1234.50 to table-value(4)
    move "555" to table-code(5)
    move "Table Item 5" to table-desc(5)
    move -123.75 to table-value(5)
    move 5 to num-items
    goback.
end method.

method-id Form1_Shown final private.
procedure division using by value sender as object e as type System.EventArgs.
    perform varying sub-1 from 1 by 1
         until sub-1 > num-items
         invoke dataGridView1::Rows::Add
         set dataGridView1::Rows[sub-1 - 1]::Cells["CodeNum"]::Value to table-code(sub-1)
         set dataGridView1::Rows[sub-1 - 1]::Cells["Description"]::Value to table-desc(sub-1)
         move table-value(sub-1) to table-value-decimal
         set dataGridView1::Rows[sub-1 - 1]::Cells["Amount"]::Value to table-value-decimal
    end-perform
    set dataGridView1::CurrentCell To dataGridView1[0, 0]
    invoke dataGridView1::Focus
    end method.

method-id btnExit_Click final private.
procedure division using by value sender as object e as type System.EventArgs.
    invoke self::Close
end method.

method-id btnSave_Click final private.
procedure division using by value sender as object e as type System.EventArgs.
    move 1 to sub-1
    perform varying myRow as type DataGridViewRow thru dataGridView1::Rows
        if myRow::Cells["CodeNum"]::Value = null
            exit perform
        end-if
        move myRow::Cells["CodeNum"]::Value to table-code(sub-1)
        move myRow::Cells["Description"]::Value to table-desc(sub-1)
        set table-value(sub-1) to type System.Convert::ToDecimal(myRow::Cells["Amount"]::Value)
        add 1 to sub-1
   end-perform
end method.
end class.

class-id CustomDataGridView inherits type DataGridView.
working-storage section.

method-id ProcessDialogKey override.
local-storage section.
01 keycode type Keys.
procedure division using by value keyData as type Keys
                             returning ret-value as condition-value.
*> Extract the key code from the key value.
    set keycode to keyData b-and type Keys::KeyCode
*> Handle the ENTER key as if it were a tab key.
    if keycode = type Keys::Enter
        set ret-value to self::ProcessTabKey(keyData)
    else
        set ret-value to super::ProcessDialogKey(keyData)
    end-if
    goback.
end method.

method-id ProcessDataGridViewKey override.
procedure division using by value e as type KeyEventArgs
                             returning ret-value as condition-value.
*> Handle the ENTER key as if it were a tab key.
    if e::KeyCode = type Keys::Enter
        set ret-value to self::ProcessTabKey(e::KeyData)
    else
        set ret-value to super::ProcessDataGridViewKey(e)
    end-if
    goback.
end method.
end class.