需要帮助在 UWP 中捕获全局键盘事件



EDIT 4:似乎这仅在通过调试器运行应用程序时发生。 所以这不是一个大问题。

我有很多自定义键盘控件,无论哪个控件具有焦点,都需要触发其中的很多控件。 所以我在我的 MainPage 构造函数中使用以下代码:

public MainPage()
{
Window.Current.CoreWindow.KeyDown += CoreWindow_KeyDown;
Window.Current.CoreWindow.KeyUp += CoreWindow_KeyUp;
}
public void public async void CoreWindow_KeyDown(CoreWindow sender, KeyEventArgs args)
{
// Handle here
}

但我遇到了最糟糕的问题。每当UI线程上发生事情时,它似乎都会严重干扰键盘输入。 就好像它存储了某种积压工作。

例如,如果我转到AutoSuggestBox,它每次按键都会执行大量逻辑,并使用从服务器等加载的图形填充结果,并且我键入" abcd",则通常"d"不会注册。 然后,当我在几秒钟后键入"e"时,"d"将通过,但"e"不会通过。它与硬件无关,它仅在我正在处理的 UWP 应用中执行此操作。

在调试器中,我已经确认这种不需要的行为都是在触发事件之前发生的。当我键入"e"时,VirtualKey正在触发"d"。

此外,键控事件仅在 95% 的时间内触发。CoreWindow.PointerWheelChanged不会出现此问题。使用与键盘相同的处理程序的游戏手柄输入也没有此问题。

UI 线程上的活动越多似乎会增加问题的严重性。

有谁知道如何纠正这种情况?任何类型的解决方法或替代解决方案,或者至少解释可能发生的事情?

编辑: 我尝试为Window.Current.CoreWindow.Dispatcher.ProcessEvents((设置所有4个选项,但没有改进。

编辑2: 似乎我正在为全局事件捕获CoreWindow.Keydown这一事实是不合逻辑的。任何焦点控件上的任何常规 KeyDown 事件也会发生此问题。

编辑3: 我相信我意识到发生了什么,我认为这是一个错误。我的粗略理解是,UWP 键盘输入是沙盒化的,以防止键盘记录器恶意软件或其他东西,因此在原始键输入和 CoreWindow 处理的 VirtualKey 之间有一些较低级别的转换。这很好,但似乎在某些条件下

它不起作用。当快速键盘输入(如键入(期间 UI 线程上有负载时,它有时不会检测到键释放。这就是为什么KeyUp不会像我提到的那样偶尔触发的原因。这也搞砸了KeyDown,因为键处于锁定状态,它认为钥匙被按住了,而实际上并非如此。然后,当下一个密钥版本注册时,CoreWindow 调度程序刷新其队列,结果是它为以前的密钥输入和新密钥输入触发事件。 所以输入'abcd'和'd不会触发。 等待 10 秒钟,然后按"e"。 突然,"d"和"e"都会出现。或者更有可能再次按"d",因为它没有第一次注册,并且将显示双"dd"。 以任何标准来看都是绝对不可接受的行为。

尝试自己重现它的最佳方法是使用 AutoSuggestBox,它执行诸如查询之类的阻止操作,并在您键入时在结果中加载图像缩略图。 请记住,即使是一点点的 UI 负载似乎也会导致它。 即使我将图像异步预加载为流或字节数组,当设置 BitmapImage 源时,它仍然会阻塞 UI 线程 1-2 毫秒。比一帧视频少 1-2 毫秒,因此在视觉上难以察觉,但似乎偶尔无法检测到何时释放键盘键就足够了。

这可能是特定于硬件的内容。我测试了不同的键盘,但没有测试不同的计算机。

你可以试试这个:

public MainPage()
{
InitializeComponent();
Window.Current.CoreWindow.CharacterReceived += CoreWindow_CharacterReceived;
}
private async void CoreWindow_CharacterReceived(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.CharacterReceivedEventArgs args)
{
if (args.KeyCode == 27 ) //Escape
{
// your code here fore Escape key
}
if (args.KeyCode == 13) //Enter
{
// your code here fore Enter key
}
}

您可以将键代码用于其他键盘字符。

在快速键盘输入期间,UI 线程上的活动会阻止 CoreWindow 检测到针对 1803 Spring Creators Update 的密钥释放,至少在某些硬件上是这样。 希望有人知道更好的解决方案,但现在这里有一个部分解决方法:

public void CoreWindow_KeyDown(Windows.UI.Core.CoreWindow sender, Windows.UI.Core.KeyEventArgs args)
{
CoreVirtualKeyStates keyState = sender.GetAsyncKeyState(args.VirtualKey);
if ((int)keyState == 3)
{
// KeyState is locked and pressed.
// Whenever this happens it means the event fired when I actaually pressed this key.
// Handle event.
}
else if ((int)keyState == 2)
{
// KeyState is locked but not pressed. How if it's not caps lock?
// When this happens it's an unwanted duplicate of the last keystroke.
// Do not handle event.
}
else if ((int)keyState == 0)
{
// Key state is None?!? How can a key that isn't currently down fire a KeyDown event?
// This is a phantom delayed rection of a missed event from two keystrokes ago.
// Do not handle event.
}
}

这将防止延迟反应问题,但仍然会因此错过击键。 不理想,但有很大的进步。

这是枚举: https://learn.microsoft.com/en-us/uwp/api/windows.ui.core.corevirtualkeystates

奇怪的状态 3("按下 |锁定"(始终表示正确的键输入不在文档中。

另请注意,"CoreVirtualKeyStates == 0"清楚地表明这是一个错误,至少在我的硬件上是这样。 状态为"无"的密钥如何触发 KeyDown 事件? 没有人的手指那么快。我认为这是 CoreWindow 调度程序刷新其队列,因为它错过了 KeyUp 事件。

最新更新