Reputation: 4709
Can anyone show me some code of how I could bypass read only cells in DatagridView when pressing TAB key?
Upvotes: 9
Views: 24875
Reputation: 137128
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.
Upvotes: 6
Reputation: 1
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
Upvotes: 0
Reputation: 31
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;
}
}
}
Upvotes: 3
Reputation: 627
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
Upvotes: 0
Reputation: 226
private void dataGridView1_CellEnter(object sender, DataGridViewCellEventArgs e)
{
if (dataGridView1.CurrentRow.Cells[e.ColumnIndex].ReadOnly)
{
SendKeys.Send("{tab}");
}
}
Upvotes: 21
Reputation: 1
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.
Upvotes: 0
Reputation: 11589
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.
Upvotes: 0