Как программно перейти из одной ячейки в сетке данных в другую?

Мне нужно разрешить ввод только одного символа в редактируемые ячейки таблицы данных (все остальные столбцы, нечетные, редактируются); если пользователь добавляет второй символ, находясь в одной из этих ячеек, курсор должен переместиться на следующую ячейку вниз и поместить туда это второе значение (повторное нажатие на эту клавишу снова перемещается вниз и т. д.). Если в нижней части сетки (12-й ряд), он должен переместиться в строку 0, а также переместить два столбца вправо.

Я пытался сделать это:

private void dataGridViewPlatypus_KeyDown(object sender, KeyEventArgs e) {
    var currentCell = dataGridViewPlatypus.CurrentCell;
    int currentCol = currentCell.ColumnIndex;
    int currentRow = currentCell.RowIndex;
    if (currentCell.Value.ToString().Length > 0) {
        if (currentRow < 11) {
            dataGridViewPlatypus.CurrentCell.RowIndex = currentRow+1;
        } else if (currentRow == 11) {
            currentCell.RowIndex = 0;
            currentCell.ColumnIndex = currentCell.ColumnIndex + 2;
            dataGridViewPlatypus.CurrentCell = currentCell;
        }
    }
}

... но я получаю ошибочные сообщения, что RowIndex и ColumnIndex не могут быть назначены, так как они доступны только для чтения.

Так как я могу это сделать?

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

ОБНОВИТЬ

Судя по ответу тергивера, это то, что у меня так далеко, но я не знаю, как перейти к следующей камере.

protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
    if (this.ActiveControl == dataGridViewPlatypus)
    {
        var currentCell = dataGridViewPlatypus.CurrentCell;
        if (currentCell.Value.ToString().Length == 1) 
        {
            ;//Now what?
        }
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

ОБНОВЛЕНИЕ 2

Спасибо всем; это то, что я использую, чтобы заставить его работать в значительной степени (я все еще хочу иметь возможность позволить пользователю просто удерживать клавишу и непрерывно вводить это значение в последующих ячейках):

private void dataGridViewPlatypus_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e) {
    int columnIndex = (((DataGridView)(sender)).CurrentCell.ColumnIndex);
    if (columnIndex % 2 == 1) {
        e.Control.KeyDown -= TextboxNumeric_KeyDown;
        e.Control.KeyDown += TextboxNumeric_KeyDown;
        e.Control.KeyUp -= TextboxNumeric_KeyUp;
        e.Control.KeyUp += TextboxNumeric_KeyUp;
    }
}

private void TextboxNumeric_KeyDown(object sender, KeyEventArgs e) {
    var tb = sender as TextBox;
    if (tb != null) {
        tb.MaxLength = 1;
    }
}

// TODO: Now need to find a way to be able to just press down once
private void TextboxNumeric_KeyUp(object sender, KeyEventArgs e) {
    var tb = sender as TextBox;
    if (tb != null && tb.TextLength >= 1) {
        if (dataGridViewPlatypus.CurrentCell.RowIndex != dataGridViewPlatypus.Rows.Count - 1) {
            dataGridViewPlatypus.CurrentCell = dataGridViewPlatypus[
                dataGridViewPlatypus.CurrentCell.ColumnIndex,
                dataGridViewPlatypus.CurrentCell.RowIndex + 1];
        } else { // on last row
            this.dataGridViewPlatypus.CurrentCell = this.dataGridViewPlatypus.CurrentCell.ColumnIndex !=    dataGridViewPlatypus.Columns.Count - 1 ? this.dataGridViewPlatypus[this.dataGridViewPlatypus.CurrentCell.ColumnIndex    + 2, 0] : this.dataGridViewPlatypus[1, 0];
        }
    }
}

4 ответа

Решение

CurrentCell собственность DataGridView имеет установщик, позволяющий перейти в новую ячейку.

Один из подходов к этой проблеме заключается в EditingControlShowing событие сетки и прикрепить KeyPress обработчик для элемента управления редактирования так:

private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{                
    if ((int)(((System.Windows.Forms.DataGridView)(sender)).CurrentCell.ColumnIndex) == 1)
    {
        e.Control.KeyPress += TextboxNumeric_KeyPress;
    }
}

