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

时间:2008-12-07 16:07:08

标签: wpf keydown

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

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

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

13 个答案:

答案 0 :(得分:34)

附加到窗口的事件

加载控件后,使用KeyDown附加到Window的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
}

答案 1 :(得分:26)

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

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

当然,考虑到它后来这已经足够了,但我百分百肯定这将至少让10个人中的5个人惊讶:)疯狂的小东西叫WPF ......

喝彩!

答案 2 :(得分:21)

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

另一个可能有用的选择是使用Snoop in CodePlex之类的东西来确定事件发生的位置。

答案 3 :(得分:12)

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

答案 4 :(得分: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中编码。

答案 5 :(得分:3)

究竟对我有用:

仅在窗口已加载的事件上收听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!");   
}

答案 6 :(得分:1)

从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 Arrow 键的魅力一样被触发。

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

希望这有效。

答案 7 :(得分:1)

@Daniel解决方案的一种替代方案,它不需要您为整个Window添加事件处理程序,请执行以下操作:

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
}

}

这样,您可以在视图的不同部分具有相同事件的不同句柄。该句柄是本地的,特定于该控件,您无需向全局窗口添加句柄。

答案 8 :(得分:0)

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

答案 9 :(得分:0)

您可以简单地使用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>

答案 10 :(得分:0)

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

IsVisibleChanged="Page_IsVisibleChanged"

然后在后面的代码中:

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

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

答案 11 :(得分:0)

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

答案 12 :(得分:-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 evens。