Click here to Skip to main content
Click here to Skip to main content
Go to top

MVVM ICommand, Lambda Expression for CanExecuteChanged

, 1 Mar 2013
Rate this:
Please Sign up or sign in to vote.
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, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Yasser Moradi DNP
Software Developer (Senior)
Iran (Islamic Republic Of) Iran (Islamic Republic Of)
A senior JavaScript & C# developer who experienced in both back end and front end.
Checkout my blog at http://ysmoradi.wordpress.com/
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
QuestionDependency Tracking PinmemberMichaelLPerry3-Dec-12 2:42 
AnswerSource Code & Reply [modified] PinmemberYasser Moradi DNP3-Dec-12 20:59 
AnswerRe: Dependency Tracking PinmemberDmitri Raiko1-Mar-13 5:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.140916.1 | Last Updated 1 Mar 2013
Article Copyright 2012 by Yasser Moradi DNP
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid