为什么 KeyUp 和 KeyDown 事件变得越来越慢?

7

我正在使用WPF窗口上的KeyUpKeyDown事件。

此窗口的CompositionTarget_Rendering事件中还有很多调用,创建UI元素并对其进行动画处理。这是由下面的引擎类完成的:

int _Fps;
Stopwatch sw = new Stopwatch();

void CompositionTarget_Rendering_Stats(object sender, EventArgs e)
{
    _Fps++;
    var ms = sw.ElapsedMilliseconds;
    sw.Restart();
    engine.Update(ms / 1000f);
    timeFrames.Add(ms);
}

我注意到,UI元素越多,KeyUpKeyDown事件的响应速度就越慢。当主窗口中有大约1000个UI元素时,无论是按下还是释放键后,control_KeyDown和control_KeyUp的代码执行时间都会延迟半秒左右。如果窗口内的动画也很卡顿,这并不会让我感到惊讶,但事实并非如此。
  • 帧率约为55 fps
  • 动画保持流畅
  • CompositionTarget_Rendering事件中的计算时间不超过20毫秒。
看来只有键盘事件轮询受到了重负的影响。 我的问题是:
  • WPF键盘处理背后的奥秘是什么:为什么在重负条件下它变得卡顿而渲染过程则没有?
  • 如何更好地处理键盘输入并避免这种情况?
编辑: 我根据Andy的评论编写了一个示例。您可以将其复制粘贴到全新WPF应用程序的主窗口中。它会在按下或释放键时更改窗口颜色,并在CompositionTarget_Rendering事件中填充尽可能多的文本框。
public partial class MainWindow : Window
{
    WrapPanel root2;

    public MainWindow()
    {
        InitializeComponent();
        root2 = new WrapPanel();
        root2.Margin = new Thickness(10);
        this.Content = root2;
    }

    private void Window_Loaded(object sender, RoutedEventArgs e)
    {
        for (var i = 0; i < 2000; i++)
            root2.Children.Add(new TextBlock() { Background = Brushes.Yellow });

        this.KeyDown += MainWindow_KeyDown;
        this.KeyUp += MainWindow_KeyUp;
        CompositionTarget.Rendering += CompositionTarget_Rendering;
    }

    void MainWindow_KeyUp(object sender, KeyEventArgs e)
    {
        this.Background = Brushes.Red;
    }

    void MainWindow_KeyDown(object sender, KeyEventArgs e)
    {
        this.Background = Brushes.Green;
    }

    void CompositionTarget_Rendering(object sender, EventArgs e)
    {
        foreach (var child in root2.Children)
            ((TextBlock)child).Text = DateTime.Now.Millisecond.ToString();
    }
}

根据您的机器性能,更改2000以增加文本框数量。当达到一定数量时,CompositionTarget_Rendering触发次数比KeyUp或KeyDown更多。当按下键盘时,可以清晰地看到边框颜色的变化:键已按下,然后触发了多次Composition_Rendering,然后触发了keydown事件,并且边框变为红色。


如果我设置一个新项目,创建一个新表单并添加一个循环来创建50,000个文本框并挂钩主窗口的KeyDown事件,实际上我根本没有注意到任何延迟。这些是嵌套控件吗? - Andy
1
没有控件嵌套。您是否想尝试将一些代码放在CompositionTarget_Rendering事件中,在TextBox中放置一些随机文本?我认为这将反映我所拥有的条件。不过,50000个文本框确实很多。 - Larry
不知道将TextBox放在像ListBox这样的虚拟化容器中是否有帮助? - paparazzo
很遗憾,我认为这与我不知道的某些东西有关,比如事件中的优先级或其他什么。 - Larry
在我的电脑上,该进程会占用大量的CPU资源,难怪一切都变得缓慢。也就是说,不仅关键事件被延迟,该机器上的所有操作都会变得更慢。 - Mare Infinitus
没错。现在我意识到示例可能与我的情况相比过于 UI 密集,尽管 CPU 和帧速率都很好,但按键事件反应缓慢。一旦有时间,我会添加更好的示例代码。 - Larry
1个回答

0
为了回答我的第二个问题,我已经找到了一种替代方法来实现全局键盘钩子:在WPF/C#中使用全局键盘钩子(WH_KEYBOARD_LL)
它的KeyUp和KeyDown事件响应速度很快,即使在CPU和图形负载较重的情况下也是如此。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接