Как получить навигацию (вкладка / стрелка) по ячейкам в DataGridView, чтобы пропустить ячейки только для чтения? - PullRequest
1 голос
/ 08 февраля 2011

У меня есть DataGridView в C # (.NET 2.0) с несколькими ячейками только для чтения (3 из 25). Я использую метод, описанный в верхнем ответе здесь , чтобы установить ReadOnly для этих конкретных ячеек в обработчике Form_Load. Связанный вопрос гласит, что

некоторые ячейки должны быть доступны только для чтения, а также, когда пользователь перемещается между ячейками с помощью TAB или ENTER, ячейки ReadOnly должны быть пропущены

поэтому я предположил, что установка флага приведет к пропуску ячеек.

Короче говоря, это не так. Ячейки только для чтения вкладываются и выбираются, даже если их нельзя редактировать. Я прочесываю свойства DataGridView, ища какое-то свойство TabMode или TabSkipsReadOnlyCells, но пока ничего. Есть ли свойство, которое нужно установить для этого поведения, или мне нужно написать какой-нибудь код обработки событий табуляции какого-нибудь рода?

Похоже, что это должно быть поведение по умолчанию, поэтому я немного раздражен тем, что мне даже приходится находить для него свойство, а тем более писать код для этого ...

РЕДАКТИРОВАТЬ: я должен уточнить, я не заинтересован только в обработке навигации с помощью клавиши Tab. Я хочу реализовать разумную навигацию с помощью клавиш со стрелками и, возможно, мыши. Это означает, что если мне нужно написать код, мне нужен прямой контроль над тем, куда перемещается выделение, когда я выбрасываю его из ячейки, доступной только для чтения, вероятно, путем установки CurrentCell в DataGridView. Это так, что если пользовательские стрелки вверх в ячейку только для чтения я могу перенаправить на ячейку выше, а не всегда на ячейку справа.

РЕДАКТИРОВАТЬ 2: Вот мое окончательное решение, управляющее навигацией по клавишам со стрелками, а также навигацией по вкладкам на основе кода Шона Гриффитса (также связанного в принятом ответе):

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: Добавлен счетчик безопасности после того, как коду удалось сегодня войти в состояние бесконечного цикла. Все ячейки в нулевом столбце были установлены только для чтения, а первый щелчок в элементе управления сеткой был в нулевом столбце, поэтому он попытался переместиться вниз, затем вверх, затем вниз ....

Ответы [ 2 ]

3 голосов
/ 09 февраля 2011

Вы вставите код для этого Один из способов сделать это -

void grd_CellEnter(object sender, DataGridViewCellEventArgs e)
{            
   if(grd[e.ColumnIndex,e.RowIndex].ReadOnly)
       SendKeys.Send("{TAB}");
}
1 голос
/ 23 февраля 2011

У меня была похожая проблема при использовании datagridview - здесь есть описание моего решения http://codemumbler.blogspot.com/2011/02/one-aspect-of-datagridviews-that-you.html

Он использует обработчик событий CellEnter и ищет следующую доступную продажу в сетке, а также делегат, чтобы избежать исключения реентера.

Надеюсь, это поможет - Шон

...