Monday, December 28, 2009

Creating a simple Commanding Framework for Silverlight 3

Whenever you are working with commanding, you are dependent on using either PRISM or perhaps the Interactivity framework. ( see my previous post )

Even in the future when Silverlight 4 is released there will only be support for commanding on Buttons and Hyperlinks.  So you might consider writing your own framework to handle all these things for you and allowing you to add some functionality on the way.

Getting Started

By default, Silverlight provides an ICommand interface that already provides you with a basic contract to get started with Commanding.  What we are going to do is extend this interface in an ITriggeredCommand interface. This will allow us to hook up some event handler when we want to execute the command.

public interface ITriggeredCommand : ICommand

     string Trigger { get; set; }
}

Setting up the Framework

Next we will create an abstract implementation to hide some of the contract from the ICommand and create our own contract.  We will however expose the possibility to invoke the CanExecuteChanged event, to be able to adapt our interface in the way we desire.

public abstract class BaseCommand<T> : ITriggeredCommand

    public string Trigger { get; set; } 

    private Action<T> execute;
    private Func<T, Boolean> canExecute; 

    public BaseCommand(Action<T> execute, Func<T, Boolean> canExecute)
    {
        if(execute == null)
            throw new ArgumentNullException("The execution action cannot be null.");
        this.execute = execute;
        this.canExecute = canExecute;
    } 

    /// <summary>
    /// When we have a canExecute function, execute it and pass the result
    /// </summary>
    public bool CanExecute(object parameter)
    {
        if (canExecute != null)
            return canExecute((T)parameter);
        return true;
    } 

    /// <summary>
    /// Execute the execution action, passing the parameter
    /// </summary>
    public void Execute(object parameter)
    {
        execute((T)parameter);
    }
    /// <summary>
    /// Allow for an implementation of this abstract class to invoke the CanExecuteChanged event
    /// </summary>
    protected void InvokeCanExecuteChanged()
    {
        if (CanExecuteChanged != null)
            CanExecuteChanged(this, EventArgs.Empty);
    } 

    public event EventHandler CanExecuteChanged;
}

At this point you should write a custom implementation of the BaseCommand<T>, however I will now continue with describing how we can hook this functionality up with our UI and ultimately allow you to use it in an MVVM application.

Making it work

So how do we hook up the commands to our UI,  well for this we can use attached properties and databind our commands to them accordingly.

The following code listing will describe how we can make sure the correct event is triggered ( the Trigger property ).

public static class Commands
{
    /// <summary>
    /// Register the new attached property for a command
    /// </summary>
    public static DependencyProperty CommandProperty = DependencyProperty.RegisterAttached
       (
           "Command",
           typeof(ITriggeredCommand),
           typeof(Commands),
           new PropertyMetadata(commandChanged)
       );  

   public static void SetCommand(DependencyObject obj, ITriggeredCommand propertyValue)
   {
       obj.SetValue(CommandProperty, propertyValue);
   } 

   public static ITriggeredCommand GetCommand(DependencyObject obj)
   {
       return (ITriggeredCommand)obj.GetValue(CommandProperty);
   } 

   //When the command changed make sure it is handled correctly 
   private static void commandChanged(DependencyObject obj, DependencyPropertyChangedEventArgs arguments)
   {
       if (arguments.NewValue is ITriggeredCommand)
       {
           ITriggeredCommand command = (ITriggeredCommand)arguments.NewValue;
           //Get the event that is related to the trigger
           EventInfo evInfo = obj.GetType().GetEvent(command.Trigger);
           //Create an eventhandler delegate for it
           Delegate executeEventDelegate = Delegate.CreateDelegate
               (
                  evInfo.EventHandlerType,
                   null,
                   typeof(Commands).GetMethod("handler",BindingFlags.Static | BindingFlags.NonPublic)
                );
            //Assign the Event handler to the object
            evInfo.AddEventHandler(obj,executeEventDelegate); 
        }
    } 

    //Execute the command
    private static void handler(object sender, EventArgs args)
    {
        ITriggeredCommand command = GetCommand((DependencyObject)sender);
        //We could implement an additional attached property to allow for a parameter to be set
        if (command.CanExecute(null))
            command.Execute(null);
    }
}

The important thing here is that we able to attach our command to an event that is related to the object we are binding our command to. 

In the commandChanged method we subscribe our handler to our event using reflection based on the EventHandlerType that was provided by the event defined in the Trigger property.

Tuesday, December 1, 2009

Creating a basic MVVM Application

In one of my previous post I mentioned MVVM ( or Model-View-ViewModel ) , so what exactly is the idea behind it, and more importantly, what are the advantages?

