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

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

问题描述

我正在尝试提高WPF业务应用程序的响应速度,以便当用户在屏幕之间等待"服务器响应后出现新屏幕时,他们仍然可以输入数据.我能够对事件进行排队(在后台面板上使用PreviewKeyDown事件处理程序),但是一旦加载后,我就很难将刚出队的事件扔回到新面板上.特别是新面板上的Te​​xtBoxes不会拾取文本.我尝试过引发相同的事件(在捕获它们时将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 space 2 space 3 tab 4 space 5 space 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天全站免登陆