Model-View-Controller for a List

With WPF, it is very simple to follow the MVC pattern where a CLR object or XML is acting as your Model, and you want to present the data from the Model in a View, which is built in XAML by binding WPF controls to elements in the Model. What happens when we want to make changes to the data in the Model, such as updating some properties of that object? Your user is interacting with the View, so you need something in between the View and the Model, which is your Controller.

The most common approach to updating the data is to write event handlers in the code behind for your XAML file (your View). However, this means that your Controller logic is tightly coupled with your View…you can’t easily reuse it in another View. If you implement your Controller logic as a WPF ICommand, you can keep the logic in a different class and use it declaratively from any XAML file.

A very good reason to do this might be for working with Lists. WPF has plenty of controls for viewing collections: ItemsControl and its many descendants (check out Bea’s page for all the declarative collection binding tutorials you’ll ever need). However, none of these WPF controls provide a way to add items to a list.

First off, let’s make a class called ListController. Its purpose is to provide WPF Command bindings to add and remove items from any IList. It should derive from FrameworkElement which allows it to be added in your logical tree in XAML markup so your other controls can bind to it. However, it has no content, so it will not display anything. You may decide you want it to have visual content, and if so, you should derive from Control or ContentControl instead.

public class ListController : FrameworkElement

Next, I’ll need a property where this Controller can be bound to its Model, an IList of any type. To do this, I have a dependency property called List:

public IList List
{
    get { return (IList)GetValue(ListProperty); }
    set { SetValue(ListProperty, value); }
}

public static readonly DependencyProperty ListProperty =
    DependencyProperty.Register("List", typeof(IList), typeof(ListController),
    new UIPropertyMetadata(null));

Nothing too special yet, we just have an object to which you can bind your IList. Next, we add an inner class to the ListController that is an ICommand implementation that will add objects to the list with the ICommand.Execute method is called. It doesn’t have to be an inner class, but I really don’t want it to be used from anywhere outside of my ListController. One thing you’ll notice, the AddCommand’s constructor takes the containing ListController as a parameter. That is how the command gets access to the List.

class AddCommand : ICommand
{
    ListController _listController;

    public AddCommand(ListController listController)
    {
        _listController = listController;
    }

    public bool CanExecute(object parameter)
    {
        bool canExecute = false;
        if (_listController.List != null)
            canExecute = true;
        return canExecute;
    }

    public event EventHandler CanExecuteChanged;

    public void Execute(object parameter)
    {
        if (_listController.List != null)
        {
            _listController.List.Add(parameter);
        }
    }
}

You’ll probably want to do additional validation and tracing in your implementation because this AddCommand will blindly try to add whatever parameter you pass directly to the List, which it may not accept.

Now, I just need to add a property to my ListController so that you can declaratively bind any ICommandSource to it (Button, Hyperlink, MenuItem). If you add other inner classes to implement ICommand for other common List functionality like Remove or Clear, you’ll need to add properties for those as well.

public ICommand AddItem
{
    get { return new AddCommand(this); }
}

To use the ListController from XAML, you just need to bind it to a command source, like a hyperlink:

<custom:ListController x:Name="myListController" 
                             List="{Binding Source={StaticResource targetList}}" />
<Button Command="{Binding Source={StaticResource myListController}, Path=AddItem}"
CommandParameter="{Binding ElementName=sourceListView, Path=SelectedItem}" />

In this example, myListController is declared earlier in the XAML and bound to a target list, which the controller keeps updated. The hyperlink is bound to the AddItem command on the controller, and adds the selected item from a sourceListView. Something like this could be used to add items from one list to another.

One thing to note, if you’re adding items to a list and see them being added when debugging your ListController, but the list doesn’t appear updated on screen, the target list is probably not an ObservableCollection. In this case, the list is updated, but it isn’t notifying the UI to refresh. The cleanest thing to do is to make the collection an observable collection, but if you can’t do that, you could add another dependency property such as TargetItemsControl to your controller that is the ItemsControl to keep updated. Whenever the contents of the list are changed by the AddItem command, you can tell the ItemsControl to update the ItemsSource:

BindingOperations.GetBindingExpressionBase(TargetItemsControl, 
   ItemsControl.ItemsSourceProperty).UpdateTarget();

Other related architectures:
Model-View-Controller works very well if you want to have a single model and multiple views of the data, which is something you often want with a list. However, there are also many scenarios where you may want to have multiple models of the same or related data. In that sort of scenario, you may want to consider the Model-View-ViewModel that has become so popular. Also, Microsoft is working to provide even more technologies that aid in separation of data, logic, and presentation in the Acropolis project, which is making its way into future WPF and Silverlight versions.

Advertisements
  1. No comments yet.
  1. No trackbacks yet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: