【发布时间】:2010-09-02 09:40:34
【问题描述】:
是否可以在 WPF 中为一系列按键定义键绑定,例如 Visual Studio 中的快捷方式,例如Ctrl + R, Ctrl + A 在当前解决方案中运行所有测试
据我所知,我只能使用元素绑定单个组合键,例如 Ctrl + S。我可以使用它来绑定序列还是必须手动处理按键才能做到这一点?
【问题讨论】:
标签: wpf xaml keyboard-shortcuts binding key-bindings
是否可以在 WPF 中为一系列按键定义键绑定,例如 Visual Studio 中的快捷方式,例如Ctrl + R, Ctrl + A 在当前解决方案中运行所有测试
据我所知,我只能使用元素绑定单个组合键,例如 Ctrl + S。我可以使用它来绑定序列还是必须手动处理按键才能做到这一点?
【问题讨论】:
标签: wpf xaml keyboard-shortcuts binding key-bindings
您需要通过覆盖Matches 方法来创建自己的InputGesture。
类似的东西:
public class MultiInputGesture : InputGesture
{
public MultiInputGesture()
{
Gestures = new InputGestureCollection();
}
public InputGestureCollection Gestures { get; private set; }
private int _currentMatchIndex = 0;
public override bool Matches(object targetElement, InputEventArgs inputEventArgs)
{
if (_currentMatchIndex < Gestures.Count)
{
if (Gestures[_currentMatchIndex].Matches(targetElement, inputEventArgs))
{
_currentMatchIndex++;
return (_currentMatchIndex == Gestures.Count);
}
}
_currentMatchIndex = 0;
return false;
}
}
它可能需要更多的东西,比如忽略某些事件(例如,KeyDown 事件之间的KeyUp 事件不应该重置_currentMatchIndex),但你明白了......
【讨论】:
@ThomasLevesque 的答案大部分是正确的,但不涉及重复键。 (请注意,按住 Ctrl 键会导致生成键重复事件。)如果用户在序列中停顿,超时也很有用。这是我正在使用的:
public class MultiKeyInputGesture : InputGesture {
private const int MAX_PAUSE_MILLIS = 1500;
private InputGestureCollection mGestures = new InputGestureCollection();
private DateTime mLastWhen = DateTime.Now;
private int mCheckIdx;
public MultiKeyInputGesture(KeyGesture[] keys) {
Debug.Assert(keys.Length > 0);
// Grab a copy of the array contents.
foreach (KeyGesture kg in keys) {
mGestures.Add(kg);
}
}
public override bool Matches(object targetElement, InputEventArgs inputEventArgs) {
if (!(inputEventArgs is KeyEventArgs)) {
// does this actually happen?
return false;
}
DateTime now = DateTime.Now;
if ((now - mLastWhen).TotalMilliseconds > MAX_PAUSE_MILLIS) {
mCheckIdx = 0;
}
mLastWhen = now;
if (((KeyEventArgs)inputEventArgs).IsRepeat) {
// ignore key-repeat noise (especially from modifiers)
return false;
}
if (!mGestures[mCheckIdx].Matches(null, inputEventArgs)) {
mCheckIdx = 0;
return false;
}
mCheckIdx++;
if (mCheckIdx == mGestures.Count) {
mCheckIdx = 0;
inputEventArgs.Handled = true;
return true;
}
return false;
}
}
我通过在 XAML 中定义 RoutedUICommand 来使用它:
<Window.Resources>
<RoutedUICommand x:Key="MyCommand" Text="My Command"/>
</Window.Resources>
照常引用<Window.CommandBindings> 和<MenuItem>。然后,在窗口构造函数中,我这样做:
RoutedUICommand ruic = (RoutedUICommand)FindResource("MyCommand");
ruic.InputGestures.Add(
new MultiKeyInputGesture(new KeyGesture[] {
new KeyGesture(Key.H, ModifierKeys.Control, "Ctrl+H"),
new KeyGesture(Key.C, ModifierKeys.Control, "Ctrl+C")
}) );
我发现this forum post 很有帮助。
您需要将明确的InputGestureText 添加到任何MenuItem,除非您想尝试链接论坛帖子中的DisplayString hack。
注意:键手势处理程序“吃掉”完成手势的键。如果您有多个处理程序,并且用户尝试连续使用两个多键序列(例如,Ctrl+H、Ctrl+C 紧跟 Ctrl+H、Ctrl+D),则第二个处理程序不会重置当按下 Ctrl+C 时。相反,它会在第二个 Ctrl+H 到达时重置,并且会错过组合。实际行为取决于调用处理程序的顺序。我目前正在通过定义一个在找到匹配项时触发的静态事件并订阅所有实例来处理这个问题。
更新:另外一件需要注意的事情:<Window.CommandBindings> 中的项目顺序很重要。如果您有一个在 Ctrl+C 上触发的 Copy 处理程序,它必须出现在 Ctrl+H、Ctrl+C 的多键手势之后的列表中。
【讨论】:
<KeyBinding x:Name="mykeybinding" Gesture="CTRL+P" Key="E"
Command="mycommand"/>
这似乎对我有用,我必须按 ctrl+P+E 才能执行“mycommand”
基于 http://msdn.microsoft.com/en-in/library/system.windows.input.keybinding.aspx
【讨论】: