【问题标题】:Need Help Capturing Global Keyboard Events in UWP需要帮助在 UWP 中捕获全局键盘事件
【发布时间】:2018-06-30 04:06:29
【问题描述】:

编辑 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”时,args.VirtualKey 正在触发一个“d”。

keyup 事件也只有 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-2ms。比一帧视频少 1-2 毫秒,因此视觉上难以察觉,但似乎偶尔无法检测到何时释放键盘键就足够了。

这可能是特定于硬件的。我测试过不同的键盘,但没有测试过不同的电脑。

【问题讨论】:

  • 我不确定你是否会通过 UWP 应用获得你想要的东西。 UWP 适用于任何 Windows 10 平台;您可以注册到本机桌面 .NET 库,即使您让它工作了,您将如何通过这个来完成应用程序挂起/恢复/后台任务?只是一种预感告诉我,这不能按照您想要的方式完成,您需要编写一个标准的 Windows 应用程序来完成它。您仍然可以将应用程序放入应用程序商店,但仅限 PC。我会监控这个,以防我错了;祝你好运。
  • 您的意思是回复其他问题吗?我遇到了 CoreWindow.KeyDown 无法正确触发的问题。我的项目已经开发了 18 个月,它是一个由 UWP、WPF 和 Web 应用程序组成的套件。我对 UWP 应用暂停或恢复没有任何问题。
  • 顺便说一句,再次感谢您向我展示如何从依赖属性更新运行代码。
  • 大声笑,不。我打算在这里回应,但也许我的评论太罗嗦了,没有意义。我想说的是,我不确定 UWP 应用程序是否完全能够始终如一地捕获来自其他应用程序的击键。我可能是错的,因为我从来没有挖过那么远,但我已经用 WPF (tripleg-3.com) 做到了这一点,并且非常成功(很多年前)。并为混乱道歉;也许我是最困惑的人,这会让我的 cmets 变得奇怪。

标签: c# xaml uwp


【解决方案1】:

你可以试试这个:

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

您可以为其他键盘字符使用键码。

【讨论】:

  • 很有趣,所以在它到达 CoreWINdow 之前捕获它,非常有希望我会尝试一下。
  • 事件根本没有触发,我没有使用输入法编辑器。
【解决方案2】:

在快速键盘输入期间 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://docs.microsoft.com/en-us/uwp/api/windows.ui.core.corevirtualkeystates

文档中没有始终表示正确键输入的奇怪状态 3(“按下 | 锁定”)。

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

【讨论】:

    猜你喜欢
    • 2017-12-18
    • 2015-10-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-01
    相关资源
    最近更新 更多