MVVM - Commanding

Introduction
In my previous article (MVVM Design Pattern) I covered the MVVM infrastructure that was lacking commanding. This one completes the previous article by adding commands to the Home Grown MVVM.
How Commanding Works Here
I am pretty sure that Silverlight 5 will have much better support for commands but right now I had to come up with some scheme to support commanding.
The way I 'wired' commands is by 'kidnapping' the events and rewire them so the command action will get invoked.
IMHO some events should remain in the code behind because they are more of a UI functionality than business or data depended functionality. But if I want to be able to do everything via commanding it requires some manipulation and more than just manipulation - certain event information I do not know how to get via XAML binding. Certain actions may require a reference to the control. Other action may need an unrelated data. Based on the above I want my command to deliver 3 objects - the sender, the original event data, and a user (XAML bound) parameter.
I also wanted to be able to bind and specify the Command with a very simple XAML line. No resources etc.
The following is not rocket science, but is not simple if you haven't done it before. If I lose you, go through the code section it may clarify it. In order to not to drawn in details the following description skips and hides few points - I'll cover them later.
So here is how it works: via the XAML I bind a property (attached dependency property) to an instance of CommandBase class. CommandBase is a class that keeps references to the command's action and other related data. When the view is rendered the binding to this property causes an execution of a callback that is specified in the dependency property 'registration'. The attached property is static but the callback delivers instances. It delivers an instance of the control and an instance of the command I bind to in the XAML. Next is the kidnaping/rewiring - I take the original event and register to it a local generic handler. At that point the binding activity is done. As a result to the above what I have is a generic handler that will run when the event is fired.
This handler receives the original events parameters, and the event sender. It also has the ability to pull the command object (I'll cover it later) and to pull the parameter the user might have specified (I'll cover it later). It combines the 3 objects into one ParameterWrapper object. Using the command object it calls the command's action and passes it the ParameterWrapper object.
CommandBase Class
This class implements the ICommand interface which defines the action and check to see if the action can be invoked at this moment. Silverlight 4 uses it with the existing built in commands. It contains 2 delegates - one that is called to see if the action can be run and one which is the action itself.

public class CommandBase : ICommandFromEvent
{
    private bool canExecuteLocal = false;
    private readonly Func<object, bool> isExecuteAllowed = null;
    private readonly Action<object> commandAction = null;
    public CommandBase(Action<object> commandDelegate, Func<object, bool> canExecuteDelegate = null)
    {
        commandAction = commandDelegate;
        isExecuteAllowed = canExecuteDelegate;
    }
It also implements a custom interface that allows me to call just the one method and the isAllowed check and execution will be done.
CommandBinder Class
This class binds a xaml declared CommandBase to a control and an event. Every event type requires its own CommandBiner.
The commandBinder contains the name of the event that it will 'kidnap'. It also contains 2 (static) attached properties: Command and ComandParameter. Each of these properties requires their respective (static) getter and setter. The Command property also requires the (static) callback. The callback instantiate a class I call Executor. The Executor class is the one that kidnaps the event (rewire it as well as calling the action from the CommandBase).
Here is the first part of the code:

private const string EName = "SelectionChanged"; //the event name
public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached
    (
        "Command",    //Property name
        typeof(ICommand),    //Property type
        typeof(SelectionChangedCommandBinder),  //Type of property owner
        new PropertyMetadata(CommandChanged)  //Callback invoked on property value has change
    );
    public static void SetCommand(DependencyObject depObj, ICommand cmd)
    {
        depObj.SetValue(CommandProperty, cmd);
    }
    public static ICommand GetCommand(DependencyObject depObj)
    {
        return (ICommand)depObj.GetValue(CommandProperty);
    }
    //the callback
    protected static void CommandChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs depArgs)
    {//create an Executor for a given even and event-args type
        var executor = new Executor<RoutedPropertyChangedEventArgs<object>>(EName);
        executor.CommandGetter = GetCommand;
        executor.ParameterGetter = GetCommandParameter;
        executor.CommandChanged(depObj, depArgs);
    }
A similar code exists for the CommandParameterProperty (minus the callback).
The CommandChanged (callback) instantiates a new Executor for every new binding. This is the place in which the static nature of the attached property 'stops' and a new instance is being assign to each and every event/control/command. While instantiating an Executor class the callback passes to it the name of the event, a delegate to find the BaseCommand and a delegate to find the CommandParameter. Since the Executor is a Generic class the callback implicitly specify the type of the event arg type.
The Executor Class
This generic class wires a generic event handler to the event. Firstly, by using reflection it finds the event based on its name, next it create a delegate out of the local generic handler, lastly it adds this delegate to the event so that next time the event is raised it will invoke the local handler.

public void CommandChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs depArgs)
{ //use reflection to add a given handler as a delegate to the given event name
    if (depObj != null)
    {
        Type type = depObj.GetType();
        var eventInfo = type.GetEvent(eventName);
        if (eventInfo != null)
        { //event name yield an event
            var delegateType = eventInfo.EventHandlerType;
            if (delegateType != null)
            { //create a delegate from the handler
                Delegate handler = Delegate.CreateDelegate(delegateType, this, "TheHandler");
                if (handler != null)
                { // delegate is good, add it to the event.
                    eventInfo.AddEventHandler(depObj, handler);
                }
            }
        }
        else
        {
            MessageBox.Show(string.Format("missing types for Event [{0}] in [{1}]", eventName, depObj));
        }
    }
}
The local generic handler uses the delegates that were passed in and calls the command's action.

