WPF 排队和重播关键事件 [英] WPF enqueue and replay key events

查看:14
本文介绍了WPF 排队和重播关键事件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试提高 WPF 业务应用程序的响应能力,以便当用户在"屏幕之间等待服务器响应后出现新屏幕时,他们仍然可以输入数据.我可以对事件进行排队(在后台面板上使用 PreviewKeyDown 事件处理程序),但是一旦加载,我就很难将我出列的事件扔回新面板.特别是新面板上的 TextBoxes 没有拾取文本.我尝试过引发相同的事件(在捕获它们时将 Handled 设置为 true,在再次引发它们时将 Handled 设置为 false)创建新的 KeyDown 事件、新的 PreviewKeyDown 事件、执行 ProcessInput、在面板上执行 RaiseEvent、将焦点设置在右侧TextBox 和在 TextBox 上做 RaiseEvent,很多事情.

I'm trying to improve the responsiveness of a WPF business application so that when users are "between" screens waiting for a new screen to appear after a server response, they can still be entering data. I'm able to queue the events (using a PreviewKeyDown event handler on background panel) but then I'm having difficulties just throwing the events I dequeue back at the new panel once it's loaded. In particular TextBoxes on the new panel are not picking up the text. I've tried raising the same events (setting Handled to true when capturing them, setting Handled to false when raising them again) creating new KeyDown events, new PreviewKeyDown events, doing ProcessInput, doing RaiseEvent on the panel, setting the focus on the right TextBox and doing RaiseEvent on the TextBox, many things.

看起来应该是很简单,但我想不通.

It seems like it should be really simple, but I can't figure it out.

以下是我尝试过的一些事情.考虑一个名为 EventQ 的 KeyEventArgs 队列:

Here are some of the things I've tried. Consider a Queue of KeyEventArgs called EventQ:

这里有一件事是行不通的:

Here's one thing that doesn't work:

        while (EventQ.Count > 0)
        {
            KeyEventArgs kea = EventQ.Dequeue();
            tbOne.Focus(); // tbOne is a text box
            kea.Handled = false;
            this.RaiseEvent(kea);
        }

这是另一个:

        while (EventQ.Count > 0)
        {
            KeyEventArgs kea = EventQ.Dequeue();
            tbOne.Focus(); // tbOne is a text box
            var key = kea.Key;                    // Key to send
            var routedEvent = Keyboard.PreviewKeyDownEvent; // Event to send
            KeyEventArgs keanew = new KeyEventArgs(
                Keyboard.PrimaryDevice,
                PresentationSource.FromVisual(this),
                0,
                key) { RoutedEvent = routedEvent, Handled = false };

            InputManager.Current.ProcessInput(keanew);
        }

还有一个:

        while (EventQ.Count > 0)
        {
            KeyEventArgs kea = EventQ.Dequeue();
            tbOne.Focus(); // tbOne is a text box
            var key = kea.Key;                    // Key to send
            var routedEvent = Keyboard.PreviewKeyDownEvent; // Event to send
            this.RaiseEvent(
              new KeyEventArgs(
                Keyboard.PrimaryDevice,
                PresentationSource.FromVisual(this),
                0,
                key) { RoutedEvent = routedEvent, Handled = false }
            );
        }

