在“纯”菜单项MVVM键盘快捷方式? [英] MenuItem keyboard shortcuts in 'pure' MVVM?

查看:114
本文介绍了在“纯”菜单项MVVM键盘快捷方式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所有菜单/ contextmenus /工具栏在WPF我使用视图模型代码的声明非常像这样的:

  MenuService.Add (新菜单项()
{
标题=DoStuff,
命令=新relayCommand(DoStuff,()=> CanDoStuffExecute())
//一些属性,如父项/图片/ ...
});



的MenuService提供单一结合点,这是菜单项的分级列表并获取结合到实际的菜单的的ItemsSource在XAML。



这工作得很好,现在我想在同一个便捷的方式添加键盘快捷键。
理想的MenuItem将得到类型的属性 System.Windows.Input.KeyGesture 这样我就可以简单地写

 快捷方式=新KeyGesture(Key.D,ModifierKeys.Control)

,这将导致在该项目的命令被调用时击中拥有该菜单窗口Ctrl + D和这也将导致在菜单自动显示Ctrl + D键。



不过我在这里失去了:我想通过设置在数据绑定集合MenuItem.InputBindings但它获得只。我怎样才能得到项目到它呢?或者是有一个已经支持这样的一个框架MVVM?最Q&放大器;一个我的键盘快捷键找到的所有关于设置通过XAML,这是没有帮助的快捷键



更新

搜索'relaycommand VS routeduicommand和relaycommand keygesture'等没有透露足够的信息来与工作虽然哈克的解决方案。肯定有其他更好的出路在那里,但目前这是超低的优先级对我来说,完美做这项工作。我添加了两个属性MenuItem类是这样的:

  //设置后的手势,原来的命令替换为的RoutedCommand 
//因为这会自动为我们提供了键盘快捷键的正确显示。
//的的RoutedCommand简单地调用回原来的命令。
//它还将设置属性的CommandBinding,仍然有被添加到
两种绑定控件或它的一个// CommandBindingCollection祖上
公共InputGesture手势
{

{
VAR origCmd =命令;
如果(origCmd == NULL)
的回报;
变种routedCmd =新的RoutedCommand(头,
typeof运算(System.Windows.Controls.MenuItem),
新InputGestureCollection {值});
=的CommandBinding新的CommandBinding(routedCmd,
(发件人,参数)=> origCmd.Execute(args.Parameter),
(发件人,参数)=> {args.CanExecute = origCmd .CanExecute(args.Parameter);});
命令= routedCmd;当设置
}
}

//创建的CommandBinding手势
公众的CommandBinding的CommandBinding {搞定;私人集; }



所以,这给我问原来的功能(即添加键盘快捷键在代码中他们很容易配置等)。所有剩下的就是注册commandbindings。目前,这是通过增加他们都Application.Current.MainWindow.CommandBindings简单地完成。


解决方案

这不实际上有资格作为一个'答案'(我不能够明显添加评论) - 但我建议你在做什么,是不是在WPF预期的方法。你这样做的Windows窗体的方法(和许多其他工具包) - 确定代码的UX。你得到尽可能你做了,但现在你会碰到南墙:关键手势是纯粹UX,绝对不是在指定的代码隐藏。外观(视视图模型的函数),和用户的交互与它(使一个给定的命令发生的方式)是用于XAML定义



属性值,和命令是您的视图模型,这样就可以重新使用此视图模型为其他视图,也容易为它创建单元测试。将如何实现在视图模型的键盘快捷键帮助可测性?而对于其他视图的使用,人们可以说,实际的快捷方式可能并不适用于新的观点,所以这是不是哪里的归属。你可能当然有其他的原因 - 但我建议你可以考虑只在XAML中定义这些



- 增加,以应对您的评论 -



您说得很对 - 我已经看到了极力避免任何代码,并结束了不必要的钝一些相当大的WPF UX项目。我尽量只使用任何方法产生一个工作的结果,是因为我能得到它的那样简单。



下面是一个简单的创建一个菜单项示例代码段..

 <菜单X:NAME =miMainDockPanel.Dock =评出的> 
<菜单项命令={绑定路径= MyGreatCommand}标题=DoSomething的/>