Тогда в обработчике нажатия клавиш у вас есть:

private void TextboxNumeric_KeyPress(object sender, KeyPressEventArgs e)
{
    TextBox tb = sender as TextBox;
     if (tb.TextLength >= 5)
     {
         dataGridView1.CurrentCell = dataGridView1[dataGridView1.CurrentCell.ColumnIndex + 1, dataGridView1.CurrentCell.RowIndex];
     }
}

Вышеприведенная логика, конечно, не верна для вашего случая, но принцип передачи нового CurrentCell (после извлечения нужной ячейки из сетки) остается в силе.

У меня есть процесс, который бомбит, если выбранная ячейка находится в первом столбце. Итак, в коде кнопки для этого процесса это первый код: (кстати, я использую выбор ячеек в сетке)

if (dgvGrid.CurrentCell.ColumnIndex == 0) // first column
    dgvGrid.Rows[dgvGrid.CurrentCell.RowIndex].Cells[1].Selected = true;

Это эффективно "вкладки" в следующий столбец, а затем весь мой процесс работает.

Клетки имеют Selected свойство, которое вы можете установить. Просто получите доступ к ячейке по столбцу и индексу строки.

Я верю, что ты можешь просто сделать

dgView.rows[0].cells[0].selected = true,

который даст вам ячейку в (0,0) или в первой строке, первый столбец пересекается. Или вы можете взять строку следующим образом:

Я думаю, что это класс ->DataGridViewRow row = dgView.rows[0]

а потом

row[0].cells[0].Selected = true,

       Column 1  Column 2
Row 1 [this guy][        ]
Row 2 [        ][        ]

РЕДАКТИРОВАТЬ:

Чтобы закончить следующую ячейку, просто сделайте:

sameRow.cells[currentCell.ColumnIndex+1].Selected = true;

Возможно, я пропустил некоторые заглавные буквы там, но вы поняли.

KeyDown на DGV не будет работать, потому что DataGridViewTextBoxColumn использует встроенный элемент управления TextBox, который он делает видимым и перемещается на место как раз вовремя для редактирования.

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

Лучше было бы использовать переопределение ProcessCmdKey формы и выполнять эту логику там. Когда происходит нажатие клавиши, проверьте, является ли DGV ActiveControl, проверьте, является ли текущая ячейка текстовой ячейкой, проверьте, содержит ли ячейка символ, а затем измените текущую ячейку, прежде чем разрешить обработку ключа.

Обновлено - дубль 2

using System;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;

class Item
{
    public string A { get; set; }
    public string B { get; set; }
    public string C { get; set; }
    public string D { get; set; }
}

class Form1 : Form
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    DataGridView dataGridViewPlatypus;

    public Form1()
    {
        ClientSize = new Size(480, 260);
        Controls.Add(dataGridViewPlatypus = new DataGridView
        {
            Dock = DockStyle.Fill,
            DataSource = Enumerable.Range(1, 10).Select(i => new Item { A = "", B = "", C = "", D = "" }).ToList(),
        });
    }

    [DllImport("User32.dll")]
    extern static int PostMessage(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam);

    protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
    {
        if (msg.Msg == 256) // WM_KEYDOWN
        {
            if (this.ActiveControl == dataGridViewPlatypus.EditingControl)
            {
                var currentCell = dataGridViewPlatypus.CurrentCell;
                if (currentCell.OwningColumn is DataGridViewTextBoxColumn && dataGridViewPlatypus.EditingControl.Text.Length > 0)
                {
                    int rowIndex = currentCell.RowIndex;
                    int columnIndex = currentCell.ColumnIndex;

                    if (++columnIndex >= dataGridViewPlatypus.Columns.Count)
                    {
                        columnIndex = 0;
                        if (++rowIndex >= dataGridViewPlatypus.Rows.Count)
                            rowIndex = 0;
                    }

                    dataGridViewPlatypus.CurrentCell = dataGridViewPlatypus[columnIndex, rowIndex];
                    PostMessage(dataGridViewPlatypus.Handle, msg.Msg, msg.WParam, msg.LParam);
                    return true; // Don't process this message, we re-sent it to the DGV
                }
            }
        }
        return base.ProcessCmdKey(ref msg, keyData);
    }
}
Другие вопросы по тегам