MVVM绑定RelayCommand可以执行到属性吗? [英] MVVM bind RelayCommand CanExecute to a Property?

查看:76
本文介绍了MVVM绑定RelayCommand可以执行到属性吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个计时器和三个按钮来控制它:开始,停止和暂停.
每个按钮都绑定到RelayCommand.
我有一个enum TimerState类型的TimerState属性. (这对于设置各种GUI元素很有用.)
有什么方法可以将RelayCommands的CanExecute功能绑定到TimerState属性吗?
目前,我有3种看起来像这样的方法:
private bool CanStartTimer() { return (TimerState == TimerState.Stopped || TimerState == TimerState.Paused); }
在TimerState setter中,我呼叫

I have a Timer and three buttons to control it: Start, Stop, and Pause.
Each button is bound to a RelayCommand.
I have a TimerState property of type enum TimerState. (This is useful for setting various GUI elements.)
Is there a way to somehow bind the RelayCommands' CanExecute functionality to the TimerState property?
Currently, I have 3 methods that look like this:
private bool CanStartTimer() { return (TimerState == TimerState.Stopped || TimerState == TimerState.Paused); }
In the TimerState setter, I call

StartTimerCmd.RaiseCanExecuteChanged();  

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

Is there a better way bind the CanExecute state of the RelayCommands to a property like TimerState?
Thanks for any insight.

推荐答案

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

I've implemented a class to handle commands, actually it's based on DelegateCommand because i'm using PRISM but it could easily be changed to be used with RelayCommand or any other class implementing ICommand

它可能有错误,我尚未对其进行全面测试,但是在我的情况下它可以正常工作,这里是:

It could have bugs, i've not yet fully tested it, however it works fine in my scenarios, here it is:

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

}

请注意,我的解决方案意味着该命令必须与处理该命令的视图模型连接,并且该视图模型必须实现INotifyPropertyChanged接口.

note that my solution implies that this command has to be wired with the viewmodel that handle it and the viewmodel has to implement the INotifyPropertyChanged interface.

这里有前两个构造函数,因此该命令与DelegateCommand向后兼容,但是第三个是重要的,它接受linq表达式来指定要监视的属性

first two constructor are there so the command is backward compatible with DelegateCommand but the 3rd is the important one, that accepts a linq expression to specify which property to monitor

用法非常简单且易于理解,让我在这里将其与方法一起编写,但是您当然可以创建处理程序方法.假设您有一个名为MyViewModel的ViewModel,它带有两个属性(PropertyX和PropertyY),它们引发propertychanged事件,并在其中的某个位置创建一个SaveCommand实例,如下所示:

the usage is pretty simple and easy to understand, let me write it here with methods but of course you can create your handler methods. Suppose you have have a ViewModel called MyViewModel with two properties (PropertyX and PropertyY) that rise the propertychanged event, and somewhere in it you create an instance of SaveCommand, it would look like this:

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

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

maybe i'll create an article somewhere about this, but this snippet should be clear i hope

这篇关于MVVM绑定RelayCommand可以执行到属性吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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