我注意到的一件奇怪的事情是,当使用 InputManager 方法 (#2) 时,确实会出现空格.但是普通的文本键没有.

One strange thing I've noticed is that when using the InputManager method (#2) spaces do appear. But normal text keys do not.

推荐答案

当我做一些研究时,我发现了相同的资源,所以我认为你在回答中所做的非常有效.

The same resources turned up for me when I did some research, so I think what you do in your answer is pretty valid.

我查看并找到了另一种使用 Win32 API 的方法.我不得不引入一些线程和小的延迟,因为由于某种原因,关键事件没有按正确的顺序重播.总的来说,我认为这个解决方案更简单,而且我还想出了如何包含修饰键(通过使用 Get/SetKeyboardState 函数).大写字母可以使用,键盘快捷键也可以使用.

I looked on and have found another way of doing it, using the Win32 API. I had to introduce some threading and small delays, because for some reason the key events were not replayed in the correct sequence without that. Overall I think this solution is easier though, and I also figured out how to include modifier keys (by using the Get/SetKeyboardState function). Uppercase is working, and so should keyboard shortcuts.

启动演示应用程序,按 1 空格 2 空格 3 制表符 4 空格 5 空格 6 键,然后单击按钮会产生以下内容:

Starting the demo app, pressing the keys 1 space 2 space 3 tab 4 space 5 space 6, then clicking the button produces the following:

Xaml:

<UserControl x:Class="WpfApplication1.KeyEventQueueDemo"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             mc:Ignorable="d" d:DesignHeight="300" d:DesignWidth="300" >

    <StackPanel>
        <TextBox x:Name="tbOne" Margin="5,2" />
        <TextBox x:Name="tbTwo" Margin="5,2" />
        <Button x:Name="btn" Content="Replay key events" Margin="5,2" />
    </StackPanel>
</UserControl>

后面的代码:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Interop;

namespace WpfApplication1
{
    /// <summary>
    /// Structure that defines key input with modifier keys
    /// </summary>
    public struct KeyAndState
    {
        public int Key;
        public byte[] KeyboardState;

        public KeyAndState(int key, byte[] state)
        {
            Key = key;
            KeyboardState = state;
        }
    }

    /// <summary>
    /// Demo to illustrate storing keyboard input and playing it back at a later stage
    /// </summary>
    public partial class KeyEventQueueDemo : UserControl
    {
        private const int WM_KEYDOWN = 0x0100;

        [DllImport("user32.dll")]
        static extern bool PostMessage(IntPtr hWnd, UInt32 Msg, int wParam, int lParam);

        [DllImport("user32.dll")]
        static extern bool GetKeyboardState(byte[] lpKeyState);

        [DllImport("user32.dll")]
        static extern bool SetKeyboardState(byte[] lpKeyState);

        private IntPtr _handle;
        private bool _isMonitoring = true;

        private Queue<KeyAndState> _eventQ = new Queue<KeyAndState>();

        public KeyEventQueueDemo()
        {
            InitializeComponent();

            this.Focusable = true;
            this.Loaded += KeyEventQueueDemo_Loaded;
            this.PreviewKeyDown += KeyEventQueueDemo_PreviewKeyDown;
            this.btn.Click += (s, e) => ReplayKeyEvents();
        }

        void KeyEventQueueDemo_Loaded(object sender, RoutedEventArgs e)
        {
            this.Focus(); // necessary to detect previewkeydown event
            SetFocusable(false); // for demo purpose only, so controls do not get focus at tab key

            // getting window handle
            HwndSource source = (HwndSource)HwndSource.FromVisual(this);
            _handle = source.Handle;
        }

        /// <summary>
        /// Get key and keyboard state (modifier keys), store them in a queue
        /// </summary>
        void KeyEventQueueDemo_PreviewKeyDown(object sender, KeyEventArgs e)
        {
            if (_isMonitoring)
            {
                int key = KeyInterop.VirtualKeyFromKey(e.Key);
                byte[] state = new byte[256];
                GetKeyboardState(state); 
                _eventQ.Enqueue(new KeyAndState(key, state));
            }
        }

        /// <summary>
        /// Replay key events from queue
        /// </summary>
        private void ReplayKeyEvents()
        {
            _isMonitoring = false; // no longer add to queue
            SetFocusable(true); // allow controls to take focus now (demo purpose only)

            MoveFocus(new TraversalRequest(FocusNavigationDirection.Next)); // set focus to first control

            // thread the dequeueing, because the sequence of inputs is not preserved 
            // unless a small delay between them is introduced. Normally the effect this
            // produces should be very acceptable for an UI.
            Task.Run(() =>
            {
                while (_eventQ.Count > 0)
                {
                    KeyAndState keyAndState = _eventQ.Dequeue();

                    Application.Current.Dispatcher.BeginInvoke((Action)(() =>
                    {
                        SetKeyboardState(keyAndState.KeyboardState); // set stored keyboard state
                        PostMessage(_handle, WM_KEYDOWN, keyAndState.Key, 0);
                    }));

                    System.Threading.Thread.Sleep(5); // might need adjustment
                }
            });
        }

        /// <summary>
        /// Prevent controls from getting focus and taking the input until requested
        /// </summary>
        private void SetFocusable(bool isFocusable)
        {
            tbOne.Focusable = isFocusable;
            tbTwo.Focusable = isFocusable;
            btn.Focusable = isFocusable;
        }
    }
}

这篇关于WPF 排队和重播关键事件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