Fluent Silverlight – Binding control events to actions


Please view the table of content of this series for reference.

Introduction

A typical Silverlight application (or any application with a GUI) contains elements or controls like command buttons , menu items, text boxes etc. The user of the application interacts with the system via these controls. When the user interacts with a control like a command button this control raises events. In the case of a command button this would most probably be the Click event; in the case of a text box it might be the TextChanged event. As a developer we want to react to those events; we want to have some action happen as a consequence of the user clicking a button.

Sample

Let’s call the element that raises an event a publisher. So we can implement a Publisher class with one event BookReady. This event is raised whenever the publisher has a new book ready. The code looks like this

public class Publisher
{
    public event EventHandler<BookEventArgs> BookReady;
 
    public void PublishNewBook(string bookTitle)
    {
        if (BookReady != null)
            BookReady(this, new BookEventArgs(bookTitle));
    }
}
</p>

Note that we have implemented a method PublishNewBook which when called raises the event. This makes sense since an event is always triggered after something has happened. In this case the publisher produces a new book and when it is ready he publicly announces this – he raises the event.

The BookEventArgs class used by the Publisher is given here

public class BookEventArgs : EventArgs
{
    public string BookTitle { get; set; }
 
    public BookEventArgs(string bookTitle)
    {
        BookTitle = bookTitle;
    }
}
</p>

Now the whole thing would be pretty useless if there weren’t interested readers or book stores around that react upon the event of the publisher, buy the book and read it. So lets implement a consumer of a book and call it the BookLover

public class BookLover
{
    public void ReadBook(string bookTitle)
    {
        // some code
    }
}
</p>

Most often the interested reader doesn’t go directly to the publish or is not directly connected with the publisher. The interested reader goes to a bookstore. The bookstore is the broker between a publisher and the readers. So let’s look at the code of the book store

public class Store
{
    public void MeetWithPublishers()
    {
        var publisherX = new Publisher();
        publisherX.BookReady += OnNewBookReady;
 
        var titleForNewBook = "Introduction to Fluent Silverlight";
        publisherX.PublishNewBook(titleForNewBook);
    }
 
    private void OnNewBookReady(object sender, BookEventArgs e)
    {
        var personA = new BookLover();
        personA.ReadBook(e.BookTitle);
    }
}
</p>

The book store organizes a meeting with the publisher (method MeetWithPublisher) and tells the publisher to produce a new book about fluent Silverlight. The store than registers itself with the publisher as a consumer  of the BookReady event. Now whenever the book is ready the method OnNewBookReady is called by the publisher. In this method the store offers the book to its readers.

That’s a lot of code just to achieve one goal… we want to offer the book to an interested reader whenever it is available from the publisher. Can’t we reduce the code? Yes we can by using anonymous delegates. For a detailed introduction into anonymous delegates and lambda expressions please refer to this post.</p>

public class Store
{
    public void OrganizeNextPublisherMeeting()
    {
        var personA = new BookLover();
        var publisherX = new Publisher();
        
        var titleForNewBook = "Advanced usage of Fluent Silverlight";
 
        publisherX.BookReady += (sender, e) => personA.ReadBook(titleForNewBook);
    }
}
</p>

In the above code snippet the action that is executed as a reaction on the event is defined as a lambda expression. By using this approach we don’t need to define an event handler method nor an extra EventArgs class and the code is much more compact.

Introducing Commands

Now let us generalize this whole thing a little bit. On one side we have a publisher of events. This can be any event e.g. the BookReady event in the sample above or the click event of a Silverlight command button. In the latter case the publisher would be the Silverlight control. On the other side we have some logic that we want to execute as a reaction to the event. We can also say that we want to “trigger an action” by the event. Let’s wrap the code that we want to execute (that is the action) into its own class. This class we call “a command”. Let’s start by defining an interface that all types of commands will implement

public interface IFsCommand<T>
{
    void Execute(T parameter);
}
</p>

The interface has a generic parameter T and thus is very flexible. The method Execute can be called with any type of parameter, be it a simple value type parameter or any reference type parameter, that is an instance of any type of class. We can then create a command class as follows

public class FsCommand<T> : IFsCommand<T>
{
    private readonly Action<T> action;
 
    public FsCommand(Action<T> action)
    {
        this.action = action;
    }
 
    public void Execute(T parameter)
    {
        action(parameter);
    }
}
</p>

This class expects an action with one parameter as parameter of the constructor. This action is executed whenever the method Execute of the command is called.

If we have a command button control and an instance of a command we can bind the click event of the button to this command like this

button.Click += (sender, e) => someCommand.Execute(someParameter);
</p>

Binding control events to commands defined in the view model

Imagine we have a Silverlight application with a view (i.e. UserControl) and a corresponding view model (see Model-View-ViewModel pattern – MVVM). The view contains a button which we want to bind to a command defined on the model. The model is defined as follows