Separating UI from Business logic

The main problem with a Silverlight application is that you have a whole bunch of UI code intermingled with a bunch of Business logic.  This makes the whole application hard to understand, hard to debug and hard to test. We would like to have a way to avoid this. 

A solution to this problem is the concept of databinding in Silverlight, this allows us to loosely couple business Logic ( ViewModel )to our UI ( View ) and display the data we want ( Model ).

To allow interaction with our ViewModel, we would also require some functionalities to be bound to some UI components such as buttons.  For this we can use Commands,  Silverlight 3 provides by default the ICommand interface, and the PRISM framework has made an implementation of this so we can define commands on our FrameworkElement using attached properties. 

In Silverlight 4 there will be support for Commands out of the box for the ButtonBase and Hyperlink classes, so we will simply be able to bind to the Command property directly.

Setting it up

To make a MVVM application we will have to define a View and a ViewModel, a practice I like to use is to create two folders in our application to group our View’s and ViewModel’s so we have a clear view were our UI and our business logic is located.

As an example we will create a simple UI with a listbox and a button that will add quotes to it and display the data when the last quote was added.

The XAML for this view is straightforward :

<StackPanel Orientation="Vertical">
 <ListBox Height="150"/>
 <TextBlock />
 <Button Content="Add Quote"/>
</StackPanel>

Notice that I have not yet defined any databinding here, we will first create our ViewModel and map our binding to this afterwards.

A ViewModel that wants changes in it to be propagated to the UI should implement INotifyPropertyChanged this will provide a NotifyPropertyChanged event that will have to be invoked whenever a property in our ViewModel changes.  We will use an ObservableCollection to bind our quotes, to automatically trigger changes when we add new quotes.

This is the code for our ViewModel

public class ViewModel : INotifyPropertyChanged

    public ViewModel() 
   
        //Initialize our properties 
        Quotes = new ObservableCollection<string>(); 
        AddQuoteCommand = new DelegateCommand<object>(onAddQuoteCommandExecute); 
    }

    /// <summary> 
    /// Command that will be triggered when a button was clicked and will add a quote to the Quotes collection 
    /// </summary> 
    public DelegateCommand<object> AddQuoteCommand { get; set; }  

    /// <summary> 
    /// The list of Quotes we will bind to our listbox 
    /// </summary> 
    public ObservableCollection<String> Quotes { get; set; }  

    private DateTime lastModified; 
    /// <summary> 
    /// When was the list last modified 
    /// </summary> 
    public DateTime LastModified 
   
        get { return lastModified; }
        set 
       
            lastModified = value
            InvokePropertyChanged("LastModified"); 
       
    }  

    /// <summary> 
    /// When the AddQuoteCommand is triggered, it execute run this method 
    /// </summary> 
    private void onAddQuoteCommandExecute(object parameter) 
   
        //Add a generated quote 
        Quotes.Add(QuoteFactory.Generate());
        //Set the last modified date 
        LastModified = DateTime.Now; 
    }  

    public event PropertyChangedEventHandler PropertyChanged; 
    /// <summary> 
    /// Invoke that a property was changed 
    /// </summary> 
    /// <param name="propertyName">The propertyname</param> 
    private void InvokePropertyChanged(string propertyName) 
   
        if (PropertyChanged != null)
         PropertyChanged(this, new PropertyChangedEventArgs(propertyName)); 
    }

}

Now we can start binding our ViewModel to our View, there are a few ways you could to this, you could set the DataContext of the View in Code, or use dependency injection to initialize the ViewModel.  But for this example I will just define an instance of the ViewModel in the XAML of the View in its DataContext. Now we can simple add the databinding to our ListBox ,our TextBlock and bind the Command to the Button.  Silverlight will take care of the rest. 

<ListBox Height="150" ItemsSource="{Binding Quotes}"/>
<TextBlock Text="{Binding LastModified}" />
<Button Content="Add Quote" Commands:Click.Command="{Binding AddQuoteCommand}"/>

Conclusion

This example illustrates that setting up MVVM is not very difficult and allows for a nice separation of UI code and business logic.  You can, using MVVM, very easily define logic on several controls without having to write spaghetti code with events.  You would simply bind to the changing property in the UI ( e.g. a checkbox that is checked ) and have your View adapt to it accordingly because the setter in the ViewModel can very easily trigger business logic to occur.

There are however cases where you cannot, or very difficultly, achieve this.  You can not, for example, bind to a DependencyObject ( which is possible in WPF ). This will throw a XAML error in Silverlight 3.  However, in Silverlight 4 it will be possible to bind to a DependencyObject, making life a bit easier for people implementing MVVM.