C# - 如何在不捕获 Shift 键的情况下捕获 Shift+Del 组合事件?
C# - How to capture Shift+Del combination event without capturing Shift key only?
我有一个文本框和一个列表框。 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 来清除文本框,而不是当时清除列表框。
我只能使用 Shift 和 Del 键,不能使用其他键。
那我该怎么办?
如果您只想依赖 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();
}
}
我个人会使用后一种解决方案。
首先,让我先说明一下,我通常建议不要单独使用 Shift 键来触发除游戏之外的任何桌面软件中的操作。这样做违反了 Principle of Least Astonishment:因为 Shift 在正常打字过程中经常使用来修改其他键的含义,所以用户习惯于期望按下它不会自己做 任何事情,当然没有破坏性。因此,如果用户不经意地按下 Shift 并发现他刚刚丢失了他的项目列表,他可能会非常不愉快地感到惊讶,特别是如果要恢复损坏是困难或乏味的.
但是,你在评论中说你在这件事上别无选择,所以话虽如此,这里有一个可能的解决方案。
为了能够区分 Shift 和 Shift + Del 组合,我们需要捕获 KeyDown
和 KeyUp
事件并使用从每个事件收集的信息。
在KeyDown
事件中,我们将KeyEventArgs.KeyData
的值保存到私有字段中,但还没有对其执行任何操作。 KeyData
包含表示按下的主键的组合 Keys
标志,以及任何修饰键,如 Ctrl、Shift 或 Alt 同时按下。注意当Shift被自己按下时,它既是主键又是修饰符,所以KeyData
会包含Keys.ShiftKey
(键)与 Keys.Shift
(修饰符)相结合。
private Keys LastKeysDown;
private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
LastKeysDown = e.KeyData;
}
在 KeyUp
事件中,我们查看相对于上次按下的键释放了哪些键。我们正在寻找两个特定的键序列:
- Shift + Del被按下,然后Del被释放而Shift 仍然按住。
- 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
事件。
我有一个文本框和一个列表框。 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 来清除文本框,而不是当时清除列表框。
我只能使用 Shift 和 Del 键,不能使用其他键。
那我该怎么办?
如果您只想依赖 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();
}
}
我个人会使用后一种解决方案。
首先,让我先说明一下,我通常建议不要单独使用 Shift 键来触发除游戏之外的任何桌面软件中的操作。这样做违反了 Principle of Least Astonishment:因为 Shift 在正常打字过程中经常使用来修改其他键的含义,所以用户习惯于期望按下它不会自己做 任何事情,当然没有破坏性。因此,如果用户不经意地按下 Shift 并发现他刚刚丢失了他的项目列表,他可能会非常不愉快地感到惊讶,特别是如果要恢复损坏是困难或乏味的.
但是,你在评论中说你在这件事上别无选择,所以话虽如此,这里有一个可能的解决方案。
为了能够区分 Shift 和 Shift + Del 组合,我们需要捕获 KeyDown
和 KeyUp
事件并使用从每个事件收集的信息。
在KeyDown
事件中,我们将KeyEventArgs.KeyData
的值保存到私有字段中,但还没有对其执行任何操作。 KeyData
包含表示按下的主键的组合 Keys
标志,以及任何修饰键,如 Ctrl、Shift 或 Alt 同时按下。注意当Shift被自己按下时,它既是主键又是修饰符,所以KeyData
会包含Keys.ShiftKey
(键)与 Keys.Shift
(修饰符)相结合。
private Keys LastKeysDown;
private void MainForm_KeyDown(object sender, KeyEventArgs e)
{
LastKeysDown = e.KeyData;
}
在 KeyUp
事件中,我们查看相对于上次按下的键释放了哪些键。我们正在寻找两个特定的键序列:
- Shift + Del被按下,然后Del被释放而Shift 仍然按住。
- 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
事件。