Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

MVVM ICommand, Lambda Expression for CanExecuteChanged

0.00/5 (No votes)
1 Mar 2013 2  
Parse CanExecute to detect when CanExecuteChanged should be called.

Introduction

Consider the following scenario:

public class SampleViewModel
{
    public ICollectionView Customers { get; set; }
    public ICommand DoSomethingWithCurrentCustomer { get; set; }
    public SampleViewModel()
    {
        DoSomethingWithCurrentCustomer = new DelegateCommand(execute: () =>
        {
            // To Do
        }, canExecute: () => Customers.CurrentItem != null);
    }
}

Main Problem

We bind the command of one button in the View to this command. The button will remain disabled while canexecute returns false. But to get this working we should do another step which I don't consider entirely appropriate.

Customers.CurrentChanged += delegate
{
    DoSomethingWithCurrentCustomer.RaiseCanExecuteChanged();
};

Or we should use Command Manager, which is accessible only in .NET. But I've provided another solution with the power of Expression trees. I've implemented an ICommand as below:

public class DelegateCommand<T> : ICommand
{
    private readonly Action<T> _execute;
    private readonly Func<T, Boolean> _canExecute;
    public DelegateCommand(Action<T> execute, Expression<Func<T, Boolean>> canExecuteExpression = null)
    {
        if (execute == null)
            throw new ArgumentNullException("execute");
        _execute = execute;
        if (canExecuteExpression != null)
        {
            _canExecute = canExecuteExpression.Compile(); // Expression will compiled to code which is invokable
        }
    }

As you can see, I have two parameters for a constructor, Action to execute, Expression to Parse and Execute.

Next Step

We should parse the provided lambda expression for two reasons:

  1. Find all Properties
  2. Find all Notifier Objects

The property in our sample is CurrentItem and the notifier is Customers. We should listen to Customers' notifier's PropertyChanged event handler. If any change happens to CurrentItem, we should call the RaiseCanExecuteChanged method.

I understand that it's not enough, but it will come in handy for more than 90% of all scenarios. For others we can call RaiseCanExecuteChanged manually. But remember: we may not repeat ourselves. To find props and notifiers I've developed an expression visitor.

public sealed class PropObjectFinder : ExpressionVisitor
{
    public IList<String> PropNames { get; set; }

    public IList<Object> Objects { get; set; }

    public PropObjectFinder(Expression expression)
    {
        PropNames = new Collection<String>();

        Objects = new Collection<Object>();

        Visit(expression);
    }

    protected override Expression VisitMember(MemberExpression node)
    {   // To Find PropNames
        MemberInfo member = node.Member;

        if (member is PropertyInfo && !PropNames.Contains(member.Name))
        {
            var prop = (PropertyInfo)member;

            PropNames.Add(prop.Name);
        }

        return base.VisitMember(node);
    }

    protected override Expression VisitConstant(ConstantExpression node)
    {   // To Find Object which maybe are notifier !
        var value = node.Value;

        if (value != null && !Objects.Contains(value))
            Objects.Add(value);

        return base.VisitConstant(node);
    }
}

VisitMember will provide all the used props, methods, and then we use prop names. We use this class in our delegate command class.

To find all notifiers from objects and all important props, we should do something like below:

var propFinder = new PropObjectFinder(canExecute);

_AllPropNamesToWatch = propFinder.PropNames.ToList();

var allObjects = propFinder.Objects;

foreach (var obj in allObjects)
{
    FindAllNotifyableObjects(obj, propFinder.PropNames);
}

FindAllNotifyableObjects's code is in the source code which is attached. After that we can start to listen to all Notifires! 

_AllNotifiers.ForEach(notifierObject =>
    {
        notifierObject.PropertyChanged += notifyableObject_PropertyChanged;
    });   
{
    if (_AllPropNamesToWatch.Contains(e.PropertyName))
        RaiseCanExecuteChanged();
}

And finally at RaiseCanExecuteChanged we have:

public virtual void RaiseCanExecuteChanged()
{
    var tempEventHandler = CanExecuteChanged;
    if (tempEventHandler != null)
    {
        System.Windows.Deployment.Current.Dispatcher.BeginInvoke(() =>
        {
            tempEventHandler(this, new EventArgs());
        });
    }
}

It's good to go. After that you may call the RaiseCanExecuteChanged method manually for special purposes, which aren't supported, but most of the scenarios are covered. And you can use it easily in .NET, Silverlight, WinRT, Windows Phone.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here