问题描述
DataGridView是一个很常用的数据控件,不仅可以用于二维数据的显示,同时还可以方便地进行编辑。一般控件越强大,往往也意味着它的结构很复杂,从而会有一些使用的问题。在DataGridView控件中,相信大多数使用者都会遇到一个问题就是:在单元格编辑后焦点会自动向下移动一格。
在网上有很多文章都提出了重写 ProcessCmdKey
来解决,并且给出了相应的代码,但是这些重写都是在窗体级完成,即写在窗体内。虽然解决了问题,但是不符合设计模式的开放封闭原则,使用起来会带来很多麻烦。
解决思路
为了解决这个问题,经研究发现 ProcessCmdKey
的传送机制是基于控件结构的遍历,即在控件上是一层一层传输,所以我们可以直接对 DataGridView 进行派生,然后在派生中重写这个方法即可。
代码实现
实现代码如下。
public class DataGridViewEx : DataGridView
{
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (IsCurrentCellInEditMode && keyData == Keys.Enter)
{
EndEdit();
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
}
使用实例
在本使用中,我们建议了一个DataGridView 的实例 dgv
。然后在编辑时,判断单元格的数据是否是数字。对以下三种情况进行处理:
- 数字字符串:则自动跳到下一行,方便用户继续编辑;
- 空字符串:退出编辑模式,继续停留在当前单元格;
- 其他字符串:弹出对话框报错,然后重新进入当前单元格的编辑。
private void Dgv_CellEndEdit(object sender, DataGridViewCellEventArgs e)
{
string value = dgv[e.ColumnIndex, e.RowIndex].Value?.ToString();
// 值为空时,只退出编辑模式,不做任何操作。
if (string.IsNullOrEmpty(value))
{
Console.WriteLine("value is null or empty.");
}
// 如果能转换,则移动到下一行。
else if (double.TryParse(value, out double result))
{
dgv.CurrentCell = dgv[e.ColumnIndex, e.RowIndex + 1];
}
// 如果不能转换为数字,则弹出对话框,然后继续进入编辑模式。
else
{
MessageBox.Show("字符串格式不正确。");
dgv.BeginEdit(true);
}
}
附:ProcessCmdKey 函数简介
定义
protected virtual bool ProcessCmdKey(ref Message msg, Keys keyData);
参数列表
msg
:通过引用传递的 Message,它表示要处理的窗口消息。
keyData
:Keys 值之一,它表示要处理的键。
返回类型
如果字符已由控件处理,则为 true;否则为 false。
使用说明
在消息预处理过程中调用此方法,以处理命令键。命令键是始终比常规输入键具有优先权的键。命令键的示例包括快捷键和菜单快捷方式。此方法必须返回 true,以指示它已经处理完命令键,或者 false,以指示该键不是命令键。仅当控件承载在 Windows 窗体应用程序中或充当 ActiveX 控件时,才调用此方法。
ProcessCmdKey 方法首先确定控件是否有 ContextMenu,如果有,则允许 ContextMenu 处理命令键。如果命令键不是菜单快捷方式,且控件有父级,那么该键传递到父级的 ProcessCmdKey 方法。净效果是命令键在控件层次结构中向上“冒”。除了用户按下的键外,键数据还指示哪些(如果有的话)修改键与该键同时按下。修改键包括 SHIFT、CTRL 和 ALT 键。
对继承者的说明: 在派生类中重写 ProcessCmdKey 方法时,控件应返回 true 以指示它已处理该键。对于未由该控件处理的键,应返回调用基类的 ProcessCmdKey 方法的结果。控件很少需要重写此方法(即使有的话)。