gpt4 book ai didi

c# - 在Winforms中,从不为任何键触发PreviewKeyDown()

转载 作者:太空狗 更新时间:2023-10-30 00:02:51 25 4
gpt4 key购买 nike

我本来是想让我的程序获取箭头键(上,下,左和右)的输入,但是却发现在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永远不会触发,但是我真的不知道为什么。

最佳答案

为什么

您可以尝试这个小实验:用两个按钮创建一个表单,覆盖PreviewKeyDown(),设置一个断点,运行它,然后按左/右箭头键。 PreviewKeyDown()方法将不会运行。但是删除按钮,将调用覆盖。

造成这种差异的原因是WinForms本身正在处理箭头键以进行导航。当您拥有按钮和文本框之类的输入控件时,WinForms将自动接管某些特殊键,例如TAB和箭头键,以从一个控件导航到下一个控件。之所以这样做,是因为很多人都希望能够使用键盘进行导航,如果您弄乱了导航键,很容易为他们打破这一点。最好为您处理它们,以免在您玩其他键时将它们弄乱。

一个幼稚的解决方法是检测表单何时失去焦点并将其收回。但是,这不起作用,因为您的表单不会失去焦点。输入控件具有焦点,它们是表单的一部分,因此表单仍(在技术上,间接地)具有焦点。只有在其他窗口上单击外部时,它才会失去焦点。

更好的解决方法是对.Net解释器正下方的“幕后”进行更好的理解。 WinForms非常接近地模拟了此级别,因此它是了解WinForms的有用指南。

当Windows将输入(例如击键)发送到程序时,您的窗体并不总是第一个获取输入的窗体。输入将转到具有焦点的任何控件。在这种情况下,该控件是其中一个按钮(我假设首先隐藏了焦点发光,以证明为什么当看起来什么都没有选择时在第一笔划中什么也没有发生)。

一旦按钮掌握了输入,就可以决定下一步将发生什么。它可以将输入传递给下一个排队的人,先执行某项操作然后传递给它,或者完全处理输入而不传递任何输入。

使用普通的字母键,按钮会决定它不知道如何处理它们,而是将其传递给其基类。基类也不知道,因此它转发 key 。最终,它命中了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。将首先使用 child 的 PreviewKeyDown()方法,然后将箭头视为不是命令键,并且不会调用您的 ProcessCmdKey()
ProcessCmdKey()context menu handling的意思。我不确定将其用于上下文菜单以外的其他选项是否明智,但是 even Microsoft recommends it for similar kinds of use确实有效,因此可能值得考虑。

结论

长话短说,导航键用于导航。弄乱它们会使用户的键盘用户感到不愉快,因此.Net使其难以接近他们,因此建议您将其与其他键弄乱。

关于c# - 在Winforms中,从不为任何键触发PreviewKeyDown(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25696507/

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