‘纯’MVVM中的MenuItem键盘快捷键?

我在wpf中使用的所有菜单/上下文菜单/工具栏都在ViewModel代码中声明,非常类似于:

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

MenuService提供单个绑定点,它是MenuItem的分层列表,并绑定到xaml中的实际Menu的ItemsSource。

这非常有效,现在我想以同样方便的方式添加键盘快捷键。 理想情况下,MenuItem将获得System.Windows.Input.KeyGesture类型的属性,所以我可以简单地写

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

这将导致在拥有菜单的窗口中按Ctrl + D时调用项目的命令,这也将导致在菜单中自动显示“Ctrl + D”。

但是我迷失在这里:我想通过数据绑定设置MenuItem.InputBindings集合,但它是get-only。 我怎么能把物品放进去呢? 或者是否有一个MVVM框架已经支持这样的东西? 我在键盘快捷键上找到的大多数q&a都是关于通过xaml设置快捷方式,这没有任何帮助。

更新

搜索’relaycommand vs routeduicommand和’relaycommand keygesture’等确实揭示了足够的信息来提出一个工作虽然hacky解决方案。 肯定有其他更好的方法,但目前这对我来说是超低优先级,并完美地完成工作。 我向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; } 

所以这给出了我原来要求的function(即在代码中添加键盘快捷键,它们很容易配置等)。 剩下的就是注册命令绑定。 目前只需将所有这些内容添加到Application.Current.MainWindow.CommandBindings即可完成此操作。

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

属性值和命令适用于您的视图模型,因此您可以将此视图模型重用于其他视图,还可以轻松地为其创建unit testing。 如何在视图模型中实现键盘快捷键有助于提高可测试性? 为了在其他视图中使用,可以认为实际的快捷方式可能不适用于新视图,因此这不属于那些视图。 当然,您可能还有其他原因 – 但我建议您可以考虑在XAML中定义这些原因。

– 已添加,以回应您的评论 –

你是对的 – 而且我已经看到了一些相当大的WPF UX项目,这些项目努力避免任何代码 – 并且不必要地钝化了。 我试着用任何一种方法产生一个工作结果,就像我能得到它一样简单。

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

   

这创建了菜单。 在这里, MyGreatCommand是一个ICommand ,它只是视图模型上的一个属性。 我通常将它放在DockPanel ,以处理StatusBar等。

分配关键手势..

   

但是,既然你提到你已经搜索了答案并且只找到了XAML – 我假设你已经尝试过这种方法。 我使用了RoutedUICommand而不是用户定义的ICommand ,在头文本中获得了良好的右对齐键手势,但我还没有找到如何做到这RoutedUICommand 。 如果您坚持在代码中创建所有命令和键手势,则可能必须创建RoutedUICommand

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

如果您希望某些菜单项仅在某些状态在视图模型中占据时显示,则可以将菜单项(可以包含其他菜单项)的Visibility属性绑定到CollapsedVisible