private void TheHandler(object sender, T e)
{
    var commander = sender as DependencyObject;
    if (commander != null)
    { //get the CommandBase
        var cmd = commandGetter(commander);
        if (cmd != null)
        { //wrap all the data by one object
            var param = new ParameterWrapper(parameterGetter(commander), sender, e as RoutedEventArgs);
            var sCmd = cmd as ICommandFromEvent;
            if (sCmd != null)
            { //as part of this call check if the command is allowed to be executed
                sCmd.SmartExecute(param);
            }
            else
            { //Execute checking might have been done earlier
                cmd.Execute(param);
            }
        }
    }
}
XAML Declaration of the Command
The nice part of all of this commanding design is the ease with which I declare the command and the command parameter in XAML:

        <sdk:DataGrid AutoGenerateColumns="True"
                             HorizontalAlignment="Left"
                             Margin="5"
                             VerticalAlignment="Top"
                             Grid.Row="1"                                                              
                             ItemsSource="{Binding DataToPresent}"
                             cmd:SelectionChangedCommandBinder.Command="{Binding ItemSelectedCommand}"
                             cmd:SelectionChangedCommandBinder.CommandParameter=
                              "{Binding SelectedItem, RelativeSource={RelativeSource self}}"
                             />
The Above XAML snippet declares the DataGrid the last 2 lines deal with the command declaration.
First I bind the ItemSelectCommand (represent an instance of CommandBase) with the Command Attached Property that resides in the SelectionChangedCommandBinder class.
The second line uses some XAML acrobatics to bind the SelectedItem property of the current control to the CommandParameter Attached Property that resides in the SelectionChangedCommandBinder class.
Steps to Create a Command
Every event that I want to convert to a command requires its own class with the 2 attached properties. The good news is that most apps need a limited number of event types. Also the creation process is quite simple, but process nonetheless.
  1. Create new command binder (I follow a name convention therefore I copy an existing one and then replace old event name by new one [I clear search criteria 'Match whole Word' - I get 6 replacements). 

    1. In the callback of command section instantiate the Executor with the correct event handler type
       
  2. Create a new command property (I always do it in the BaseViewModel in the region 'command properties')
  3. Add a new Action (I create a virtual one in the BaseViewModel in the region 'command actions'
  4. Instantiate the CommandBase in BaseViewModel  in the Initialize method
  5. In the specific view bind the command in the XAML.
Step No. 1

public class KeyDownCommandBinder
{
    private const string EName = "KeyDown";
    public static readonly DependencyProperty CommandParameterProperty = DependencyProperty.RegisterAttached
          (
              "CommandParameter",             //Property name
              typeof(object),                        //Property type
              typeof(KeyDownCommandBinder),       //Type of property owner
              new PropertyMetadata(CommandParameterChanged)  //Callback invoked on property value has change
          );
    public static void SetCommandParameter(DependencyObject depObj, object param)
    {
        depObj.SetValue(CommandParameterProperty, param);
    }
    public static object GetCommandParameter(DependencyObject depObj)
    {
        return depObj.GetValue(CommandParameterProperty);
    }
    private static void CommandParameterChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs depArgs)
    {
    }
    public static readonly DependencyProperty CommandProperty = DependencyProperty.RegisterAttached
        (
            "Command",    //Property name
            typeof(ICommand),    //Property type
            typeof(KeyDownCommandBinder),    //Type of property owner
            new PropertyMetadata(CommandChanged)        //Callback invoked on property value has change
        );
    public static void SetCommand(DependencyObject depObj, ICommand cmd)
    {
        depObj.SetValue(CommandProperty, cmd);
    }
    public static ICommand GetCommand(DependencyObject depObj)
    {
        return (ICommand)depObj.GetValue(CommandProperty);
    }
    public static void CommandChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs depArgs)
    {
        var executor = new Executor<KeyEventArgs>(EName);
        executor.CommandGetter = GetCommand;
        executor.ParameterGetter = GetCommandParameter;
        executor.CommandChanged(depObj, depArgs);
    }
}
Step No. 2

private ICommand keyDownCommand = null;
public ICommand KeyDownCommand
{
    get { return keyDownCommand; }
    set
    {
        keyDownCommand = value;
        OnPropertyChanged("KeyDownCommand");
    }
}
Step No. 3

protected virtual void KeyDownAction(object param)
{
    MessageBox.Show("KeyDown Action is not ready");
}
Step No. 4

KeyDownCommand = new CommandBase(KeyDownAction);
Step No. 5

cmd:KeyDownCommandBinder.Command="{Binding KeyDownCommand}"
cmd:KeyDownCommandBinder.CommandParameter="{Binding Text, RelativeSource={RelativeSource self}}"
or

cmd:LoadedCommandBinder.Command="{Binding LoadedCommand}"
cmd:LoadedCommandBinder.CommandParameter="[ClassicView]"


Similar Articles