44

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

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

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

4

13 回答 13

52

附加到窗口的事件

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

XAML

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

背后的代码

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
}
于 2013-02-15T02:40:15.597 回答
29

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

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

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

干杯!

于 2009-06-11T11:10:38.260 回答
24

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

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

于 2008-12-07T16:26:32.647 回答
13

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

于 2011-12-09T16:09:15.543 回答
9

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

提到的以下代码@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
}

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

这里我建议,

YourWindow.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 或 YourUserControl.xaml.cs

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

无需在 xaml 中编码。

于 2014-09-16T04:07:35.030 回答
5

什么对我有用:

仅在窗口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!");   
}
于 2014-03-09T13:13:47.430 回答
2

从 WinForms 调用 WPF 窗口时,我遇到了类似的问题。KeyDown或PreviewKeyDown事件均未触发

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

但是,将窗口显示为对话框,它可以工作

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

PreviewKeyDown事件就像Escape箭头键的魅力一样被触发。

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;
            }
        }

希望这有效。

于 2016-10-10T21:23:20.260 回答
1

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

这是一个例子

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

不要忘记将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>
于 2019-07-24T11:06:36.043 回答
1

如果您不想附加到窗口的事件,请为您的用户控件或页面的可见更改添加一个事件:

IsVisibleChanged="Page_IsVisibleChanged"

然后在后面的代码中:

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

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

于 2019-08-25T18:09:12.770 回答
1

@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
}

}

这样,您可以在视图的不同部分拥有同一事件的不同句柄。句柄是本地的并且特定于控件,您无需将句柄添加到全局窗口。

于 2019-08-26T11:14:11.147 回答
0

尝试使用 ElementHost.EnableModelessKeyboardInterop 启用您的窗口。就我而言,它在 Keydown 事件中捕获箭头键。

于 2019-01-25T11:28:13.420 回答
0

我解决了这个问题,只是在视图中的一个按钮上调用 .Focus() 并确保窗口已完成所有元素的加载

于 2021-07-29T07:40:29.547 回答
-1

这是经过测试并且绝对有效的。

  Private Sub textbox1_PreviewKeyDown(sender As Object, e As KeyEventArgs) Handles textbox1_input.PreviewKeyDown
        If (e.Key = Key.Down) Then
            MessageBox.Show("It works.")
        End If
    End Sub

'detect key state directly with something like this below
 Dim x As KeyStates = System.Windows.Input.Keyboard.GetKeyStates(Key.Down)

PreviewKeyDown 是大多数人错过的。将 PreviewKeyDown 视为键盘事件的整体观察者,(但如果我没记错,则不会影响它们)并且 KeyDown 事件正在您当前所在的控件或类的范围内被监听。

于 2016-01-03T00:16:51.113 回答