public class Model
{
    public IFsCommand<int> SaveCustomerAction;
 
    public Model()
    {
        SaveCustomerAction = new FsCommand<int>(p => OnSaveCustomer(p));
    }
 
    private void OnSaveCustomer(int parameter)
    {                              
        // logic to save the customer...
    }
}                             
</p>

and the view is defined like this

public class View
{
    private Model model;
 
    public View(Model model)
    {
        this.model = model;
 
        var saveCustomerButton = new FsButton();
 
        var parameter = 123;
        saveCustomerButton.Click += (sender, e) => model.SaveCustomerAction.Execute(parameter);
    }
}
</p>

Each time that the click event of the saveCustomerButton is raised the action OnSaveCustomer is executed on the view model. It very important to notice that the action is defined on the view model rather than on the view. We have a clear separation of layout code/logic (the view) and presentation logic (the view model). This makes it easy to unit test the logic since it is completely decoupled from the view. In Silverlight a view cannot be tested in isolation thus it is very important to take out any logic of the view and put it into the model.

Availability and interactability of a command

A certain command can be available or not available depending on the context. This is a concept independent of a UI. But when a command is triggered by a visual element on a view then this availability can be represented by the visibility and/or interactability of the control. The availability of a command is usually governed by security requirements or the application or by the state in a workflow. Thus we’d rather set the availability of a command in the view model than in the view and we expect the bound controls to automatically set their state (visible and/or enabled) accordingly.

We can introduce a property IsExecuatble of type bool in the interface of our commands as well as an event IsExecutableChanged which is triggered whenever the value of the IsExecutable property changes.

public interface IFsCommand<T>
{
    void Execute(T parameter);
    bool IsExecutable { get; set; }
    event EventHandler IsExecutableChanged;
}
</p>

we can now change the code that binds the control to the command as follows

var action = model.SaveCustomerAction;
saveCustomerButton.Click += (sender, e) => action.Execute(parameter);
action.IsExecutableChanged += (sender, e) => saveCustomerButton.IsEnabled = action.IsExecutable;
// to initialze the button with the current value
saveCustomerButton.IsEnabled = action.IsExecutable;
</p>

From now on the button automatically updates its IsEnabled property whenever the property IsExecutable of the bound command changes.

We could now introduce a similar concept for the Visibility property of the control. How we do this depends on the specific context. In the applications we write at our company we have so called edit mode specifications which govern the interactability and visibility of commands or actions. These specification make a clear distinction between a command being visible but not interactable and a command not being visible at all. In this context we introduced the visibility as an extra property in the interface of the command analogous to the IsExecutable property. But your requirements may vary.

Our command class now looks like this

public class FsCommand<T> : IFsCommand<T>
{
    private readonly Action<T> action;
    private bool isExecutable;
 
    public FsCommand(Action<T> action)
    {
        this.action = action;
    }
 
    public void Execute(T parameter)
    {
        action(parameter);
    }
 
    public bool IsExecutable
    {
        get { return isExecutable; }
        set 
        { 
            if(isExecutable == value) return;
 
            isExecutable = value;
            if (IsExecutableChanged != null)
                IsExecutableChanged(this, EventArgs.Empty);
        }
    }
 
    public event EventHandler IsExecutableChanged;
}
</p>

There remains one problem though. If our application is multi-threaded then the setter method of the IsExecutable property might cause troubles. Remember that the properties of any (Silverlight) control can only be accessed from the UI thread (usually the main thread). Thus if another thread trigger the change of the IsExecutable property of the command we will have a cross thread access exception. To avoid this scenario we thus have to synchronize threads. We can use the Dispatcher class of Silverlight to do so.

public bool IsExecutable
{
    get { return isExecutable; }
    set
    {
        if (isExecutable == value) return;
 
        isExecutable = value;
        OnIsExecutableChanged();
    }
}
 
private void OnIsExecutableChanged()
{
    if (IsExecutableChanged == null) return;
 
    var dispatcher = DispatcherProvider.Dispatcher();
 
    if (dispatcher != null && !dispatcher.CheckAccess())
        dispatcher.BeginInvoke(OnIsExecutableChanged);
    else
        IsExecutableChanged(this, EventArgs.Empty);
}
</p>

</p>

Note that we use a DispatcherProvider to decouple the command from the framework and thus make unit testing possible. The DispatcherProvider just returns null in the context of a unit test. We defined the provider like this

public static class DispatcherProvider
{
    public static Func<Dispatcher> Dispatcher = 
        () => Deployment.Current != null ? Deployment.Current.Dispatcher : null;
}
</p>

Now when unit testing a command we can simply configure the DispatcherProvider as follows

DispatcherProvider.Dispatcher = () => null;
</p>

In my next post I will continue with more details about binding control events to command/actions and will specifically discuss how we can weakly bind a control to an action. Stay tuned.

Fluent Silverlight – Table of content