gpt4 book ai didi

c# - 为什么我的自定义 DataGridViewEditingControl 永远不会收到 Enter 键的 KeyDown 事件?

转载 作者:太空宇宙 更新时间:2023-11-03 16:11:25 25 4
gpt4 key购买 nike

我有一个自定义的 DataGridView 编辑控件,它使用 Enter 键来实现某些功能。它使用以下代码实现 IDataGridViewEditingControl 接口(interface)方法“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 方法中放置了一个条件断点,以查看数据 GridView 是否曾经调用它来确定我是否想响应 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 可能已经注册了一个 IMessageFilter 来处理 key 本身,但如果是这样的话,控件将调用ProcessCmdKey(我通过添加自己的 IMessageFilter 自己检查了这一点)。

有谁知道 DataGridView 正在做什么以防止我的自定义编辑控件获得对 OnKeyDown 的调用,以及是否有办法改变该行为?

我唯一能想到的就是通过 ProcessCmdKey 方法自己处理消息的路由,但这感觉很老套。

[编辑]

回复King King的评论:

编辑控件是 TextBox 的自定义子类。自定义子类只需添加更高级的自动完成功能(比对 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)正在捕获 ProcessCmdKeyWndProc 方法之间的消息。

最佳答案

经过大量挖掘,我找到了一个看起来有点老套的解决方案。

创建一个新的 IMessageFilter,我最终得到了以下内容:

private sealed class RouteKeyDownMessageFilter : IMessageFilter
{
private readonly Control mControl;
private readonly Keys mKey;

public RouteKeyDownMessageFilter(Control control, Keys key)
{
this.mControl = control;
this.mKey = (Keys.KeyCode & key);
}

public bool PreFilterMessage(ref Message m)
{
if ((m.Msg == WM_KEYDOWN) &&
(m.HWnd == this.mControl.Handle) &&
(((Keys)m.WParam & Keys.KeyCode) == this.mKey))
{
SendMessage(m.HWnd, m.Msg, m.WParam, m.LParam);
}

return false;
}

public const int WM_KEYDOWN = 0x0100;

[DllImport("user32.dll")]
public static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}

然后,在实现 IDataGridViewEditingControl 接口(interface)的类中,我将此附加逻辑添加到 EditingControlDataGridView 属性以注册/注销消息过滤器。

public DataGridView EditingControlDataGridView
{
get { return this.mEditingControlDataGridView; }
set
{
if (this.mEditingControlDataGridView != null)
{
this.mEditingControlDataGridView.Disposed -= this.EditingControlDataGridView_Disposed;

Application.RemoveMessageFilter(this.mCurrentMessageFilter);
this.mCurrentMessageFilter = null;
}

this.mEditingControlDataGridView = value;

if (this.mEditingControlDataGridView != null)
{
this.mCurrentMessageFilter = new RouteKeyDownMessageFilter(this, Keys.Return);
Application.AddMessageFilter(this.mCurrentMessageFilter);

this.mEditingControlDataGridView.Disposed += this.EditingControlDataGridView_Disposed;
}
}
}

private void EditingControlDataGridView_Disposed(object sender, EventArgs e)
{
if (this.mCurrentMessageFilter != null)
{
Application.RemoveMessageFilter(this.mCurrentMessageFilter);
}
}

因此,只要编辑控件分配有数据 GridView ,所有这些都可以确保消息过滤器就位。它还为数据 GridView 的 Dispose 事件注册一个事件处理程序,以确保我们在清理数据 GridView 时注销消息过滤器。

消息过滤器本身只需要一个控件和一个触发键,并在它获取给定控件的消息时重新路由消息。引用 Control 而不是 ControlHandle 很重要,因为 Handle 可能会在 Control 的生命周期。

我有点困惑,重新发送消息就可以解决问题,但是当它就位(和适当的日志记录)时,我得到以下消息链:

RoutingMessageFilter.PreFilterMessage: Return
RoutingMessageFilter, Routing Message.
WndProc: Return
EditingControlWantsInputKey: Return
PreProcessMessage: Return
ProcessCmdKey: Return
WndProc: Return
OnKeyDown: Return
EditingControl_KeyDown: Return

此消息流唯一奇怪的地方是 WndProc 被调用了两次。我不确定为什么会这样,但它似乎没有任何不良副作用。更重要的是,EditingControlWantsInputKey 现在可以在编辑控件中与 OnKeyDown 方法一起正确调用。

关于c# - 为什么我的自定义 DataGridViewEditingControl 永远不会收到 Enter 键的 KeyDown 事件?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17325707/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com