我有一个自定义 DataGridView 编辑控件,它使用 Enter 键来实现其某些功能。它IDataGridViewEditingControl
使用以下代码实现接口方法'`EditingControlWantsInputKey':
public bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
{
switch (keyData & Keys.KeyCode)
{
case Keys.Left:
case Keys.Right:
case Keys.Up:
case Keys.Down:
case Keys.Home:
case Keys.End:
case Keys.Enter:
case Keys.Delete:
return true;
default:
return !dataGridViewWantsInputKey;
}
}
但是,它永远不会收到 Enter 键的 KeyDown 事件。我在EditingControlWantsInputKey
方法中放置了一个条件断点,以查看数据网格视图是否曾经调用过它,以确定我是否想响应 Enter 键,只是发现它从未被调用过。
在我的编辑控件中,我重写了该方法以查看是否使用以下代码ProcessCmdKey
将底层发送到控件。Message
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if ((msg.Msg == User32.WM_KEYDOWN) &&
((Keys)msg.WParam == Keys.Enter))
{
Console.WriteLine("GOT HERE");
}
return base.ProcessCmdKey(ref msg, keyData);
}
它实际上是在获取消息,但它从未发送到该OnKeyDown
方法。
有一次我认为DataGridView
可能已经注册 aIMessageFilter
来处理密钥本身,但如果是这种情况,控件会调用到ProcessCmdKey
(我自己通过添加自己的 来检查这个IMessageFilter
)。
有谁知道 DataGridView 正在做什么来防止我的自定义编辑控件被调用,OnKeyDown
以及是否有办法改变这种行为?
我唯一能想到的就是自己从ProcessCmdKey
方法中处理消息的路由,但这只是感觉很糟糕。
[编辑]
回答 King King 的评论:
编辑控件是 a 的自定义子类TextBox
。自定义子类只需添加更高级的自动完成功能(比对 a 的内置支持更好TextBox
)即可工作。自定义文本框使用 KeyDown 事件来了解用户何时想要选择建议的自动完成项。它在应用程序中的其他几个地方使用已经在生产代码中使用了几个月(所以我非常有信心它不是罪魁祸首)。
[编辑 - 带有示例代码]
我已经构建了一个最小的程序,它似乎显示了这种情况。创建一个新的 WinForms 项目并在 Form 中放置一个 DataGridView。对不起,代码墙,但这只是看到效果所需的最低限度。
如果运行,您会注意到当键码为 Enter 时将EditingControlWantsInputKey
永远不会被调用,不会被调用,但会被调用。CustomEditingControl
OnKeyDown
ProcessCmdKey
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
dataGridView.EditingControlShowing += this.DataGridView_EditingControlShowing;
}
private void DataGridView_EditingControlShowing(object sender, DataGridViewEditingControlShowingEventArgs e)
{
// Must remove first to avoid adding the same event handler twice.
e.Control.KeyDown -= this.EditingControl_KeyDown;
e.Control.KeyDown += this.EditingControl_KeyDown;
}
private void EditingControl_KeyDown(object sender, KeyEventArgs e)
{
Console.WriteLine(e.KeyData);
}
}
public class CustomEditingControl : DataGridViewTextBoxEditingControl
{
public override bool EditingControlWantsInputKey(Keys keyData, bool dataGridViewWantsInputKey)
{
if ((Keys.KeyCode & keyData) == Keys.Enter)
{
Console.WriteLine("EditingControlWantsInputKey: Enter");
}
else
{
Console.WriteLine("EditingControlWantsInputKey: Other");
}
return base.EditingControlWantsInputKey(keyData, dataGridViewWantsInputKey);
}
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
const int WM_KEYDOWN = 0x0100;
if ((msg.Msg == WM_KEYDOWN) &&
((Keys)msg.WParam == Keys.Enter))
{
Console.WriteLine("ProcessCmdKey: Enter");
}
return base.ProcessCmdKey(ref msg, keyData);
}
protected override void OnKeyDown(KeyEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
Console.WriteLine("OnKeyDown: Enter");
}
else
{
Console.WriteLine("OnKeyDown: Other");
}
base.OnKeyDown(e);
}
}
public class CustomDataGridViewTextBoxCell : DataGridViewTextBoxCell
{
public override Type EditType
{
get
{
return typeof(CustomEditingControl);
}
}
}
public class CustomDataGridViewColumn : DataGridViewTextBoxColumn
{
public CustomDataGridViewColumn()
{
this.CellTemplate = new CustomDataGridViewTextBoxCell();
}
}
[编辑 - 更多发现]
我在消息链中添加了尽可能多的日志记录。对于普通文本框,按 Enter 键会产生如下消息跟踪:
PreFilterMessage: Return
PreProcessMessage: Return
ProcessCmdKey: Return
WndProc: Return
ProcessKeyPreview: Return
OnKeyDown: Return
对于编辑控件,按下“1”键(例如)会产生以下轨迹:
PreFilterMessage: D1
PreProcessMessage: D1
ProcessCmdKey: D1
WndProc: D1
EditingControlWantsInputKey: D1
ProcessKeyPreview: D1
OnKeyDown: D1
EditingControl_KeyDown: D1 (This is from the hooked up event handler)
对于编辑控件,按“Enter”键会产生以下轨迹
PreFilterMessage: Return
PreProcessMessage: Return
ProcessCmdKey: Return
所以有些东西(大概是 DataGridView)正在捕获ProcessCmdKey
和WndProc
方法之间的消息。