更轻松的MVVM ViewModel RoutedUICommand用法?

时间:2013-07-09 00:13:51

标签: wpf xaml mvvm menu command

我结合了Josh Smith的Using RoutedCommands with a ViewModel in WPF,vbfox的Using CommandBindings in MVVM和我自己的旋转来指定RoutedUICommand Text和KeyGesture,同时在XAML中声明了CommandBinding。

优点:

  • 无需像Smith一样明确引用ViewModel。他确实:
xmlns:vm="clr-namespace:VMCommanding.ViewModel" 
...
<UserControl.CommandBindings>
  <jas:CommandSinkBinding 
    Command="vm:CommunityViewModel.KillAllMembersCommand" />
</UserControl.CommandBindings>

我正在使用MVVM Light Toolkit's Locator类来间接获取ViewModel,所以我不想这样做。此外,由于这是在底层创建一个正常的CommandBinding,您不能使用Bindings来指定命令(CommandBinding不从DependencyObject继承)。 vbfox通过创建自己的CommandBindings类来解决这个问题。

  • 无需为vbfox等单独的ICommand创建。他确实:
<f:Mvvm.CommandBindings>
  <f:MvvmCommandBindingCollection>
    <f:MvvmCommandBinding
      Command="f:MyRoutedCommands.SomeRoutedCommand"
      Target="{Binding MyCommandInViewModel}"
      CanExecuteChangedSuggestRequery="True" />
  </f:MvvmCommandBindingCollection>
</f:Mvvm.CommandBindings>

所以你必须在某个地方(在你的View目录中?)创建一个RoutedUICommand,在你的ViewModel中创建一个单独的ICommand。

相反,我“只”必须在我的ViewModel中实现ToggleReadComand:

_commandSink.RegisterCommand(_toggleReadCommand, 
                             p => CanToggleRead, 
                             p => ToggleRead());
...
public static readonly RoutedUICommand _toggleReadCommand =
       new RoutedUICommand();
public static RoutedUICommand ToggleReadCommand
{
  get { return _toggleReadCommand; }
}

private bool CanToggleRead
{
  get { return Files.CurrentItem != null; }
}

private void ToggleRead()
{
  ToggleFileInfoFields(f => f.Read = !f.Read);
}

我这样使用它:

<cmd:Mvvm.CommandBindings>
  <cmd:MvvmCommandBindingCollection>
    <cmd:MvvmCommandBinding Command="{Binding ToggleReadCommand}" 
                            Gesture="Toggle _Read|Ctrl+R" />
  </cmd:MvvmCommandBindingCollection>
</cmd:Mvvm.CommandBindings>
...
<MenuItem Header="_View">
  <MenuItem Command="{Binding ToggleReadCommand}" />
</MenuItem>

我获得了使用RoutedUICommands的所有优点:Command文本自动显示在我的MenuItem中以及快捷键(包括禁用支持),我的菜单项UI(或任何其他绑定到ToggleReadCommand的控件)在一个中指定在我的视图中通过XAML放置(没有更多单独的CommandBindings和InputBindings),但我仍然可以将我的命令代码放在它所属的ViewModels中。

现在这是我的问题。首先,我希望能够做到这一点(模拟如何声明正常的Window.CommandBindings):

<cmd:Mvvm.CommandBindings>
  <cmd:MvvmCommandBinding Command="{Binding ToggleReadCommand}" 
                          Gesture="Toggle _Read|Ctrl+R" />
</cmd:Mvvm.CommandBindings>

如何避免必须显式指定MvvmCommandBindingCollection?

My Gesture支持有效,但我在Visual Studio 2012 XAML编辑器的Gesture="Toggle _Read|Ctrl+R"下面得到了“对象引用没有设置为对象的实例”错误指示?我将以下内容添加到vbfox的MvvmCodeBinding.cs:

public static readonly DependencyProperty GestureProperty = 
  DependencyProperty.Register(
    "Gesture",
    typeof(String),
    typeof(MvvmCommandBinding),
    new PropertyMetadata(null, OnGestureChanged));

static void OnGestureChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
  ((MvvmCommandBinding) d).OnGestureChanged((String) e.NewValue);
}

private string _gesture;
void OnGestureChanged(String newValue)
{
  _gesture = newValue;
  var command = _commandSinkBinding.Command as RoutedUICommand;
  if (command == null)
    return;

  CreateGesture(command, newValue);
}

private static void CreateGesture(RoutedUICommand command, string gestureStr)
{
  if (string.IsNullOrEmpty(gestureStr) || command == null)
    return;

  var fields = gestureStr.Split(new char[] {'|'}, 2);
  if (fields.Length == 1)
    command.Text = gestureStr;
  else {
    command.Text = fields[0];
    var gesture = fields[1];
    var kgc = new KeyGestureConverter();
    var kg = kgc.ConvertFromString(gesture) as System.Windows.Input.KeyGesture;
    command.InputGestures.Add(kg);
  }
}

[Bindable(true)]
public String Gesture
{
  get { return (String) GetValue(GestureProperty); }
  set { SetValue(GestureProperty, value); }
}

(我还将vbfox的CommandBinding _commandBinding更改为Smith的CommandSinkBinding _commandSinkBinding,如果在Command之前创建了Gesture,则从OnCommandChanged()调用CreateGesture()。如何摆脱XAML编辑器错误指示?

最后,这有点像弗兰肯斯坦怪物。我确信有各种重复的功能。 vbfox明确表示他提出了他的代码以避免使用Smith的VMCommanding。另外,我开始使用MVVM Light Toolkit。我现在的东西只是一个“概念证明”,表明我的方法可行。

有人知道是否有更好的方法来完成已在某处实施的所有内容?我希望能够在XAML中声明我的RoutedUICommand(通过Binding)在一行中也设置Text和Gesture(不需要单独的CommandBinding&amp; InputBinding)。我希望实际的RoutedUICommands在我的ViewModel中实现。

如果没有,我可以通过仔细查看这三个软件包并进行一些重构来消除大量的kruft。

0 个答案:

没有答案