Click handler, begone: Databinding a selected list item with Caliburn.Micro

As I’m shifting my mindset from the familiar codebehind style of development into MVVM mode, one area that often trips me up is user interactions that I used to handle with a Click event. I encountered this recently when I added a ListBox to my app. I wanted the app to update some on-screen data elements when the user touched a different item in the list.

In codebehind development, it’s obvious how to handle this: Just define a Click event handler in the list item’s XAML template, then write the logic to change the displayed data based on the selected item.

But how does this work in MVVM, where you don’t use Click events?

More databinding

Databinding is taking over a lot of the tasks I used to perform in my C#, and this is another example. Instead of using a Click handler, the example below binds the ListBox.SelectedItem property directly to a property in the viewmodel–no code required, other than the code that defines the bound properties themselves. Let’s dive in.

Simple viewmodel

There’s nothing particularly special going on in the MainPageViewModel. The List property is a container for the items that will be displayed in the ListBox. (The snippet below omits some code that populates the list with sample items when the constructor is called.) The main thing to note here is the fully implemented SelectedItem property. I used the expanded syntax because I needed to be able to call NotifyOfPropertyChange() when the item selection changes. (If anyone knows of a shorthand way to do this with auto implemented properties, please let me know in the comments.) The NotifyOfPropertyChange() call is what will trigger the UI to update itself. In this case, a TextBlock will update with the title of the selected album.

public class Album
{
    public string Title { get; set; }
    public string Artist { get; set; }
}

public class MainPageViewModel : PropertyChangedBase
    {
        private Album selectedItem;

        public Album SelectedItem
        {
            get
            {
                return selectedItem;
            }
            set
            {
                selectedItem = value;
                NotifyOfPropertyChange(() => SelectedItem);
            }
        }

        public List<Album> MyAlbumList { get; set; }
        
        ...
    }
}

Most of the work happens in the XAML listed below. I’ve stripped out the formatting and boiler plate stuff for clarity. Scan through the whole thing, then we’ll take a look at a few key snippets.

<ListBox ItemsSource="{Binding MyAlbumList}"
         SelectedItem="{Binding SelectedItem, Mode=TwoWay}">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel>
                <TextBlock Text="{Binding Title}"/>
                <TextBlock Text="{Binding Artist}" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>
<StackPanel>
    <TextBlock Text="Selected Album Title:" />
    <TextBlock Name="SelectedItem_Title" />
</StackPanel>

Notice that I’m binding the ItemsSource manually to the MyAlbumList property. There may be a way to accomplish the same result using Caliburn.Micro’s convention-style bindings, but this is how I was able to get it working.

The real magic is in the next attribute, SelectedItem="{Binding SelectedItem, Mode=TwoWay}". When the ListBox selected item changes, Caliburn.Micro assigns the value of the item to the SelectedItem property in the viewmodel. In this example, the viewmodel’s SelectedItem property is of type Album, which is the same type as the items in MyAlbumList.

The Mode=TwoWay setting allows the view to communicate back to the viewmodel. (Omitting this is a common databinding mistake—one I made just the other day. If actions in your UI aren’t triggering the expected response in your viewmodel, double check to make sure twoway binding is set.)

The remaining XAML is standard ListBox for binding to a list. I’m creating a template that will be used for each item and binding the list item’s properties to TextBlocks to display their contents. The Name="SelectedItem_Title" syntax may be unfamiliar; I am using a deep path property binding to get at the title of the selected list item. You could accomplish the same thing by defining a viewmodel wrapper property that returns SelectedItem.Title.

Download the sample code

A complete Visual Studio project that implements the example above is available here. It was built with the February 2011 version of the Window Phone Developer tools. Other than the WP7 tools, there are no external dependencies. The Caliburn.Micro framework code is included in the Framework folder.

5 Comments

Filed under Caliburn.Micro, MVVM, Windows Phone 7

5 responses to “Click handler, begone: Databinding a selected list item with Caliburn.Micro

  1. Caliburn.Micro understands controls that inherit from ItemsControl and Selector. If you name the property the same as an ItemsControl, we will databinding the ItemsSource. If the ItemsSource is also a Selector we will try to bind it’s SelectedItem property for you, following a secondary naming convention, which singularizes the items source name and then prepends “Selected”, “Activate” or “Current” to determine the view model property. For example, if your view model had a collection named Albums and a property named SelectedAlbum all you would need to do is name your ListBox “Albums” and both properties would be bound by convention. Additionally, if not DataTemplate is set on an ItemsControl and no DisplayNamePath is configured, Caliburn.Micro will install its default DataTemplate into the ItemsControl. The default template invokes Caliburn.Micro’s view location strategy for each item in the list. So, in the case of the above example, if you had not inlined the template, but created a UserControl named AlbumView, then it would be automatically used to render each item. All that just by creating a ListBox with a name of “Albums.” As a final thought, this pattern is so common that you may benefit in some cases from using Conductor.Collection.OneActive. This class basically maintains an Items collection and a single ActiveItem. Inheriting your ViewModel from this will provide the same functionality as above. In addition, all conductors recognize Caliburn.Micro’s lifecycle interfaces such as IActivate, IDeactivate, etc. so if you child ViewModels need custom code, it’s just a matter of them inheriting Screen or implementing a single interface to tie into that. In this way you can drive a fairly complex UI with simple conventional databinding.

    • Excellent–thanks, Rob. There’s a ton of good stuff there to digest. I love the idea of eliminating the datatemplate from the ListBox XAML–it would clean my view code up a lot. If I can get it working, I’ll try to do a follow up.

  2. You *can* eliminate the DataTemplate if you want. However, you have to decide whether or not its worth it since the inline DataTemplate is going to be a little more lightweight. For a template as simple as that, I normally just leave it inline. The important thing is that you have options and you can rely on conventions help eliminate the tedium of one choice vs. another.

    • I played around with another demo project this evening and was able to get the conventions binding working on the listbox and selected item textbox. I’ll have to do a follow up post and publish the sample–it took a little fiddling but worked great once I figured out how everything needed to line up. I even got the deep path bindings working. I wanted to figure that part out because the app I’m writing has that singleton items list, and I’d prefer to be able to interact directly with it from my view rather than writing wrappers.

      One question I was hoping you could answer is whether there’s a shorthand way to call NotifyOfPropertyChange() and still use auto implemented properties instead of the longhand implementation where you declare a private variable, then a getter and setter, and finally call NotifyOfPropertyChange() inside of the setter. It would be nice to eliminate that bit of ceremony if possible.

  3. I highly recommend that you look into NotifyPropertyWeaver. There is a sample that demonstrates integration with Caliburn.Micro. You can find it here http://code.google.com/p/notifypropertyweaver/

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