C# KeyDown 事件未触发,KeyPreview 设置为 true
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10267034/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
KeyDown event is not firing, KeyPreview set to true
提问by WonderCsabo
I'm building a small Forms application, i've just started it. But i have this problem: if i put a Control to the form, the KeyDown event is not firing. I'm aware of the KeyPreview property, and set it to true. But that didn't helped... :( I also tried to set the focus to the main form, no success either.
我正在构建一个小的 Forms 应用程序,我刚刚开始使用它。但是我有这个问题:如果我在表单中放置一个控件,KeyDown 事件不会触发。我知道 KeyPreview 属性,并将其设置为 true。但这并没有帮助... :( 我也尝试将焦点设置在主窗体上,但也没有成功。
Any thoughts?
有什么想法吗?
Edit:
编辑:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
KeyDown += new KeyEventHandler(Form1_KeyDown);
this.KeyPreview = true;
}
void Form1_KeyDown(object sender, KeyEventArgs e)
{
switch (e.KeyCode)
{
case Keys.Left: MessageBox.Show("Left");
break;
case Keys.Right: MessageBox.Show("Right");
break;
}
}
}
采纳答案by WonderCsabo
I already commented my solution, but I also post it as an answer, so it can be easily found.
我已经评论了我的解决方案,但我也将其作为答案发布,因此很容易找到。
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
switch (keyData)
{
case Keys.Left:
// left arrow key pressed
return true;
case Keys.Right:
// right arrow key pressed
return true;
case Keys.Up:
// up arrow key pressed
return true;
case Keys.Down:
// down arrow key pressed
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
回答by gibertoni
The arrow keys are one kind of special key that are automatically handled by Controls. So if you want to make them raise the KeyDown event you could:
箭头键是一种由 Controls 自动处理的特殊键。因此,如果您想让它们引发 KeyDown 事件,您可以:
1) Override the isInputKey method in every control of your form
1) 在表单的每个控件中覆盖 isInputKey 方法
OR
或者
2) Handle the PreviewKeyDown event and set the IsInputKey property to true
2) 处理 PreviewKeyDown 事件并将 IsInputKey 属性设置为 true
More info can be found here.
更多信息可以在这里找到。
I know WonderCsabo solved his problem already but someone else put a bounty on it because is having the same problem and no answer was selected. WonderCsabo please post your solution as answer as well.
我知道 WonderCsabo 已经解决了他的问题,但其他人悬赏了它,因为他遇到了同样的问题并且没有选择答案。WonderCsabo 请将您的解决方案也作为答案发布。
回答by Catalin M.
The reason of the behaviour you observe is that special keys like TAB, UP/DOWN/LEFT/RIGHT ARROW, PAGE UP/DOWN, HOME, END, etc. are often considered "Input Keys" by common controls.
您观察到的行为的原因是诸如 TAB、UP/DOWN/LEFT/RIGHT ARROW、PAGE UP/DOWN、HOME、END 等特殊键通常被常用控件视为“输入键”。
For example, ARROW keys are considered "Input Keys" by the TabControl as these keys allows you to change the selected TabPage. A similar behaviour is present with a multiline TextBox where the ARROWS keys allows you to move the text cursor.
例如,箭头键被 TabControl 视为“输入键”,因为这些键允许您更改选定的 TabPage。类似的行为存在于多行文本框,其中箭头键允许您移动文本光标。
I assume that the Rumba Mainframe control you have does the same thing for the same reasons. You can try overriding it and changing the implementation of the IsInputKey method or handling the PreviewKeyDown event and setting the IsInputKey property to true.
我假设您拥有的 Rumba Mainframe 控件出于相同的原因执行相同的操作。您可以尝试覆盖它并更改 IsInputKey 方法的实现或处理 PreviewKeyDown 事件并将 IsInputKey 属性设置为 true。
Please see the documentation of the Control.IsInputKey Methodand Control.PreviewKeyDown Eventfor further details
有关详细信息,请参阅Control.IsInputKey 方法和Control.PreviewKeyDown 事件的文档
回答by Mohammad Dehghan
If you were on WPF, you could easily catch the required events, because WPF uses routed event systemto dispatch events. In winforms, I recomment one of these two ways:
如果您使用 WPF,您可以轻松捕获所需的事件,因为 WPF 使用路由事件系统来调度事件。在winforms中,我推荐以下两种方式之一:
1. Using Application.AddMessageFilter Method:
1. 使用Application.AddMessageFilter Method:
Define a Message Filter class:
定义一个消息过滤器类:
public class KeyMessageFilter : IMessageFilter
{
private enum KeyMessages
{
WM_KEYFIRST = 0x100,
WM_KEYDOWN = 0x100,
WM_KEYUP = 0x101,
WM_CHAR = 0x102,
WM_SYSKEYDOWN = 0x0104,
WM_SYSKEYUP = 0x0105,
WM_SYSCHAR = 0x0106,
}
[DllImport("user32.dll")]
private static extern IntPtr GetParent(IntPtr hwnd);
// We check the events agains this control to only handle
// key event that happend inside this control.
Control _control;
public KeyMessageFilter()
{ }
public KeyMessageFilter(Control c)
{
_control = c;
}
public bool PreFilterMessage(ref Message m)
{
if (m.Msg == (int)KeyMessages.WM_KEYDOWN)
{
if (_control != null)
{
IntPtr hwnd = m.HWnd;
IntPtr handle = _control.Handle;
while (hwnd != IntPtr.Zero && handle != hwnd)
{
hwnd = GetParent(hwnd);
}
if (hwnd == IntPtr.Zero) // Didn't found the window. We are not interested in the event.
return false;
}
Keys key = (Keys)m.WParam;
switch (key)
{
case Keys.Left:
MessageBox.Show("Left");
return true;
case Keys.Right:
MessageBox.Show("Right");
return true;
}
}
return false;
}
}
So you have a class that every message in Windows Forms passes through it. You can do whatever you want with the event. If PreFilterMessagemethod returns true, it means that the event should not be dispatched to it's respcetive control.
所以你有一个类,Windows 窗体中的每条消息都通过它。你可以对事件做任何你想做的事情。如果PreFilterMessage方法返回 true,则意味着不应将事件分派给它的相应控件。
(Note that the values in the Keysenumeration is almost idential to virtual key codes)
(请注意,Keys枚举中的值几乎与虚拟键码相同)
Before this works, you have to add it to the application's message filters:
在此工作之前,您必须将其添加到应用程序的消息过滤器中:
public partial class Form1 : Form
{
// We need an instance of the filter class
KeyMessageFilter filter;
public Form1()
{
InitializeComponent();
filter = new KeyMessageFilter(panel1);
// add the filter
Application.AddMessageFilter(filter);
}
protected override void OnFormClosed(FormClosedEventArgs e)
{
base.OnFormClosed(e);
// remove the filter
Application.RemoveMessageFilter(filter);
}
}
The filter is only active in the lifetime of the Form1.
过滤器仅在Form1.
Notice: This will catch events in any form! If you want it to work for only one form, pass the form to the filter class, and compare its Handleproperty with m.HWndin PreFilterMessage
注意:这将捕获任何形式的事件!如果您希望它仅适用于一种表单,请将表单传递给过滤器类,并将其Handle属性与m.HWndinPreFilterMessage
2. Using Windows Hooks:
2. 使用Windows 钩子:
This is a more advanced and complicated (and low level) approach. And it requires more code. I've wrote a HookManagerclass that makes the process very simple. I'm gonna publish the class to github and write an article about it.
这是一种更高级和复杂(和低级)的方法。它需要更多的代码。我写了一个HookManager类,使这个过程非常简单。我要把这个类发布到 github 并写一篇关于它的文章。

