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

发布于 2024-07-09 14:10:31 字数 259 浏览 10 评论 0原文

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

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

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

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

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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(14

最单纯的乌龟 2024-07-16 14:10:31

附加到窗口的事件

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

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

Attach to the Window's Event

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

The 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
}
黯然 2024-07-16 14:10:31

我曾经也有过一样的问题。 我有一个窗口,其中托管一个框架,该框架加载/导航到各个页面,而这些页面又只包含 1 个用户控件,其中包含普通控件/元素(标签、超链接等)。

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

当然,事后想一想,这很有道理,但我 100% 确定这会让至少十分之五的人感到惊讶:) 名为 WPF 的疯狂小东西......

干杯!

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).

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.

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...

cheers!

够运 2024-07-16 14:10:31

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

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

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?

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

那些过往 2024-07-16 14:10:31

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

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

少女净妖师 2024-07-16 14:10:31

我提出了一种加强@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中编码。

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

The following code @Doc mentioned will work since the KeyDown routed event is bubbled to the outermost Window. Once Window receives the KeyDown event bubbled from an inner element, Window triggers any KeyDown event-handler registered to it, like this 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

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

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

There is no need to code in xaml.

π浅易 2024-07-16 14:10:31

到底什么对我有用:

仅在窗口 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!");   
}

What exactly worked for me:

Only on window Loaded event listen to PreviewKeyDown event:

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!");   
}
若水微香 2024-07-16 14:10:31

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

IsVisibleChanged="Page_IsVisibleChanged"

然后在后面的代码中:

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

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

If you don't want to attach to the window's event, add an event for visible changed of your UserControl or 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.

ぃ弥猫深巷。 2024-07-16 14:10:31

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

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

但是,将窗口显示为对话框时,它

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

可以像Escape箭头键一样触发PreviewKeyDown事件。

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

希望这有效。

I had a similar issue when calling the WPF window out of WinForms. Neither KeyDown or PreviewKeyDown events were fired.

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();

PreviewKeyDown event fired like a charm for Escape and Arrow keys.

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.

独留℉清风醉 2024-07-16 14:10:31

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

}

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

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:

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.

苹果你个爱泡泡 2024-07-16 14:10:31

您只需使用 Window_KeyDown 事件即可。

这是一个示例:

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

不要忘记将 Window_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>

You can simply use the Window_KeyDown event.

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 Window_KeyDown attribute to the Window tag

<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>
强辩 2024-07-16 14:10:31

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

Try enabling your window with ElementHost.EnableModelessKeyboardInterop. In my case it capture arrow keys in the Keydown event.

乜一 2024-07-16 14:10:31

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

I fixed this issue just calling .Focus() on a button in the view and make sure the the window has finished the loading of all elements

月亮是我掰弯的 2024-07-16 14:10:31

您的 UserControl 有一个初始值设定项。 将处理程序添加到该代码后面。 无需在 XAML 中执行任何操作。

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
        KeyDown += Handle_KeyDown;
    }

    private void Handle_KeyDown(object sender, KeyEventArgs e)
    {
        throw new NotImplementedException();
    }
}

您不需要 onload 事件。 这包含在初始化程序中。 将所有内容保存在一个文件中将使维护变得更加容易。

正如其他人提到的,PreviewKeyDown 可能比 KeyDown 更适合您,具体取决于您的需求。

Your UserControl has an initializer. Add the handler to that code behind. No need to do anything in XAML.

public partial class UserControl1 : UserControl
{
    public UserControl1()
    {
        InitializeComponent();
        KeyDown += Handle_KeyDown;
    }

    private void Handle_KeyDown(object sender, KeyEventArgs e)
    {
        throw new NotImplementedException();
    }
}

You don't need an onload event. That's included in the initializer. And keeping everything in one file will make maintaining it easier.

As others have mentioned, PreviewKeyDown might serve you better than KeyDown, depending on your needs.

热血少△年 2024-07-16 14:10:31

这是经过测试并且肯定有效的。

  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 事件正在您当前所在的控件或类的范围内被监听。

This is tested and defintiely works.

  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 is what most people miss. Think of PreviewKeyDown as an overall observer of keyboard events, (but cannot effect them if im not mistaken) and KeyDown evens are being listened to within the confines of the control or class you are currently in.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文