我在C#(.NET 2.0)中有一个带有一些只读单元格的DataGridView(25个中的3个)。我正在使用顶部答案here中概述的方法在Form_Load处理程序中的那些特定单元格上设置ReadOnly。链接的问题表明
某些单元格必须是ReadOnly,当用户在单元格之间使用TAB或ENTER导航时,应绕过ReadOnly单元格
所以我假设设置标志会导致跳过单元格。
长话短说,不是。只读单元格即使无法编辑也会被选中并选中。我正在梳理DataGridView的属性,寻找某种TabMode或TabSkipsReadOnlyCells属性,但到目前为止还没有。是否有为此行为设置的属性,或者我是否必须编写某种选项卡事件处理代码?
这看起来应该是默认行为,所以我甚至不得不为它找到一个属性,更不用说编写代码了... < / p>
编辑:我应该澄清一下,我对仅使用Tab键处理导航不感兴趣。我想用箭头键和可能的鼠标实现合理的导航。这意味着如果我必须编写代码,我需要直接控制选择在我从一个只读单元格中跳出时的位置,可能是通过在DataGridView上设置CurrentCell。这样,如果用户向上箭头进入只读单元格,我可以重定向到上面的单元格,而不是总是向右单元格。
编辑2:这是我的最终解决方案,处理箭头键导航以及基于Sean Griffiths' code的标签导航(也在接受的答案中链接):
private void GridForm_Load(object sender, EventArgs e)
{
dataGridView1.CellEnter += dataGridView1_CellEnter;
}
//a delegate is needed to avoid a circular loop when selecting a cell when in a cell selection event
private delegate void SetColumnAndRowOnGrid(DataGridView grid, int columnIndex, int rowIndex);
static SetColumnAndRowOnGrid setCellMethod = new SetColumnAndRowOnGrid(setGridCell);
// Method pointed to by the delegate
private static void setGridCell(DataGridView grid, int columnIndex, int rowIndex)
{
grid.CurrentCell = grid.Rows[rowIndex].Cells[columnIndex];
grid.BeginEdit(true);
}
// Track the cell we leave so we can determine direction of "travel"
int _lastRow = 0, _lastCol = 0;
private void dataGridView1_CellLeave(object sender, DataGridViewCellEventArgs e)
{
_lastRow = e.RowIndex;
_lastCol = e.ColumnIndex;
}
enum Direction { Up, Down, Left, Right }
// When we enter a read only cell, determine direction
// of "travel" and keep going that way
private void dataGridView1_CellEnter(object sender, DataGridViewCellEventArgs e)
{
int currRow = e.RowIndex;
int currCol = e.ColumnIndex;
if (dataGridView1.Rows[currRow].Cells[currCol].ReadOnly)
{
Direction direction = Direction.Right;
if ((currRow != _lastRow) && (currCol == _lastCol))
{
// moving vertically
if (currRow < _lastRow) direction = Direction.Up;
else direction = Direction.Down;
}
else
{
// moving horizontally
if (currCol == 0 &&
_lastCol == dataGridView1.Columns.Count - 1 &&
currRow == _lastRow + 1)
{
// Special case - probably just tabbed from end of row
direction = Direction.Right;
}
else if (currCol == dataGridView1.Columns.Count - 1 &&
_lastCol == 0 &&
currRow == _lastRow - 1)
{
// Special case - probably just shift-tabbed from start of row
direction = Direction.Left;
}
else if (currCol < _lastCol) { direction = Direction.Left; }
}
//this cell is readonly, find the next tabable cell
if (!SetNextTabableCell(dataGridView1, currCol, currRow, direction))
{
// All the cells in the grid have been tried, none could be tabbed
// to so move onto the next control
bool tabForward = direction == Direction.Right || direction == Direction.Down;
SelectNextControl(this, tabForward, true, true, true);
}
}
}
// Find the next cell that we want to be selectable
private static bool SetNextTabableCell(DataGridView grid, int nextColumn, int nextRow, Direction direction)
{
//keep selecting each next cell until one is found that isn't either readonly or invisible
int maxMoves = grid.ColumnCount * grid.RowCount;
int moves = 0;
do
{
if (!GetNextCell(grid, ref nextColumn, ref nextRow, ref direction)) return false;
// Prevent infinite loop - I managed to get in one when this function
// wound up in a readonly column with a direction of Down (if we've moved
// to another cell more times than there are cells in the grid, just give up)
if (++moves > maxMoves) return false;
}
while (grid.Rows[nextRow].Cells[nextColumn].ReadOnly == true ||
grid.Rows[nextRow].Cells[nextColumn].Visible == false);
//a cell has been found that can be entered, use the delegate to select it
grid.BeginInvoke(setCellMethod, grid, nextColumn, nextRow);
return true;
}
// Get the next cell in the indicated direction
// Wrap around if going left-right
// Bounce at the edge if going up/down
private static bool GetNextCell(DataGridView grid, ref int nextColumn, ref int nextRow, ref Direction direction)
{
switch (direction)
{
case Direction.Right:
if (nextColumn < grid.Columns.Count - 1)
{
// Nominal case - move right one cell
nextColumn = nextColumn + 1;
}
else // at the last column
{
// go the the first column
nextColumn = 0;
if (nextRow < grid.Rows.Count - 1)
{
// Nominal case - move down one row
nextRow = nextRow + 1;
}
// at the last row and last column exit this method, no cell can be selected
else { return false; }
}
break;
case Direction.Left:
if (nextColumn > 0)
{
// Nominal case - move left one cell
nextColumn = nextColumn - 1;
}
else // at the first column
{
// go the the last column
nextColumn = grid.Columns.Count - 1;
if (nextRow > 0)
{
// Nominal case - move up one row
nextRow = nextRow - 1;
}
// at the first row and first column exit this method, no cell can be selected
else { return false; }
}
break;
case Direction.Down:
if (nextRow < grid.Rows.Count - 1)
{
// Nominal case - move down one cell
nextRow = nextRow + 1;
}
else // at the last row
{
// turn around
nextRow = nextRow - 1;
direction = Direction.Up;
}
break;
case Direction.Up:
if (nextRow > 0)
{
// Nominal case - move up one cell
nextRow = nextRow - 1;
}
else // at the first row
{
// turn around
nextRow = nextRow + 1;
direction = Direction.Down;
}
break;
default: return false;
}
return true;
}
如果有人使用它并发现它表现不佳的情况,我想听听它,所以我希望能通过修复来更新它。
编辑3:在代码设法使其自身处于无限循环状态之后添加了安全计数器。第0列中的所有单元格都设置为只读,第一次单击进入网格控件的是第0列,因此它尝试向下移动,然后向上移动,然后向下移动....
答案 0 :(得分:3)
您将为此添加一些代码 一种方法是
void grd_CellEnter(object sender, DataGridViewCellEventArgs e)
{
if(grd[e.ColumnIndex,e.RowIndex].ReadOnly)
SendKeys.Send("{TAB}");
}
答案 1 :(得分:1)
我在使用datagridview时遇到了类似的问题 - 我的解决方案在这里写了http://codemumbler.blogspot.com/2011/02/one-aspect-of-datagridviews-that-you.html
它使用CellEnter事件处理程序并搜索网格中的下一个可用可用销售,并使用委托来避免重入异常。
希望这会有所帮助 - 肖恩