다음을 통해 공유


An ICommand with IsEnabled

ICommand is a simple interface with three members – Execute, CanExecute, and CanExecuteChanged (more on those here). You can write your own implementations of that interface, one for each command, but that gets a bit heavyweight. So there are several implementations of ICommand that are pluggable and re-usable, like DelegateCommand, RelayCommand, and RoutedCommand.

 

One tricky part of implementing ICommand is the CanExecuteChanged event, because you have to know when to raise it. (The easy alternative is to raise it whenever the CommandManager.RequerySuggested event is raised, but you need to use that with caution, because it can happen frequently and get expensive.)

 

Really, though, CanExecute and CanExecuteChanged look like a typical Foo/FooChanged property/event pair that we use in data binding. The reason that CanExecute isn’t actually a property, though, is that it takes an optional parameter. But if you’re not using that parameter, a property would work just as well.

 

So I’ve been playing with an ICommand implementation that takes a delegate for the Execute method (just like DelegateCommand and RelayCommand), but also has an IsEnabled property that wraps CanExecute/CanExecuteChanged. And since it’s all a DependencyObject, you can put a binding on IsEnabled.

 

I wasn’t sure what to call it though. The key is that it has an IsEnabled property, but DelegateCommandWithIsEnabled didn’t sound good. And it turns out that Enabled-able isn’t a word. The closest thing I could come up with that had “able” in it was AbilityCommand. Good enough for a blog post.

 

Anyway, here’s what the public members look like:

 

public class AbilityCommand : DependencyObject, ICommand

{

    public AbilityCommand(

        Action<object> executeDelegate,

        Binding isEnabledBinding );

    public AbilityCommand( Action<object> executeDelegate )

        : this( executeDelegate, null ) { }

   

    public bool IsEnabled {get; set; }

  public static readonly DependencyProperty IsEnabledProperty ...;

}

 

Note that you can set IsEnabled however you want, including with a binding, and in fact the constructor lets you pass in the Binding to use. AbilityCommand’s ICommand.CanExecute simply returns IsEnabled. And any time IsEnabled’s value changes, AbilityCommand raises ICommand.CanExecuteChanged.

 

And here it is in practice. In this snippet, the (View)Model object is the first page of a wizard-type view (with a “Next” button on it). The SubmitCommand property binds the command’s IsEnabled to the object’s IsValid property:

 

public class FirstPage : INotifyPropertyChanged

{

    public FirstPage()

    {

        SubmitCommand = new AbilityCommand(

            (x) => OnSubmit(),

            new Binding("IsValid") { Source=this } );

    }

    public ICommand SubmitCommand { get; private set; }

    private void OnSubmit()

    {

        // ...

    }

    // INotifyPropertyChanged fires for this IsValid property if

    // either the "FirstProperty" or "SecondProperty" property is updated

    public bool IsValid

    {

        get

        {

            return !String.IsNullOrEmpty(FirstProperty)

                && !String.IsNullOrEmpty(SecondProperty);

        }

    }

And finally, here’s the whole AbilityCommand:

 

public class AbilityCommand : DependencyObject, ICommand

{

    Action<object> _executeDelegate;

    public AbilityCommand( Action<object> executeDelegate )

        : this( executeDelegate, null ) { }

   

    public AbilityCommand(

        Action<object> executeDelegate,

        Binding isEnabledBinding )

    {

        _executeDelegate = executeDelegate;

  if( isEnabledBinding != null )

            BindingOperations.SetBinding(this, IsEnabledProperty, isEnabledBinding);

    }

    void ICommand.Execute(object parameter)

    {

        _executeDelegate(parameter);

    }

    bool ICommand.CanExecute(object parameter)

    {

        return IsEnabled;

       

    }

    public event EventHandler CanExecuteChanged;

    private void RaiseCanExecuteChanged()

    {

        if( CanExecuteChanged != null )

            CanExecuteChanged( this, new EventArgs() );

    }

    public bool IsEnabled

    {

        get { return (bool)GetValue(IsEnabledProperty); }

        set { SetValue(IsEnabledProperty, value); }

    }

    public static readonly DependencyProperty IsEnabledProperty =

        DependencyProperty.Register("IsEnabled", typeof(bool), typeof(AbilityCommand),

                            new PropertyMetadata(OnIsEnabledChanged));

    static void OnIsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs args)

    {

        (d as AbilityCommand).RaiseCanExecuteChanged();

    }

}

Comments

  • Anonymous
    March 23, 2009
    PingBack from http://blog.a-foton.ru/index.php/2009/03/24/an-icommand-with-isenabled/

  • Anonymous
    March 24, 2009
    Thank you for submitting this cool story - Trackback from DotNetShoutout

  • Anonymous
    March 24, 2009
    Exactly what I was looking for.  Binding makes the IsEnabled a lot cleaner than sprinkling the VM with RequerySuggested calls.

  • Anonymous
    March 25, 2009
    You've been kicked (a good thing) - Trackback from DotNetKicks.com

  • Anonymous
    September 27, 2012
    Good one Mike.