【问题标题】:How to detect key down/up when a second key is already held down当第二个键已经被按下时如何检测键向下/向上
【发布时间】:2012-07-04 02:10:53
【问题描述】:

在我的应用程序中,我允许用户使用ProcessCmdKey 按住右箭头键 滚动电影。现在我想让用户能够在需要时增加滚动速度。理想情况下,用户应该能够按住右箭头键,然后当他决定提高速度时,他应该在不释放右箭头键的情况下同时按住Shift 键,当他决定恢复正常速度时,他只需松开 Shift 键。因此,滚动速度的差异应仅由 Shift 键 修饰符给出,该修饰符应添加或删除到 右箭头键 压力。

我尝试了这样的代码,但没有成功(在这个测试示例中,我的表单中有一个简单的标签):

int count = 0;
bool keyRightDown = false;

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if (keyData == Keys.Right)
    {
        keyRightDown = true;
        count++;
        label.Text = "count = " + count.ToString();
        return true;
    }
    if (keyData == (Keys.Shift | Keys.ShiftKey) && keyRightDown)
    {
        count += 10;
        label.Text = "count = " + count.ToString();
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

protected override bool ProcessKeyMessage(ref Message m)
{
    if ((Keys)m.WParam == Keys.Right)
    {
        if (m.Msg == 0x101) // KEYUP
        {
            keyDown = false;
            return true;
        }
    }
    return base.ProcessKeyMessage(ref m);
}

当用户将 Shift 键 添加到 右箭头 时,keyData 不包含我所期望的 (Keys.Shift | Keys.Right),而是包含 (Keys.Shift | Keys.ShiftKey)。但是这个问题仍然可以通过布尔值keyRightDown 来解决。主要问题是,当用户通过此时仅按下 右箭头 来释放 Shift 键 时,没有其他对 ProcessCmdKeyProcessKeyMessage 的调用被触发。我怎样才能实现我的目标?

【问题讨论】:

  • 只是猜测 - 是否可以在您的班级中按住“右箭头键向下”信息并检查当按下“shift”时这是否成立?您必须处理“key up”事件来重置该变量,但也许可以这样做?
  • 是的,这是一个用于管理何时按下 Shift 键的选项,但我仍然无法检测到何时释放 Shift 键(同时始终按住右箭头)。我已经编辑了文本以包含您的解决方案
  • 一些更多的研究倾向于让我假设你想做的事情是不可能的(以一种很好的方式)。一旦注册了 Shift 的 KeyUp,持续触发的 KeyDown 事件似乎确实停止了。我现在想到的一个问题是,为什么不简单地使用右箭头键以速度 x 前进,而使用 shift 键以速度 x * 10 前进?无论如何,您必须按多个键,这样您就不会花费太多时间来解决这个“并发按键”问题?

标签: c# winforms keyboard


【解决方案1】:

我发现的唯一方法是结合调用 GetKeyState API 函数 (user32.dll) 和 Timer。这是它在测试应用程序上的工作原理:

System.Windows.Forms.Timer keyManagerTimer = new System.Windows.Forms.Timer();
int count = 0;

public Form1()
{
    InitializeComponent();

    this.keyManagerTimer.Tick += (s, e) => ProcessKeys();
    this.keyManagerTimer.Interval = 25;
}

protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
{
    if ((keyData & Keys.Right) != 0)
    {
        keyManagerTimer.Enabled = true;
        return true;
    }
    return base.ProcessCmdKey(ref msg, keyData);
}

private void ProcessKeys()
{
    bool isShiftKeyPressed = IsKeyPressed(Keys.ShiftKey);
    bool isRightKeyPressed = IsKeyPressed(Keys.Right);

    if (isRightKeyPressed && !isShiftKeyPressed)
    {
        count++;
    }
    else if (isRightKeyPressed && isShiftKeyPressed)
    {
        count += 10;
    }
    label.Text = "count = " + count.ToString();
}

public static bool IsKeyPressed(Keys key)
{
    return BitConverter.GetBytes(GetKeyState((int)key))[1] > 0;
}

[DllImport("user32")]
private static extern short GetKeyState(int vKey);

然后在我的真实代码中,我在有视频的ControlLeave 事件上禁用Timer。可能另一种解决方案是使用 IMessageFilter(请参阅here)。

【讨论】:

    【解决方案2】:

    一种可能的解决方案是将所有可能的按键存储在一个 bool 数组中,然后检查 bool 数组是否为真。在按住按钮的情况下,您可以将按钮键设置为 true,然后在释放键时将其设置为 false。当我需要检查多个按键时,我通常倾向于使用此选项。

    【讨论】:

    • 我仍然没有看到适合我的情况的解决方案:当您按住一个键并按下然后释放另一个键时(因此您在起点处仅按住第一个键),不会处理其他关键消息。
    • 是的,但例如。你首先按下左键,布尔数组键现在都是假的,除了 keys[left] (其中 left 是你自己选择的整数)。现在你按右键,keys[right] 设置为 true。当您按下键时,您将其设置为 true,并在“键释放”事件中将值设置为 false。它们将保持真实,直到您在关键发布事件中明确表示它们是错误的。在您的逻辑阶段,您检查是否为真键并根据这些键执行操作。
    • 除非我遗漏了什么,如果你在我发布的代码中尝试你的解决方案(这真的很简单,你只需要一个带有标签的表单)它不会起作用,因为不会有任何键按下第二个键并释放后要处理的消息。我认为它工作的唯一方法是启动一个新的任务/计时器,以便不断检查 bool 数组中的按键,但我不太喜欢这个解决方案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-03-31
    • 1970-01-01
    • 2021-09-23
    • 1970-01-01
    • 2017-06-22
    • 2015-05-25
    • 2010-11-09
    相关资源
    最近更新 更多