如何在 WPF 页面或 UserControl 对象上捕获 KeyDown 事件?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/347724/
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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 19:51:30  来源:igfitidea点击:

How can I capture KeyDown event on a WPF Page or UserControl object?

wpfkeydown

提问by Sailing Judo

I have a Page with a UserControl on it. If the user presses Esc while anywhere on Page I want to handle.

我有一个带有 UserControl 的页面。如果用户在我想要处理的页面上的任何地方按下 Esc 键。

I thought this would be as easy as hooking up the PreviewKeyDown event, testing for the Esc key, and then handling it. However, when I placed I breakpoint in the event handler I found it was never getting called. I thought perhaps the UserControl might be getting hit, so I tried PreviewKeyDown there... same result.

我认为这就像连接 PreviewKeyDown 事件,测试 Esc 键,然后处理它一样简单。但是,当我在事件处理程序中放置断点时,我发现它从未被调用。我想也许 UserControl 可能会被击中,所以我在那里尝试了 PreviewKeyDown...同样的结果。

Does anyone know the proper place to test for a KeyDown or PreviewKeyDown on a Page object?

有谁知道在 Page 对象上测试 KeyDown 或 PreviewKeyDown 的正确位置吗?

采纳答案by Andy

I believe that the PreviewKeyDown event is a tunneling routed event, rather than a bubbling one. If that is the case, then if your Page isn't getting the event, the UserControl shouldn't be either since it is below the Page in the visual tree. Maybe try handling it at the top level of your app (Window perhaps?) and see if it is getting the event?

我相信 PreviewKeyDown 事件是一个隧道路由事件,而不是冒泡事件。如果是这种情况,那么如果您的 Page 没有收到该事件,则 UserControl 也不应如此,因为它位于可视化树中的 Page 下方。也许尝试在您的应用程序的顶层(也许是窗口?)处理它,看看它是否正在获取事件?

Another option that might help would be to use something like Snoop in CodePlexto figure out where the events are going.

另一个可能有帮助的选择是使用CodePlex 中的 Snoop 之类的东西来确定事件的去向。

回答by Daniel

Attach to the Window's Event

附加到窗口的事件

After the control is loaded, attach to the Window's KeyDownevent (or any event) by using Window.GetWindow(this), like so:

加载控件后,使用 附加到窗口的KeyDown事件(或任何事件)Window.GetWindow(this),如下所示:

The XAML

XAML

<UserControl Loaded="UserControl_Loaded">
</UserControl>

The Code Behind

背后的代码

private void UserControl_Loaded(object sender, RoutedEventArgs e) {
  var window = Window.GetWindow(this);
  window.KeyDown += HandleKeyPress;
}

private void HandleKeyPress(object sender, KeyEventArgs e) {
  //Do work
}

回答by Sailing Judo

I have/had the same problem. I have a window which hosts a Frame which loads/navigates to various Page's which at their turn contain just 1 UserControl which contains normal Controls/Elements (Labels, Hyperlinks, etc).

我曾经有过同样的问题。我有一个窗口,它承载一个框架,该框架加载/导航到各种页面,这些页面轮流只包含 1 个包含普通控件/元素(标签、超链接等)的 UserControl。

what I discovered (after some hours of frustration of course!!!) is that if you do NOT have the focus on one of the Controls/Elements mentioned above the PreviewKeyDown event does NOT fire on the UserControl (stops somewhere at Frame level) but as soon as you put the focus (for instance calling ctrl.Focus() inside the UserCotrol's Loaded event handler) on one of the controls magic happens, it works, the event fires also for the UserControl.

我发现(当然是经过几个小时的沮丧之后!!!)如果您没有将注意力集中在上面提到的控件/元素之一上,PreviewKeyDown 事件不会在 UserControl 上触发(在帧级别的某处停止)但是只要您将焦点(例如在 UserCotrol 的 Loaded 事件处理程序中调用 ctrl.Focus() )放在其中一个控件上,魔法就会发生,它就会起作用,该事件也会为 UserControl 触发。

of course thinking of it afterwards this makes enough sense but I am 100% sure this will cacth at least 5 out of 10 people by surprise :) crazy little thing called WPF...

当然,事后想想这很有意义,但我 100% 肯定这会让 10 人中至少有 5 人感到惊讶:) 一个叫做 WPF 的疯狂小东西......

cheers!

干杯!

回答by Gigo

Setting the Focusable property to true on my UserControl solved the issue for me.

在我的 UserControl 上将 Focusable 属性设置为 true 为我解决了这个问题。

回答by Jeff T.

I propose a method which strengthens the one @Doc mentioned.

我提出了一种加强@Doc 提到的方法的方法。

The following code @Doc mentioned will work since the KeyDown routed event is bubbled to the outermost Window. Once Windowreceives the KeyDown event bubbled from an inner element, Windowtriggers any KeyDown event-handler registered to it, like this HandleKeyPress.

以下代码@Doc 提到将起作用,因为 KeyDown 路由事件冒泡到最外层Window. 一旦Window接收到从内部元素冒泡的 KeyDown 事件,Window就会触发注册到它的任何 KeyDown 事件处理程序,如下所示HandleKeyPress

private void UserControl_Loaded(object sender, RoutedEventArgs e) {
  var window = Window.GetWindow(this);
  window.KeyDown += HandleKeyPress;
}

