WPF:如何在键*事件中检测按键重复?

9

注意: e.IsRepeat 已被确认可以工作,问题的存在是因为我在 Ubuntu 到 Windows 的远程桌面模式下使用了

我找到了一个解决这个远程桌面问题的方法:

  1. 在 Ubuntu 中禁用键盘重复功能。
  2. 在主机 Windows 中:启用带有“打开重复键和慢键”的 FilterKeys。
  3. 使用 regedit 软件前往 HKEY_CURRENT_USER\Control Panel\Accessibility\Keyboard Response
    1. AutoRepeatDelay, AutoRepeatRate, 以及 Last Valid Delay, Last Valid Repeat 设置为足够小的值。
    2. 禁用 FilterKeys 并重新启用以刷新注册表更改。

如何在 KeyUp/KeyDown (或 PreviewKeyDown/PreviewKeyUp) 事件中检测按键的重复次数?

以下是我的测试案例:

    public Window1() {
        InitializeComponent();

        this.KeyDown += new KeyEventHandler(Window1_KeyDown);
        this.KeyUp += new KeyEventHandler(Window1_KeyUp);
    }

    void Window1_KeyUp(object sender, KeyEventArgs e) {
        if (e.Key == Key.D) {
            Console.WriteLine("DOWN: key: {0}, rep{1}, togg{2}, dow{3}, up{4}", e.Key, e.IsRepeat, e.IsToggled, e.IsDown, e.IsUp);
        }
    }

    void Window1_KeyDown(object sender, KeyEventArgs e) {
        if (e.Key == Key.D) {
            Console.WriteLine("UP: key: {0}, rep{1}, togg{2}, dow{3}, up{4}", e.Key, e.IsRepeat, e.IsToggled, e.IsDown, e.IsUp);
        }
    }

如果我按下字母D并在一段时间后松开,屏幕上会出现以下输出结果
// Note: Here I press D-key down.
UP: key: D, repFalse, toggTrue, dowTrue, upFalse
DOWN: key: D, repFalse, toggTrue, dowFalse, upTrue
UP: key: D, repFalse, toggFalse, dowTrue, upFalse
DOWN: key: D, repFalse, toggFalse, dowFalse, upTrue
UP: key: D, repFalse, toggTrue, dowTrue, upFalse
DOWN: key: D, repFalse, toggTrue, dowFalse, upTrue
UP: key: D, repFalse, toggFalse, dowTrue, upFalse
DOWN: key: D, repFalse, toggFalse, dowFalse, upTrue
UP: key: D, repFalse, toggTrue, dowTrue, upFalse
DOWN: key: D, repFalse, toggTrue, dowFalse, upTrue
UP: key: D, repFalse, toggFalse, dowTrue, upFalse
DOWN: key: D, repFalse, toggFalse, dowFalse, upTrue
UP: key: D, repFalse, toggTrue, dowTrue, upFalse
DOWN: key: D, repFalse, toggTrue, dowFalse, upTrue
UP: key: D, repFalse, toggFalse, dowTrue, upFalse
DOWN: key: D, repFalse, toggFalse, dowFalse, upTrue
UP: key: D, repFalse, toggTrue, dowTrue, upFalse
DOWN: key: D, repFalse, toggTrue, dowFalse, upTrue
UP: key: D, repFalse, toggFalse, dowTrue, upFalse
DOWN: key: D, repFalse, toggFalse, dowFalse, upTrue
UP: key: D, repFalse, toggTrue, dowTrue, upFalse
DOWN: key: D, repFalse, toggTrue, dowFalse, upTrue
UP: key: D, repFalse, toggFalse, dowTrue, upFalse
DOWN: key: D, repFalse, toggFalse, dowFalse, upTrue
// Note: Here I release D-key.

显然e.IsRepeat总是false,所以它是无用的。我还注意到有时第一个事件也是toggFalse、dowTrue,所以不能将其用作模式。

