可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I need to only allow one character to be entered into the editable datagridview cells (every other column, the odd-numbered ones, are editable); if the user adds a second character while in one of these cells, the cursor should move to the next cell down and put that second value there (pressing again on that key moves down again, and so forth). If at the bottom of the grid (the 12th row), it should move to row 0 and also move two columns to the right.
I tried doing this:
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;
}
}
}
...but I get err msgs that RowIndex and ColumnIndex cannot be assigned to, as they are readonly.
So how can I accomplish this?
Caveat: I know that I will also have to add logic to move to column 1 if currently at the bottom of the last editable column.
UPDATE
From tergiver's answer, this is what I've got so far, but I don't know how to advance to the next cell.
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);
}
UPDATE 2
Thanks all; this is what I'm using to get it pretty much working (I still want to be able to allow the user to simply hold the key down, and have that value continuously entered in subsequent cells):
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];
}
}
}
回答1:
The CurrentCell
property of the DataGridView
has a setter, allowing you to pass in a new cell.
One approach to this problem is to handle the EditingControlShowing
event of the grid and attach a KeyPress
handler to the editing control like so:
private void dataGridView1_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
if ((int)(((System.Windows.Forms.DataGridView)(sender)).CurrentCell.ColumnIndex) == 1)
{
e.Control.KeyPress += TextboxNumeric_KeyPress;
}
}
Then in the key press handler you have:
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];
}
}
The logic above is of course not correct for your case but the principle of passing in a new CurrentCell (after retrieving the desired cell from the grid) stands.
回答2:
The cells have a Selected
property you can set. Just access the cell by column and row index.
I believe you can just do
dgView.rows[0].cells[0].selected = true
,
which will give you the cell at (0,0) or first row, first column intersect.
Or, you can grab the row like this:
I think that's the class -->DataGridViewRow row = dgView.rows[0]
and then
row[0].cells[0].Selected = true
.
Column 1 Column 2
Row 1 [this guy][ ]
Row 2 [ ][ ]
EDIT:
To get the next cell over, just do:
sameRow.cells[currentCell.ColumnIndex+1].Selected = true;
I may have missed some capitalizations in there, but you get the point.
回答3:
KeyDown on the DGV won't work because a DataGridViewTextBoxColumn uses an in-place TextBox control which it makes visible and moves into place just in time for editing.
Since there is only one in-place TextBox for all text columns, you could subscribe to its KeyDown event, but there may be chicken and egg issue with obtaining a reference to that control.
Better would be to use the Form's ProcessCmdKey override and perform this logic there. When a key down occurs, check if the DGV is the ActiveControl, check if the current cell is a text cell, check if the cell already contains a character, then change the current cell before allowing the key to be processed.
Updated - Take 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);
}
}
回答4:
I have a process that bombs if the selected cell is in column one. So in the code for the button for that process, this is the first code in it: (BTW, I use cell selection in the grid)
if (dgvGrid.CurrentCell.ColumnIndex == 0) // first column
dgvGrid.Rows[dgvGrid.CurrentCell.RowIndex].Cells[1].Selected = true;
This effectively "tabs" to the next column, and then the rest of my process works.