private void HandleKeyPress(object sender, KeyEventArgs e) {
  //Do work
}

But this +=is risky, a programmer is more likely to forget to un-register the event-handler. Then memory leaking or some bugs will happen.

但这+=是有风险的,程序员更有可能忘记取消注册事件处理程序。然后会发生内存泄漏或一些错误。

Here I suggest,

这里我建议,

YourWindow.xaml.cs

你的窗口.xaml.cs

protected override void OnKeyDown(KeyEventArgs e)
{
    base.OnKeyDown(e);

    // You need to have a reference to YourUserControlViewModel in the class.
    YourUserControlViewModel.CallKeyDown(e);

    // Or, if you don't like ViewModel, hold your user-control in the class then
    YourUserControl.CallKeyDown(e);
}

YourUserControlViewModel.cs or YourUserControl.xaml.cs

YourUserControlViewModel.cs 或 YourUserControl.xaml.cs

public void CallKeyDown(KeyEventArgs e) {
  //Do your work
}

There is no need to code in xaml.

无需在 xaml 中编码。

回答by alansiqueira27

What exactly worked for me:

什么对我有用:

Only on window Loaded eventlisten to PreviewKeyDown event:

仅在窗口Loaded 事件上监听 PreviewKeyDown 事件:

void GameScreen_Loaded(object sender, RoutedEventArgs e)
{
     this.PreviewKeyDown += GameScreen_PreviewKeyDown;
     this.Focusable = true;
     this.Focus();
}

void GameScreen_PreviewKeyDown(object sender, KeyEventArgs e)
{
     MessageBox.Show("it works!");   
}

回答by Janez Krnc

I had a similar issue when calling the WPF window out of WinForms. Neither KeyDownor PreviewKeyDownevents were fired.

从 WinForms 调用 WPF 窗口时,我遇到了类似的问题。无论的KeyDownPreviewKeyDown事件被解雇。

var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
    ElementHost.EnableModelessKeyboardInterop(wpfwindow);
    wpfwindow.Show();

However, showing window as a dialog, it worked

但是,将窗口显示为对话框,它起作用了

var wpfwindow = new ScreenBoardWPF.IzbiraProjekti();
    ElementHost.EnableModelessKeyboardInterop(wpfwindow);
    wpfwindow.ShowDialog();

PreviewKeyDownevent fired like a charm for Escapeand Arrowkeys.

PreviewKeyDown事件就像EscapeArrow键的魅力一样被激发。

void MainWindow_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            switch (e.Key)
            {
                case Key.Escape:
                    this.Close();
                    break;
                case Key.Right:
                    page_forward();
                    break;
                case Key.Left:
                    page_backward();
                    break;
            }
        }

Hope this works.

希望这有效。

回答by dE Source

You can simply use the Weindow_KeyDownevent.

您可以简单地使用该Weindow_KeyDown事件。

here is an example

这是一个例子

private void Window_KeyDown(object sender, KeyEventArgs e)
        {
            // ... Test for F1 key.
            if (e.Key == Key.F1)
            {
                this.Title = "You pressed F1 key";
            }
        }

don't forget to add the Weindow_KeyDownattribute to the Window tag

不要忘记将Weindow_KeyDown属性添加到 Window 标签

<Window x:Class="WpfApplication25.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525"
        KeyDown="Window_KeyDown">
</Window>

回答by Kevin

If you don't want to attach to the window's event, add an event for visible changed of your UserControl or Page:

如果您不想附加到窗口的事件,请为 UserControl 或 Page 的可见更改添加一个事件:

IsVisibleChanged="Page_IsVisibleChanged"

Then in code behind:

然后在后面的代码中:

private void Page_IsVisibleChanged(object sender, DependencyPropertyChangedEventArgs e)
        {
            if(this.Visibility == Visibility.Visible)
            {
                this.Focusable = true;
                this.Focus();
            }
        }

Now your event KeyDown would be fired if you press any key.

现在,如果您按任意键,您的事件 KeyDown 将被触发。

回答by Leo Kolezhuk

An alternative to the solution of @Daniel, that doesn't require you to add an event handler for the entire Window, is to do the following:

不需要为整个窗口添加事件处理程序的@Daniel 解决方案的替代方法是执行以下操作:

public partial class MyControl : UserControl{

public MyControl()
{
   MouseEnter+= MouseEnterHandler;
   MouseLeave+= MouseLeaveHandler;
}

protected void MouseEnterHandler(object sender, MouseEventArgs e)
{
   var view = sender as MyControl;
   view.KeyDown += HandleKeyPress;
   view.KeyUp += HandleKeyReleased;
   view.Focus();
}

protected void MouseLeaveHandler(object sender, MouseEventArgs e)
{
   var view = sender as MyControl;
   view.KeyDown -= HandleKeyPress;
   view.KeyUp -= HandleKeyReleased;
}

protected void HandleKeyPress(object sender, KeyEventArgs e)
{
    // What happens on key pressed
}

protected void HandleKeyReleased(object sender, KeyEventArgs e)
{
    // What happens on key released
}

}

This way you can have different handles of the same event in different parts of the view. The handle is local and specific to the control, you don't add handles to the global window.

通过这种方式,您可以在视图的不同部分拥有同一事件的不同句柄。句柄是局部的并且特定于控件,您无需向全局窗口添加句柄。