我还发现可以使用巧妙的时间方式来检测重复,但肯定有本机的方法来实现此功能。


进一步观察后,我发现我无法重复您的实验。无论是使用文本框还是写入控制台,无论是在调试或发布模式下,无论是在KeyDown还是PreviewKeyDown中,它总是设置了“IsRepeat”。您的代码中是否有未显示的部分? - Abel
Abel,不,我只是为此创建了一个空项目。 - Ciantic
你说你正在使用远程桌面。你能否尝试不用它?很可能,RD + Ubuntu 在起作用。 - Abel
你找到了一个很好的解决方法!干得好(有趣的是,在Ubuntu中禁用它会使其通过不同的方式在RD中可用)。 - Abel
2个回答

10

为什么不使用本地功能?我在窗口和两个文本框上添加了PreviewKeyDown事件。按住第二个文本框中的一个键,得到了以下输出:

Repeat: False, key: D
Repeat: True, key: D
Repeat: True, key: D
Repeat: True, key: D
Repeat: True, key: D
Repeat: True, key: D
Repeat: True, key: D
Repeat: True, key: D

这是我使用的代码:

private void Grid_PreviewKeyDown(object sender, KeyEventArgs e)
{
    textBox1.Text += String.Format(
        "Repeat: {0}, key: {1}\n", 
        e.IsRepeat, 
        e.Key);
}

更新:我删除了所有的代码(其中包含其他测试的垃圾代码),并将您的代码原封不动地粘贴进去。它在控制台中给出了以下输出,因此我想我们应该查看其他原因...

UP: key: D, repFalse, toggTrue, dowTrue, upFalse
UP: key: D, repTrue, toggTrue, dowTrue, upFalse
UP: key: D, repTrue, toggTrue, dowTrue, upFalse
UP: key: D, repTrue, toggTrue, dowTrue, upFalse
UP: key: D, repTrue, toggTrue, dowTrue, upFalse
UP: key: D, repTrue, toggTrue, dowTrue, upFalse

什么是正确的事件?就像我所说,如果我将事件更改为Preview*,在我的计算机上设置e.IsRepeat也会失败。 - Ciantic
注意到的区别:没有。我尝试了你尝试过的(但是只是在写完这篇文章后)。我使用 .NET 3.5 SP1 和一个普通的 WPF 项目。添加了一个窗口,然后加入了你的代码。 - Abel
刚刚测试了一下,没有文本框也能正常工作。然后用了你的代码进行测试,也正常工作。嗯...是.NET版本不够新还是你的系统上有键盘记录器? - Abel
我需要更深入地研究我的系统,希望能从其他人那里获得更多的结果。我使用的是Windows 7 RC1和3.5SP1(我想是SP1,但3.5肯定没错)...而且我是通过Ubuntu远程桌面连接的。 - Ciantic
等等!远程桌面可能在这里起作用了!客户端系统(Ubuntu)必须通过RD客户端将键事件发送到Win7服务器。尝试从控制台运行相同的代码(即坐在实际计算机上),您很可能会发现它再次正常工作。 - Abel

0

当 keydown 事件触发时,设置一个变量以跟踪按下的键,然后执行您的操作,然后忽略该键的进一步事件。当 keyup 触发时,清除该变量。您可能需要一个列表来跟踪多个键。


嗯,对了,看到KeyUp事件总是被触发,所以那不会自然地起作用。 - Ciantic
但是你描述的方法只有在使用巧妙的时机控制时才能起作用,但那简直是疯狂的...正如我在问题中指出的,必须有其他方法来检测按键重复,而不是将密集的按键弹起/按下事件堆叠在一起。 - Ciantic
不需要聪明的计时,只需在正确的事件中使用IsRepeat,一切都在那里。 - Abel
抱歉,我对WPF还比较新,所以如果按住一个键不放,keyup事件会触发吗?这似乎是一个重大问题! - Mike B

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