我原本试图让我的程序获得箭头键(向上,向下,向左和向右)的输入,但是发现在KeyDown()中的那些键从来没有做过。之后我发现我可以通过进入PreviewKeyDown()函数并设置来启用箭头键:
e.IsInputKey = true;
无论条件和逻辑如何。麻烦的是当我写这个函数时:
private void Form1_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{ /*whatever logic goes here*/}
它永远不会被解雇;我甚至设置了一个断点,可以在函数内部触发来确定。另外,我试过了:
this.Focus()
在构造函数中以确保主窗体具有焦点,但它没有任何区别。唯一有效的方法是将焦点设置为我创建的Button,并且该按钮还通过调用上面的Form1_PreviewKeyDown()触发PreviewKeyDown事件。
所以在这一点上我有一个工作方法,但任何人都可以帮助我理解为什么它从未被解雇过?我假设由于某种原因Form的PreviewKeyEvent永远不会触发,但我真的不知道为什么。
答案 0 :(得分:8)
您可以尝试这个小实验:使用两个按钮创建一个表单,覆盖PreviewKeyDown()
,设置断点,运行它,然后按左/右箭头键。 PreviewKeyDown()
方法无法运行。但是删除按钮并重写将调用。
区别的原因是WinForms正在处理箭头键本身进行导航。当您有按钮和文本框等输入控件时,WinForms将自动接管某些特殊键,如TAB
和箭头键,以便从一个控件导航到下一个控件。它可能是这样做的,因为很多人都喜欢能够使用键盘进行导航,如果你搞乱了导航键,很容易打破它们。最好为你处理它们,这样你就可以在玩其他键的时候不小心弄乱它们。
一个天真的解决方法是检测何时形成失去焦点并将其取回。但这并不起作用,因为你的表格不会失去焦点。输入控件具有焦点,它们是表单的一部分,因此表单仍然(技术上,间接地)具有焦点。当你在其他窗口点击外面时,它只会失去焦点。
更好的解决方法是更好地了解在.Net解释器下面的#34;下面发生的事情。 WinForms非常接近地模仿这个级别,因此它是了解WinForms最新功能的有用指南。
当Windows向您的程序发送输入(如击键)时,您的表单始终不是第一个获得输入的表单。输入转到任何具有焦点的控件。在这种情况下,该控件是其中一个按钮(我假设首先隐藏焦点发光,以证明为什么在没有看到任何内容时第一个笔划没有发生任何事情)。
一旦按钮抓住输入,就可以决定接下来会发生什么。它可以将输入传递给下一个排队的人,做一些事情然后然后传递它,或完全处理输入而不是传递它。
使用普通的字母键,按钮决定它不知道如何处理它们并将它们传递给它的基类。基类也不知道,所以它转发密钥。最终,它会命中Control
类,它通过将其传递给Control
属性中的Parent
来处理它。如果这种情况持续很长时间,您的表单最终将有机会处理输入。
简而言之,WinForms首先将输入提供给最具体的目标,然后研究越来越多的一般事物,直到有人知道如何处理输入。
但是,在箭头键的情况下,按钮知道如何处理它们。它通过将焦点传递给下一个输入控件来处理它们。此时,按钮声明输入完全处理,吞下键并且没有给别人机会查看它。按钮后面的任何人都不知道按键发生了。
这就是为什么你的PreviewKeyDown()
覆盖没有被调用的原因。只有当你的Form
获得击键时它才会被调用,但是它永远不会被击键,因为它进入了一个输入控件,提供了输入控件让导航代码看到它,吞下了导航代码它
不幸的是,绕过这个将是一些工作。键击消失在输入控件中,因此您需要获取将箭头键添加到表单中所涉及的所有输入控件。
为此,您需要从您使用的所有输入控件类型派生新控件,并使用它们代替原件。然后,您必须覆盖每个方法中的OnPreviewKeyDown()
方法并设置e.IsInputKey = true
。这会让你的箭头键进入派生的控件中。 KeyDown()
处理程序,而不是导航代码窃取它们。
接下来,您还必须处理所有这些控件中的KeyDown()
事件。由于您希望箭头键在Form
中引发事件,因此所有派生控件都需要跟踪其形式并将键传递给它(这意味着表单的方法需要公开)
将所有这些放在一起,箭头键传递控件将看起来像这样。
class MyButton : Button
{
public MyButton()
{
this.KeyDown += new KeyEventHandler(MyButton_KeyDown);
}
protected override void OnPreviewKeyDown(PreviewKeyDownEventArgs e)
{
e.IsInputKey = true;
base.OnPreviewKeyDown(e);
}
private void MyButton_KeyDown(object sender, KeyEventArgs e)
{
Form1 f = (Form1)this.FindForm();
f.Form1_KeyDown(sender, e);
}
}
对于所有重复的代码,这会有点容易出错。
更简单的方法是覆盖表单的ProcessCmdKey()
方法并在那里处理密钥。这样的事可能会奏效:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
if (keyData == Keys.Up || keyData == Keys.Down ||
keyData == Keys.Left || keyData == Keys.Right)
{
object sender = Control.FromHandle(msg.HWnd);
KeyEventArgs e = new KeyEventArgs(keyData);
Form1_KeyPress(sender, e);
return true;
}
return base.ProcessCmdKey(ref msg, keyData);
}
即使在输入控件有机会之前,这也会有效地窃取命令键(那些特殊的导航键)。除非这些控件覆盖PreviewKeyDown()
并设置e.IsInputKey = true
。孩子的PreviewKeyDown()
方法将首先出现,然后箭头将被视为不是命令键,而ProcessCmdKey()
将被调用。
ProcessCmdKey()
适用于context menu handling。我不确定将它用于上下文菜单之外的其他事情是否明智,但是even Microsoft recommends it for similar kinds of use它确实有效,所以值得考虑。
长话短说,导航键用于导航。与它们混淆可能会使键盘用户的用户体验变得不愉快,因此.Net使得难以接触它们,因此您将被鼓励混淆其他键。
答案 1 :(得分:3)
我遇到了同样的问题!
幸运的是,我找到了一个密集的答案:)
你可以在每个键按下的Form类的定义中使用bool函数。但请记住返回基本功能!
public partial class myForm : Form
{
public myForm ()
{
InitializeComponent();
}
protected override bool ProcessDialogKey(Keys keyData)
{
//Add your code here
return base.ProcessDialogKey(keyData);
}
}
希望我有所帮助。但如果我的答案不完整请注意我!
答案 2 :(得分:2)
this.KeyPreview = true;