这将创建菜单。在这里, MyGreatCommand 的ICommand 了,简直是在视图模型的属性。我一般地方内的 DockPanel中,处理状态栏



要分配的关键姿态。

 < Window.InputBindings> 
<键绑定键为X修饰符=ALT命令={绑定路径= MyGreatCommand}/>



不过,既然你提到你已经寻找答案,发现只有XAML - 我想你'已经已经尝试过这种方法。我已经使用 RoutedUICommand 的S代替用户定义的的ICommand S,得到那个漂亮的右对齐键手势标题文本,但我还没有找到如何做到既。如果你坚持在创建命令和密钥的手势都在代码中,你可能必须创建 RoutedUICommand 秒。



为什么你想设置其它的键手势比你的XAML?



如果您希望在某些状态视图中占据主导地位的一些菜单项只出现-model,那么你可以一个菜单项(其中可以包含其他菜单项)的能见度属性绑定到折叠可见


All menus/contextmenus/toolbars I use in wpf are declared in ViewModel code pretty much like this:

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

The MenuService provides a single binding point which is a hierarchical list of MenuItem and gets bound to the actual Menu's ItemsSource in xaml.

This works very well and now I'd like to add keyboard shortcuts in the same convenient way. Ideally MenuItem would get a property of type System.Windows.Input.KeyGesture so I can simply write

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

which would result in the Command of the item being called upon hitting Ctrl+D in the window that owns the menu, and which would also lead to automatically display "Ctrl+D" in the menu.

However I'm lost here: I wanted to set the MenuItem.InputBindings collection via databinding but it is get-only. How can I get items into it anyway? Or is there an MVVM framework that already supports something like this? Most q&a I found on keyboard shortcuts are all about setting the shortcuts through xaml, which is of no help.

Update

Searching for 'relaycommand vs routeduicommand and 'relaycommand keygesture' etc did reveal enough information to come up with a working though hacky solution. There are definitely other and better ways out there, but at the moment this is ultra low priority for me and does the job perfectly. I added two properties to the MenuItem class like this:

//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; }

So this gives the functionality I asked for originally (ie adding keyboard shortcuts in code where they are easily configurable etc). All that is left is to register the commandbindings. At the moment this is done simply by adding all of them to Application.Current.MainWindow.CommandBindings.

解决方案

This doesn't actually qualify as an 'answer' (I'm not able to add a comment evidently) - but I'd suggest that what you're doing, is not the intended method in WPF. You're doing this the Windows Forms way (and as in many other toolkits) - defining your UX in code. You got as far as you did, but now you've run into a brick wall: the key gestures are purely UX, definitely not to be specified in code-behind. The appearance (as a function of the view-model), and the user's interaction with it (ways of making a given command happen) are for the XAML definition.

Property values, and Commands are for your view-model, so that you can reuse this view-model for other views, and also easily create unit-tests for it. How would implementing your keyboard shortcuts in the view-model help the testability? And for use in other views, one could argue that the actual shortcuts might not apply to a new view, so that is not where those belong. You may have other reasons of course - but I'd suggest you might consider just defining these in XAML.

-Added, in response to your comment-

You're quite right - and I've seen some rather large WPF UX projects that tried hard to avoid any code-and wound up unnecessarily obtuse. I try to just use whichever approach yields a working result, and is as simple as I can get it.

Here is a sample snippet that simply creates the MenuItem..

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

That creates the menu. Here, MyGreatCommand is an ICommand, and is simply a property on the view-model. I generally place that within a DockPanel, to handle the StatusBar, etc.

To assign the key gesture..

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

However, since you mentioned that you've already searched for answers and found only XAML - I assume you've already tried this approach. I have used RoutedUICommands instead of user-defined ICommands, to get that nice right-aligned key-gesture in the header text, but I haven't found how to do both. If you insist upon creating the commands and key-gestures all in code, you may have to create RoutedUICommands.

Why are you wanting to set the key-gestures in other than your XAML?

If you want some menu-items to appear only when certain states hold sway within your view-model, then you can bind the Visibility property of a menu-item (which can contain other menu-items) to Collapsed or Visible.

这篇关于在“纯”菜单项MVVM键盘快捷方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