Search code examples
c#winformsdatagridviewdatagridviewcolumn

How to tab between cells in one column of a DataGridView


Today I ran into the following requirements of a DataGridView:

Imagine You have a DataGridView with multiple columns that have only informational (non editable) values. You may want to only tab between the editable columns. In my requirements I had a DataGridView with 3 non editable text columns a one editable DataGridViewComboBoxColumn. It was required to tab only between the DataGridViewComboBoxCells.


Solution

  • I came up with the following solution I'd like to share.

    /// <summary>
    /// Overrided method to tab only in a DataGridComboBox column.
    /// </summary>
    /// <param name="msg">A Message, passed by reference, that represents the window message to process.</param>
    /// <param name="keyData">One of the Keys values that represents the key to process.</param>
    /// <returns>True if the character was processed by the control. Otherwise, False.</returns>
    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        bool TabIsPressed = ((keyData == Keys.Tab) || (keyData == (Keys.Shift | Keys.Tab))); // Checks if Tab or Shift/Tab is pressed.
        Control CurrentControl = this.ActiveControl; // Gets the currently focused control.
        bool IsDataGridViewControl = (CurrentControl.GetType() == typeof(DataGridView)); // Checks if the currently focused control is a DataGridView control.
        bool IsComboBoxEditingControl = (CurrentControl.GetType() == typeof(DataGridViewComboBoxEditingControl)); // Checks if the currently focused control is a DataGridViewComboBoxEditingControl control.
        bool DataGridViewIsValid = (IsDataGridViewControl && dataGridViewDMSSettings.Rows.Count > -1); // Checks if a DataGridView control is focused is not empty.
        if
        (
            TabIsPressed // If a Tab key is pressed.
            && (
                DataGridViewIsValid // And a DataGridView is focused and valid.
                || IsComboBoxEditingControl // Or a DataGridViewComboBoxEditingControl is focused.
            )
        )
        {
            int CurrentRowIndex = dataGridViewDMSSettings.CurrentCell.RowIndex; // Gets the current rows index.
            int LastRowIndex = dataGridViewDMSSettings.RowCount - 1; // Gets the last rows index.
            if (IsComboBoxEditingControl) // If a DataGridViewComboBoxEditingControl is focused.
            {
                ((DataGridViewComboBoxEditingControl)CurrentControl).EditingControlDataGridView.EndEdit(); // Ends the editing mode for the control.
            }
            int NextRowIndex = CurrentRowIndex; // Creates a variable for the next rows index.
            switch (keyData) // Switches through the pressed keys.
            {
                case (Keys.Tab): // If the Tab key is pressed.
                    if (IsComboBoxEditingControl) // If a DataGridViewComboBoxEditingControl is focused.
                    {
                        NextRowIndex++; // Sets the next row index to the next row.
                        if (NextRowIndex > LastRowIndex) // If next row index is greater than the last row index.
                        {
                            NextRowIndex = 0; // Focuses the first row. (Alternatively call "base.ProcessCmdKey(ref msg, keyData)" to exit the DataGridView)
                        }
                    }
                    break;
                case (Keys.Shift | Keys.Tab): // If Shift and Tab key is pressed.
                    NextRowIndex--; // Sets the next row index to the previous row.
                    if (NextRowIndex < 0) // If previous row index is smaller than the first row index.
                    {
                        NextRowIndex = LastRowIndex; // Focuses the last row. (Alternatively call "base.ProcessCmdKey(ref msg, keyData)" to exit the DataGridView)
                    }
                    break;
            }
            dataGridViewDMSSettings.CurrentCell = dataGridViewDMSSettings.Rows[NextRowIndex].Cells["DMSIndex"]; // Sets the focus on the next DataGridViewComboBox.
            this.Refresh(); // Rerenders the current form.
            return true; // Returns true to the caller.
        }
        else // If another key (Not a Tab key) is pressed or the currently focused control is not a DataGridView or DataGridViewComboBoxEditing control.
        {
            return base.ProcessCmdKey(ref msg, keyData); // Performs the standard ProcessCmdKey method.
        }
    }
    

    The only minor problem with this solution is that the exited DataGridComboBoxCell visualy renders a little bit slow (You can see it turning white for a brief second) I already have minimized the effect with:

    this.Refresh();
    

    But it's not gone. P.S. Enabling the forms doubble buffer didn't change this effect.

    Maybe someone can improve my method to fix this last problem.