windows 设置 Form.KeyPreview = true 的缺点?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2386695/
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
Disadvantage of setting Form.KeyPreview = true?
提问by Dan Bystr?m
I wonder what the Form.KeyPreview property actually is good for? Why do it exist and what do I "risk" by setting it to true? I guess it must have somenegative effect - otherwise it shouldn't exist at all (or at least be true by default)?
我想知道 Form.KeyPreview 属性实际上有什么用?为什么它存在,我将它设置为 true 有什么“风险”?我想它一定有一些负面影响 - 否则它根本不应该存在(或者至少默认情况下是真的)?
EDIT: I know perfectly well whatit does. I'm asking why. Why do I have to set it to true in order to make the keyboard events fire? Why doesn't the keyboard events alwaysfire for a form. What isn't just that the standard behavior?
编辑:我非常清楚地知道什么它。我在问为什么。为什么我必须将其设置为 true 才能触发键盘事件?为什么键盘事件不总是为表单触发。什么不只是标准行为?
The particular reason I ask is: I have just set KeyPreview = true in the base form of my app, which all other forms inherit from. Am I in for any nasty surprise?
我问的特别原因是:我刚刚在我的应用程序的基本形式中设置了 KeyPreview = true,所有其他形式都从它继承。我有什么讨厌的惊喜吗?
回答by Hans Passant
Form.KeyPreview is a bit of an anachronism, inherited from the Visual Basic object model for form design. Back in the VB6 days, you needed KeyPreview to be able to implement short-cut keystrokes. That isn't needed anymore in Windows Forms, overriding the ProcessCmdKey() is the better solution:
Form.KeyPreview 有点过时了,它继承自用于表单设计的 Visual Basic 对象模型。回到 VB6 时代,您需要 KeyPreview 才能实现快捷键击键。在 Windows 窗体中不再需要它,覆盖 ProcessCmdKey() 是更好的解决方案:
protected override bool ProcessCmdKey(ref Message msg, Keys keyData) {
if (keyData == (Keys.Control | Keys.F)) {
DoSomething(); // Implement the Ctrl+F short-cut keystroke
return true; // This keystroke was handled, don't pass to the control with the focus
}
return base.ProcessCmdKey(ref msg, keyData);
}
But KeyPreview was supported to help the legion of VB6 programmers switch to .NET back in the early 2000's. The point of KeyPreview or ProcessCmdKey() is to allow your UI to respond to shortcut keystrokes. Keyboard messages are normally sent to the control that has the focus. The Windows Forms message loop allows code to have a peek at that message before the control sees it. That's important for short-cut keys, implementing the KeyDown event for everycontrol that might get the focus to detect them is very impractical.
但是支持 KeyPreview 以帮助 VB6 程序员军团在 2000 年代初期切换到 .NET。KeyPreview 或 ProcessCmdKey() 的目的是让您的 UI 响应快捷键。键盘消息通常发送到具有焦点的控件。Windows 窗体消息循环允许代码在控件看到该消息之前查看该消息。这对于快捷键很重要,为每个可能获得焦点以检测它们的控件实现 KeyDown 事件是非常不切实际的。
Setting KeyPreview to True doesn't cause problems. The form's KeyDown event will run, it will only have an affect if it has code that does something with the keystroke. But do beware that it closely follows the VB6 usage, you can't see the kind of keystrokes that are used for navigation. Like the cursor keys and Tab, Escape and Enter for a dialog. Not a problem with ProcessCmdKey().
将 KeyPreview 设置为 True 不会导致问题。表单的 KeyDown 事件将运行,只有当它具有对击键执行某些操作的代码时才会产生影响。但请注意,它严格遵循 VB6 用法,您看不到用于导航的击键类型。与用于对话框的光标键和 Tab、Escape 和 Enter 类似。ProcessCmdKey() 不是问题。
回答by Michal Ciechan
From MSDN
来自MSDN
When this property is set to true, the form will receive all KeyPress, KeyDown, and KeyUp events. After the form's event handlers have completed processing the keystroke, the keystroke is then assigned to the control with focus. For example, if the KeyPreview property is set to true and the currently selected control is a TextBox, after the keystroke is handled by the event handlers of the form the TextBox control will receive the key that was pressed. To handle keyboard events only at the form level and not allow controls to receive keyboard events, set the KeyPressEventArgs.Handled property in your form's KeyPress event handler to true.
You can use this property to process most keystrokes in your application and either handle the keystroke or call the appropriate control to handle the keystroke. For example, when an application uses function keys, you might want to process the keystrokes at the form level rather than writing code for each control that might receive keystroke events.
当此属性设置为 true 时,表单将接收所有 KeyPress、KeyDown 和 KeyUp 事件。在窗体的事件处理程序完成对击键的处理后,击键被分配给具有焦点的控件。例如,如果 KeyPreview 属性设置为 true 并且当前选定的控件是 TextBox,则在窗体的事件处理程序处理击键后,TextBox 控件将接收按下的键。若要仅在窗体级别处理键盘事件并且不允许控件接收键盘事件,请将窗体的 KeyPress 事件处理程序中的 KeyPressEventArgs.Handled 属性设置为 true。
您可以使用此属性来处理应用程序中的大多数击键,并处理击键或调用适当的控件来处理击键。例如,当应用程序使用功能键时,您可能希望在表单级别处理击键,而不是为每个可能接收击键事件的控件编写代码。
Basically when you set it to true, your form can process key events as well as your controls.
基本上,当您将其设置为 true 时,您的表单可以处理关键事件以及您的控件。
E.G User presses K key, the forms event handlers are called(Key Down, Key Up, Key Pressed) and then the event handlers on the currently active control are called.
EG 用户按下 K 键,调用窗体事件处理程序(Key Down、Key Up、Key Pressed),然后调用当前活动控件上的事件处理程序。
EDIT: No there are no disadvantages or nasty suprises. The only thing i can think of is a very tiny performance decrease as it needs to check for event handles on the form for each KeyDown,KeyUp, KeyPressed. Apart from that unless you're adding event handlers to the form, and doing something that might cause problems. you're perfectly fine. If you don't need to globally handle key events except on controls then I would suggest you leave this as false to prevent the extra checks. On modern PC's this wouldnt have a visible difference.
编辑:不,没有缺点或令人讨厌的惊喜。我唯一能想到的是性能下降非常小,因为它需要检查表单上每个 KeyDown、KeyUp、KeyPressed 的事件句柄。除此之外,除非您将事件处理程序添加到表单中,并做一些可能会导致问题的事情。你很好。如果您不需要全局处理除控件之外的关键事件,那么我建议您将其保留为 false 以防止额外检查。在现代 PC 上,这不会有明显的区别。
回答by Mike Dimmick
The standard Windows event model is that the window with the keyboard focus gets all keyboard events. Remember that in Windows, everything is a window - a 'control' is just a window that is a child of another window. It's up to that window to fire messages to its parent if it chooses to do so, when certain keys are pressed.
标准的 Windows 事件模型是具有键盘焦点的窗口获取所有键盘事件。请记住,在 Windows 中,一切都是一个窗口——“控件”只是一个窗口,它是另一个窗口的子窗口。如果按下某些键,则由该窗口向其父级发送消息(如果它选择这样做)。
To standardise navigation between controls on a dialog box, Windows also provides the 'dialog manager'. In native code, for modal dialogs this is handled by the modal message loop inside the DialogBox
function. For modeless dialogs you have to call IsDialogMessage
within your own message loop. This is how it steals the Tab and cursor keys to navigate between controls, and Enter to press the default button. This has the opposite effect of not allowing the controls to handle Enter by default, which multi-line edit controls would normally handle. To discover whether a control wants to handle a key, the dialog manager code sends the focused control a WM_GETDLGCODE
message; if the control responds appropriately the dialog manager returns FALSE
allowing DispatchMessage
to actually deliver it to the window procedure, otherwise the dialog manager does its own thing.
为了标准化对话框上控件之间的导航,Windows 还提供了“对话框管理器”。在本机代码中,对于模态对话框,这是由DialogBox
函数内的模态消息循环处理的。对于无模式对话框,您必须IsDialogMessage
在您自己的消息循环中调用。这就是它如何窃取 Tab 键和光标键在控件之间导航,并使用 Enter 键按下默认按钮。这具有相反的效果,即不允许控件在默认情况下处理 Enter,而多行编辑控件通常会处理这些。为了发现一个控件是否想要处理一个键,对话管理器代码向焦点控件发送一条WM_GETDLGCODE
消息;如果控件做出适当的响应,则对话管理器返回FALSE
允许DispatchMessage
将它实际传递给窗口过程,否则对话框管理器会做自己的事情。
Windows Forms largely just wraps up the old native controls, so it has to conform to Win32's event model. It implements the same dialog manager approach - hence why it doesn't, by default, allow you to see Tab, Return and the cursor keys.
Windows 窗体在很大程度上只是封装了旧的本机控件,因此它必须符合 Win32 的事件模型。它实现了相同的对话框管理器方法 - 因此默认情况下它不允许您查看 Tab、Return 和光标键。
The recommended approach, if you want to handle one of those keys, is to override PreviewKeyDown
and set the PreviewKeyDownEventArgs
IsInputKey
property to true
.
如果您想处理这些键之一,推荐的方法是覆盖PreviewKeyDown
并将PreviewKeyDownEventArgs
IsInputKey
属性设置为true
。
回答by user11511276
A simple and trivial, though practical reason:
一个简单而微不足道但实用的原因:
In a game like Space Invaders https://www.mooict.com/c-tutorial-create-a-full-space-invaders-game-using-visual-studio/the user repeatedly hammers the space bar to vaporize aliens. When the last invader is gone, a text box pops up to say, "good job". The user's still twitching thumb hits the space bar (or maybe just the keyboard buffer releases?) and the congrats MessageBox vanishes before it can be read. I couldn't see a work around because of how Forms handle button/space bar clicks.
在像 Space Invaders https://www.mooict.com/c-tutorial-create-a-full-space-invaders-game-using-visual-studio/这样的游戏中,用户反复敲击空格键以蒸发外星人。当最后一个入侵者离开时,会弹出一个文本框说“干得好”。用户仍在抽搐的拇指击中空格键(或者可能只是键盘缓冲区释放?)并且祝贺 MessageBox 在它可以被读取之前消失。由于表单处理按钮/空格键点击的方式,我看不到解决方法。
My custom dialog uses keypreview to preprocess keystrokes sent to the GameOverDialog to ignore any space bar taps. The user has to close with a mouse click or Enter. This is a just a FixedDialog with a "you win" label and an [OK] button.
我的自定义对话框使用 keypreview 来预处理发送到 GameOverDialog 的击键以忽略任何空格键点击。用户必须通过单击鼠标或 Enter 来关闭。这只是一个带有“you win”标签和 [OK] 按钮的 FixedDialog。
public partial class GameOverDialog : Form
{
public GameOverDialog()
{
InitializeComponent();
this.MaximizeBox = false;
this.MinimizeBox = false;
}
// keyhandler keypreview = true
private void SpaceDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
e.Handled = true;
return;
}
}
private void SpaceUp(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.Space)
{
e.Handled = true;
return;
}
}
private void button1_Click(object sender, EventArgs e)
{
this.Close();
}
}
Also, an interesting option that I've not tested: If you think about it, this would be a great way to embed cheats, hidden messages, etc. into innocuous [OK] dialogs or any Form that allows key preview.
此外,我还没有测试过一个有趣的选项:如果您考虑一下,这将是将作弊、隐藏消息等嵌入无害 [OK] 对话框或任何允许按键预览的表单的好方法。