MVVM将RelayCommand CanExecute绑定到属性?

Num*_*er8 3 wpf mvvm-light

我有一个Timer和三个按钮来控制它:Start,Stop和Pause.
每个按钮都绑定到一个RelayCommand.
我有一个类型的TimerState属性enum TimerState.(这对于设置各种GUI元素很有用.)
有没有办法以某种方式将RelayCommands的CanExecute功能绑定到TimerState属性?
目前,我有3个方法,如下所示:
private bool CanStartTimer() { return (TimerState == TimerState.Stopped || TimerState == TimerState.Paused); }
在TimerState setter中,我调用

StartTimerCmd.RaiseCanExecuteChanged();  
Run Code Online (Sandbox Code Playgroud)

有没有更好的方法将RelayCommands的CanExecute状态绑定到TimerState之类的属性?
感谢您的任何见解.

Fab*_*ela 6

我已经实现了一个类来处理命令,实际上它基于DelegateCommand,因为我正在使用PRISM但它可以很容易地被改为与RelayCommand或任何其他实现ICommand的类一起使用

它可能有错误,我还没有完全测试它,但它在我的场景中工作正常,这里是:

public class MyDelegateCommand<TViewModel> : DelegateCommand where TViewModel : INotifyPropertyChanged {
  private List<string> _PropertiesToWatch;

  public MyDelegateCommand(TViewModel viewModelInstance, Action executedMethod)
     : base(executedMethod) {
  }

  public MyDelegateCommand(TViewModel viewModelInstance, Action executedMethod, Func<bool> canExecuteMethod)
     : base(executedMethod, canExecuteMethod) {
  }

  /// <summary>
  /// 
  /// </summary>
  /// <param name="viewModelInstance"></param>
  /// <param name="executedMethod"></param>
  /// <param name="selector"></param>
  public MyDelegateCommand(TViewModel viewModelInstance, Action executedMethod, Func<bool> canExecuteMethod, Expression<Func<TViewModel, object>> propertiesToWatch)
     : base(executedMethod, canExecuteMethod) {

     _PropertiesToWatch = RegisterPropertiesWatcher(propertiesToWatch);
     viewModelInstance.PropertyChanged += PropertyChangedHandler;
  }


  /// <summary>
  /// handler that, everytime a monitored property changes, calls the RaiseCanExecuteChanged of the command
  /// </summary>
  /// <param name="sender"></param>
  /// <param name="e"></param>
  private void PropertyChangedHandler(object sender, PropertyChangedEventArgs e) {
     if (_PropertiesToWatch.Contains(e.PropertyName)) {
        this.OnCanExecuteChanged();
     }
  }

  /// <summary>
  /// giving an expression that identify a propriety or a list of properties, return the property names obtained from the expression
  /// Examples on selector usage
  /// proprietà singola:
  ///   entity => entity.PropertyName
  /// proprietà multiple
  ///   entity => new { entity.PropertyName1, entity.PropertyName2 }
  /// </summary>
  /// <param name="selector"></param>
  /// <returns></returns>
  protected List<string> RegisterPropertiesWatcher(Expression<Func<TViewModel, object>> selector) {
     List<string> properties = new List<string>();

     System.Linq.Expressions.LambdaExpression lambda = (System.Linq.Expressions.LambdaExpression)selector;

     if (lambda.Body is System.Linq.Expressions.MemberExpression) {
        System.Linq.Expressions.MemberExpression memberExpression = (System.Linq.Expressions.MemberExpression)(lambda.Body);
        properties.Add(memberExpression.Member.Name);
     }
     else if (lambda.Body is System.Linq.Expressions.UnaryExpression) {
        System.Linq.Expressions.UnaryExpression unaryExpression = (System.Linq.Expressions.UnaryExpression)(lambda.Body);

        properties.Add(((System.Linq.Expressions.MemberExpression)(unaryExpression.Operand)).Member.Name);
     }
     else if (lambda.Body.NodeType == ExpressionType.New) {
        NewExpression newExp = (NewExpression)lambda.Body;
        foreach (var argument in newExp.Arguments) {
           if (argument is System.Linq.Expressions.MemberExpression) {
              System.Linq.Expressions.MemberExpression mExp = (System.Linq.Expressions.MemberExpression)argument;
              properties.Add(mExp.Member.Name);
           }
           else {
              throw new SyntaxErrorException("Syntax Error, selector has to be an expression that returns a new object containing a list of properties, e.g.: s => new { s.Property1, s.Property2 }");
           }
        }
     }
     else {
        throw new SyntaxErrorException("Syntax Error, selector has to be an expression that returns a new object containing a list of properties, e.g.: s => new { s.Property1, s.Property2 }");
     }

     return properties;
  }
Run Code Online (Sandbox Code Playgroud)

}

请注意,我的解决方案意味着此命令必须与处理它的viewmodel连接,并且viewmodel必须实现INotifyPropertyChanged接口.

前两个构造函数在那里,所以命令向后兼容DelegateCommand但第三个是重要的,它接受一个linq表达式来指定要监视的属性

用法非常简单易懂,让我用方法在这里写,但当然你可以创建你的处理程序方法.假设你有一个名为MyViewModel的ViewModel,它有两个属性(PropertyX和PropertyY),它们引发了propertychanged事件,并且在其中某处你创建了一个SaveCommand实例,它看起来像这样:

this.SaveCommand = new MyDelegateCommand<MyViewModel>(this,
        //execute
        () => {
          Console.Write("EXECUTED");
        },
        //can execute
        () => {
          Console.Write("Checking Validity");
           return PropertyX!=null && PropertyY!=null && PropertyY.Length < 5;
        },
        //properties to watch
        (p) => new { p.PropertyX, p.PropertyY }
     );
Run Code Online (Sandbox Code Playgroud)

也许我会在某处创建一篇关于此的文章,但我希望这个片段应该清楚