“纯”MVVM中的MenuItem键盘快捷键?

问题描述:

所有菜单/ contextmenus /工具栏在WPF我用在视图模型代码中声明非常像这样:“纯”MVVM中的MenuItem键盘快捷键?

MenuService.Add(new MenuItem() 
    { 
    Header = "DoStuff", 
    Command = new relayCommand(DoStuff,() => CanDoStuffExecute()) 
    // some more properties like parent item/image/... 
    }); 

的MenuService提供了一个单一的结合点,这是菜单项的分级列表,并获取绑定到实际的菜单的ItemsSource在xaml中。

这工作得很好,现在我想以相同的便捷方式添加键盘快捷键。 理想的菜单项会得到System.Windows.Input.KeyGesture类型的属性,所以我可以简单地写

Shortcut = new KeyGesture(Key.D, ModifierKeys.Control) 

这将导致该项目的命令后击中拥有菜单的窗口Ctrl + d被调用,而这也将导致菜单中自动显示“Ctrl + D”。

但是我迷失在这里:我想通过数据绑定来设置MenuItem.InputBindings集合,但它是只能获取的。无论如何,我怎样才能将物品放入其中?还是有一个MVVM框架已经支持这样的事情?我在键盘快捷键上找到的大多数q &都是关于通过xaml设置快捷方式的,这没有任何帮助。

更新

搜索“relaycommand VS routeduicommand和‘relaycommand keygesture’等确实揭示了足够的信息来与工作虽然哈克解决方案。在那里肯定还有其他更好的方法,但目前这对我来说是非常低的优先事项,并且完美地完成了这项工作。我添加了两个属性,这样MenuItem类:

//Upon setting a Gesture, the original command is replaced with a RoutedCommand 
//since that automatically gives us proper display of the keyboard shortcut. 
//The RoutedCommand simply calls back into the original Command. 
//It also sets the CommandBinding property, which still has to be added to the 
//CommandBindingCollection of either the bound control or one of it ancestors 
public InputGesture Gesture 
{ 
    set 
    { 
    var origCmd = Command; 
    if(origCmd == null) 
     return; 
    var routedCmd = new RoutedCommand(Header, 
     typeof(System.Windows.Controls.MenuItem), 
     new InputGestureCollection { value }); 
    CommandBinding = new CommandBinding(routedCmd, 
     (sender, args) => origCmd.Execute(args.Parameter), 
     (sender, args) => { args.CanExecute = origCmd.CanExecute(args.Parameter); }); 
    Command = routedCmd; 
    } 
} 

//CommandBinding created when setting Gesture 
public CommandBinding CommandBinding { get; private set; } 

所以这让我问原来的功能(即添加在代码中的键盘快捷键,他们是很容易配置等)。剩下的就是注册命令绑定。目前,只需将它们全部添加到Application.Current.MainWindow.CommandBindings中即可完成。

这实际上并不符合“答案”(我无法明确添加评论) - 但我建议您在做什么,而不是WPF中的预期方法。您正在使用Windows Forms的方式(以及许多其他工具包) - 在代码中定义UX。你尽可能地做到了,但现在你已经遇到了一堵砖墙:关键手势纯粹是UX,绝对不能在代码隐藏中指定。外观(作为视图模型的函数)以及用户与它的交互(发出给定命令的方式)都是针对XAML定义的。

属性值和命令用于您的视图模型,以便您可以将此视图模型用于其他视图,还可以轻松地为其创建单元测试。如何在视图模型中实现键盘快捷键有助于可测试性?并且为了在其他视图中使用,可以争辩说实际的快捷方式可能不适用于新的视图,因此这不属于这些视图。当然你可能有其他的原因 - 但我建议你可以考虑在XAML中定义这些。

- 增加,响应你的comment-

你说得对 - 我已经看到了极力避免任何代码和清盘不必要的钝一些相当大的WPF UX项目。我试图用任何一种方法产生一个工作结果,并且尽可能简单。

这是一个简单创建MenuItem的示例代码片段。

<Menu x:Name="miMain" DockPanel.Dock="Top"> 
    <MenuItem Command="{Binding Path=MyGreatCommand}" Header="DoSomething"/> 

创建菜单。在这里,MyGreatCommand是一个ICommand,它只是视图模型的一个属性。我一般把一个DockPanel内,处理StatusBar

要分配的关键姿势..

<Window.InputBindings> 
    <KeyBinding Key="X" Modifiers="ALT" Command="{Binding Path=MyGreatCommand}"/> 

但是,既然你提到你已经寻找答案,发现只有XAML - 我假设你已经尝试过这种方法。我已经使用RoutedUICommand而不是用户定义的ICommand s,以在标题文本中获得很好的右对齐的按键手势,但是我还没有找到如何做到这一点。如果你坚持用代码创建命令和键盘手势,你可能需要创建RoutedUICommand

为什么你想要在XAML以外设置按键手势?

如果您希望在某些国家您的视图模型中占据主导地位的一些菜单项仅出现,那么你就可以在菜单项的Visibility属性(可以包含其他的菜单项)绑定到CollapsedVisible

+0

你能提供一些代码示例吗?你的意思是我应该在xaml中定义完整的菜单吗?或者只是快捷方式?我想看看..我需要像全局的'工具'菜单,插件可以随意添加子菜单 - 甚至可以在xaml中完成吗?同样它也可以,它会让xaml代码乱扔垃圾,不是吗?现在我需要添加一个菜单命令就是所示的代码。这是尽可能简短和方便,它可以得到和它的工作美丽。顺便说一下,你的砖墙在两个方向上工作:我还没有看到一个大规模的应用程序,没有在代码中定义单个UX部分。这是一个神话。 – stijn 2014-04-16 08:21:34

+0

你是对的 - 我曾经看到一些相当大的WPF UX项目,他们努力避免任何代码 - 并且不必要的沉默。我尝试只使用任何一种方法,最简单。 这里是一个示例代码片段(赦免缺乏中文 - 我不确定如何格式化这个网站).. '

JamesWHurst 2014-04-16 10:48:15
+1

编辑你的答案,粘贴代码,选择它,然后单击*代码示例*按钮。 – stijn 2014-04-16 10:55:21