【问题标题】:C# - How to capture Shift+Del combination event without capturing Shift key only?C# - 如何在不捕获 Shift 键的情况下捕获 Shift+Del 组合事件?
【发布时间】:2017-11-22 16:39:57
【问题描述】:

我有一个文本框和一个列表框。 Shift 必须清除列表框,Shift+Del 组合必须清除文本框。

这是我的代码。

private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
    if (e.Shift && e.KeyCode == Keys.Delete) textBox.Clear();
    if (e.Shift) listBox.Items.Clear();
}

这显然是错误的,因为当 Shift 按下时,列表框总是被清除。我需要能够通过按 Shift+Del 来清除文本框,并且当时不清除列表框。

我只需要使用 ShiftDel 键,不能使用其他键。

那我该怎么办?

【问题讨论】:

  • 只是忽略 shift 事件一小段时间,比如 500 毫秒?但有点不清楚你问的是什么。你不能用minimal reproducible example 演示你的问题吗?
  • 哪些事件?你是如何监控键盘的?有代码吗?
  • 非常不清楚您在谈论什么事件以及为什么您甚至单独“捕获”转变......显然您知道转变状态伴随着 KeyDown 事件(bing.com/search?q=c%23+keydown+shift),所以您应该询问别的东西 - 请edit 发帖澄清。
  • 请详细说明您是如何捕获的。通常,您可以检查关键事件附带的修饰符标志,例如 Shift、Ctrl、Alt。您通常不会明确地听 shift 然后听另一个键。
  • 我开始纠正一个答案,但实际上使用 Shift 作为实际键以及其他键的修饰符只是一个非常糟糕的主意。我只是建议您使用除 Shift、Ctrl 或 Alt 以外的任何东西来清除列表框项目。你有没有见过那些用于你正在做的功能的键?您可能没有这样做是有原因的;)

标签: c# winforms key keydown event-capturing


【解决方案1】:

首先,让我先说一下,我通常建议不要单独使用 Shift 键来触发任何桌面软件中的操作,而不是游戏。这样做违反了Principle of Least Astonishment:由于 Shift 在正常打字过程中经常用于修改其他键的含义,因此用户习惯于期望按下它不会做任何事情 本身就有后果,当然没有破坏性。因此,如果用户不经意地按下 Shift 并发现他刚刚丢失了他的项目列表,他可能会感到非常不愉快,特别是如果要撤消损坏是困难或乏味的。

但是,您在 cmets 中说您在这件事上别无选择,所以说,这是一个可能的解决方案。

为了能够单独区分 ShiftShift + Del 组合,我们需要捕获 两个 KeyDownKeyUp 事件并使用从每个事件中收集的信息。

KeyDown 事件中,我们将KeyEventArgs.KeyData 的值保存到一个私有字段,但尚未对其采取任何操作。 KeyData 包含组合的 Keys 标志,代表被按下的主键,以及任何修饰键,如 CtrlShiftAlt 同时被按住。请注意,当 Shift 本身按下时,它既是主键 也是 修饰符,所以 KeyData 将包含 Keys.ShiftKey(键)和 @ 987654330@(修饰符)。

private Keys LastKeysDown;

private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
    LastKeysDown = e.KeyData;
}

KeyUp 事件中,我们查看相对于上次按下的键释放了哪些键。我们正在寻找两个特定的键序列:

  1. Shift + Del 被按下,然后 Del 被释放,同时 Shift 仍然被按住。
  2. Shift 在没有其他键的情况下按下,然后松开。

如果我们找到了我们正在寻找的东西,我们就会启动相应的操作,否则我们什么也不做。

private void MainForm_KeyUp(object sender, KeyEventArgs e)
{
    // Shift + Delete pressed, then Delete released while Shift still held down
    if (LastKeysDown == (Keys.Shift | Keys.Delete) && e.KeyData == LastKeysDown)
        textBox1.Clear();

    // Shift pressed with no other key, then released
    else if (LastKeysDown == (Keys.Shift | Keys.ShiftKey) && e.KeyData == Keys.ShiftKey)
        listBox1.Items.Clear();

    LastKeysDown = Keys.None;
}

请注意,作为最后的任务,我们清除了私有 LastKeysDown 字段。这有助于确保如果我们连续获得两个KeyUp 事件,则只有第一个与前一个KeyDown 配对。但请注意,这并非万无一失:如果您按住一个键的时间足够长,计算机的按键重复将启动并生成额外的KeyDown 事件。

【讨论】:

    【解决方案2】:

    如果你只想依赖Key_Down,你可以使用BackgroundWorker(或者定时器也可以),它会在清除列表之前等待几毫秒。如果在此期间有另一个密钥进入,则取消 BackgroundWorker。您的实现可能如下所示:

    private void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        if (e.Shift && 
            e.KeyCode == Keys.Delete)
        {
            if (backgroundWorker1.IsBusy)
            {
                Trace.WriteLine("cancelling");
                backgroundWorker1.CancelAsync();
            }
            textBox.Clear();
        }
    
        if (e.Shift && 
            e.KeyCode == Keys.ShiftKey && 
            !backgroundWorker1.IsBusy)
        {
            backgroundWorker1.RunWorkerAsync();
        }
    
    }
    
    private void  backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
    {
        e.Result = false;
        // wait 2 seconds (that is a bit long, adjust accordingly)
        Thread.Sleep(2000); // HACK, use a proper timer here
        // tell RunWorkerCompleted if we're good to clear
        e.Result = !backgroundWorker1.CancellationPending;
    }
    
    // this gets called on the UI thread
    private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        Trace.WriteLine(String.Format("{0} - {1}", e.Cancelled, e.Result));
        if ((bool) e.Result)
        {
            listBox.Items.Clear();
        }
    }
    

    如果您还可以利用 KeyUp 事件,解决方案会变得更加容易:

    private bool onlyShift = false; // keeps shift state
    
    private void Form1_KeyDown(object sender, KeyEventArgs e)
    {
        // becomes true if this is the shift key pressed
        onlyShift = e.Shift && e.KeyCode == Keys.ShiftKey;
    
        if (e.Shift && 
            e.KeyCode == Keys.Delete)
        {
            textBox.Clear();
        }               
    }
    
    private void Form1_KeyUp(object sender, KeyEventArgs e)
    {
        // was the shiftkey the last one in keydown AND
        // are we releasing the shiftkey now
        if (onlyShift && e.KeyCode == Keys.ShiftKey)
        {
             listBox.Items.Clear();
        }
    }
    

    我个人会使用后一种解决方案。

    【讨论】:

    • 感谢您的帮助。还有一个问题。 e.Shift 和 (e.KeyCode == Keys.ShiftKey) 有什么区别。他们不是平等的吗?
    • @J.Doe 很好,是的。我没有费心进一步优化。
    猜你喜欢
    • 2011-09-19
    • 2011-08-08
    • 2015-09-20
    • 1970-01-01
    • 2010-10-23
    • 2023-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多