简体   繁体   中英

Bypass read only cells in DataGridView when pressing TAB key

任何人都可以向我展示一些代码,说明按下 TAB 键时如何绕过 DatagridView 中的只读单元格?

private void dataGridView1_CellEnter(object sender, DataGridViewCellEventArgs e)
{
   if (dataGridView1.CurrentRow.Cells[e.ColumnIndex].ReadOnly)
    {
        SendKeys.Send("{tab}");
    }
}

Overriding the SelectionChanged event is the right approach. The property CurrentCell can be used to set the current cell. You want something like this:

private void dataGridView_SelectionChanged(object sender, EventArgs e)
{
    DataGridViewCell currentCell = dataGridView.CurrentCell;
    if (currentCell != null)
    {
        int nextRow = currentCell.RowIndex;
        int nextCol = currentCell.ColumnIndex + 1;
        if (nextCol == dataGridView.ColumnCount)
        {
            nextCol = 0;
            nextRow++;
        }
        if (nextRow == dataGridView.RowCount)
        {
            nextRow = 0;
        }
        DataGridViewCell nextCell = dataGridView.Rows[nextRow].Cells[nextCol];
        if (nextCell != null && nextCell.Visible)
        {
            dataGridView.CurrentCell = nextCell;
        }
    }
}

You'll need to add a test for the current cell being read only and loop while the next cell is invisible or read only. You'll also need to check to make sure that you don't loop for ever if all cells are read only.

You'll have to cope with the case where the display index is different to the base index too.

To get this behaviour just when pressing Tab you'll need to add a KeyDown handler:

private void AlbumChecker_KeyDown(object sender, KeyEventArgs e)
{
    if (e.KeyCode == Keys.Tab)
    {
        SelectNextEditableCell(DataGridView dataGridView);
    }
}

and put the first code in this new method.

You might want to check that the DataGridView has focus too.

I did an example inheriting DataGridView class. The example works for TAB and ENTER keys so the user can quickly insert data, but can still use the mouse or the up, down, right, left keys to select the cells and copy them to an excel. It works simulating several TABs until the Grid gets to a non ReadOnly Cell. Hope it's useful.

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;

namespace System.Windows.Forms
{
  class MyDataGridView : DataGridView
  {
    protected override bool ProcessDialogKey(Keys keyData)
    {
      if (keyData == Keys.Enter || keyData == Keys.Tab)
      {
        MyProcessTabKey(Keys.Tab);
        return true;
      }
      return base.ProcessDialogKey(keyData);
    }

    protected override bool ProcessDataGridViewKey(KeyEventArgs e)
    {
      if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Tab)
      {
        MyProcessTabKey(Keys.Tab);
        return true;
      }
      return base.ProcessDataGridViewKey(e);
    }

    protected bool MyProcessTabKey(Keys keyData)
    {
      bool retValue = base.ProcessTabKey(Keys.Tab);
      while (this.CurrentCell.ReadOnly)
      {
        retValue = base.ProcessTabKey(Keys.Tab);
      }
      return retValue;
    }
  }
}

Hector - I hope you are still listening. Your solution is the most elegant and straightforward one I've seen in extensive searching. I've run into the suggestion to override the key events, but your suggestion to invoke base.ProcessTabKey is especially simple and it handles focus passing to the next control when you reach the end of the dgv. The one additional thing your solution needed was a check in MyProcessTabKey for the last cell of the dgv. If that cell is readonly and the user tabs in the previous cell, the while statement goes into an infinite loop. Adding the following code as the first statement in the while loop seems to solve the problem, though it does leave the last (readonly) cell the "active" cell (meaning it appears as though it's selected).

    if (this.CurrentCell.RowIndex == this.Rows.Count - 1
                && this.CurrentCell.ColumnIndex == this.Columns.Count - 1)
                { retValue = false; break; } 

I have a follow-on question. Do you or anyone else know how to make this approach work with Shift-Tab, so the dgv skips readonly cells in a backward direction as well? Since Shift and Tab are different key events, I don't know how to detect Shift-Tab in the overridden ProcessDialogKey and ProcessDataGridViewKey methods. Thanks. Steve

When the cell is in edit mode, and you want to listen to keystroke events, you might try handling the DataGridView's EditingControlShowing event...

Private Sub myDvGrid_EditingControlShowing(ByVal sender As System.Object, ByVal e As System.Windows.Forms.DataGridViewEditingControlShowingEventArgs) Handles myDvGrid.EditingControlShowing
    Dim c As DataGridViewTextBoxEditingControl = DirectCast(e.Control, DataGridViewTextBoxEditingControl)

    RemoveHandler c.PreviewKeyDown, AddressOf edtctrlOn_PreviewKeyDown
    RemoveHandler c.TextChanged, AddressOf edtctrlOn_TextChanged

    AddHandler c.TextChanged, AddressOf edtctrlOn_TextChanged
    AddHandler c.PreviewKeyDown, AddressOf edtctrlOn_PreviewKeyDown

End Sub

Then, in the edtctrlOn_PreviewKeyDown event, you can bubble the arguments over to the original datagrid's PreviewKeyDown event handler.

You can catch Tab Key by this code in ProcessDialogKey

    Dim uKeyCode As Keys = (keyData And Keys.KeyCode)
    Dim uModifiers As Keys = (keyData And Keys.Modifiers)

    (uKeyCode = Keys.Return OrElse uKeyCode = Keys.Tab) AndAlso uModifiers = Keys.Shift

And by this code in ProcessDataGridViewKey

    (e.KeyCode = Keys.Return OrElse e.KeyCode = Keys.Tab) AndAlso e.Modifiers = Keys.Shift

Inherit DataGridView and override ProcessDialogKey (for key pressed while editing) and ProcessDataGridViewKey (for key pressed while not editing). When Tab was pressed, set CurrentCell to the next non-readonly cell.

Optionally override WndProc to filter mouse clicks on readonly cells. (See DataGridView.GetColumnDisplayRectangle to find which column was clicked).

Good source to start from here .

 if (e.KeyValue == 13)
        {
            e = new KeyEventArgs(Keys.Tab);
        }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM