Databinding WP7 lists to singletons with Caliburn.Micro

Databinding still seems a lot like black magic to me: You wave your hands over the keyboard, copy and paste some code from Stack Overflow, utter some dark words, and … it either works or it doesn’t. If it works, you shrug and move on. When it doesn’t … more hand-waving and increasingly dark utterances ensue.

The Caliburn.Micro framework removes a lot of the ceremony behind databinding. In most scenarios you can just name a view element, like a textbox, something like FirstName, and then make sure you have a property named FirstName in your viewmodel. Magic! Instant two-way databinding.

My current Windows Phone 7 project involves a list of data elements that will be displayed on multiple pages in the application. Rather than storing the list in isolated storage and reading it out each time one of the pages needs to display it, I’m making this list a singleton object, which will be created when the app first launches and will survive until the app closes.

The list is injected into each viewmodel that relies on it via the viewmodel’s constructor. Caliburn.Micro includes a simple IoC container that makes the injection process straightforward. Once the list has been injected into the viewmodel, I am storing the list items in a viewmodel property for display in the view.

Caliburn.Micro’s documentation is excellent, but I wasn’t able to find any example code or sample projects that showed how to databind to an injected list. I ended up thrashing around for a few hours before I found the following solution. (A complete sample project will be provided at the end of the post.) We’ll be creating an app that creates a list of cars, stores the list in a singleton object, and displays it on the page.

Let’s start by …

Creating the singleton in the bootstrapper

Any object to which you want to databind in a Caliburn.Micro project needs to be registered in the AppBootstrapper.cs file with the container.RegisterSingleton() method. Note that the second parameter, the key, should be null unless you plan to use the key to access the object later. This had me spinning my wheels for a while until Caliburn.Micro creator Rob Eisenberg pointed out my mistake. Here’s what our bootstrapper looks like:

public class AppBootstrapper : PhoneBootstrapper
{
    PhoneContainer container;

    protected override void Configure()
    {
        LogManager.GetLog = type => new DebugLogger(type);

        container = new PhoneContainer(this);

        container.RegisterPerRequest(typeof(MainPageViewModel), "MainPageViewModel", typeof(MainPageViewModel));
        container.RegisterSingleton(typeof(Cars), null, typeof(Cars));

        ...
       
        AddCustomConventions();
    }
   
    ...
}

Building the model objects

Our model is very straightforward. The Car class contains a few simple properties. The Cars class contains a property that holds a list of Car objects. It also includes a constructor that creates several sample cars for us to work with.

public class Car
    {
        public string Make { get; set; }
        public string Model { get; set; }
        public string Year { get; set; }
    }

public class Cars
    {
        public List Items;

        public Cars()
        {
            Items = new List();
            Items.Add(new Car() { Make = "Toyota", Model = "Camry", Year = "2007" });
            Items.Add(new Car() { Make = "Ford", Model = "Fiat", Year = "1999" });
  Items.Add(new Car() { Make = "Pontiac", Model = "Firebird", Year = "1980" });
            Items.Add(new Car() { Make = "Ford", Model = "Mustang", Year = "1964" });
        }
    }


Injecting the list into the viewmodel

The Caliburn.Micro will handle injecting the list into your viewmodel almost automatically. All you need to do is include the list as a parameter in your viewmodel’s constructor. Inside the constructor, assign the injected object to a property or private variable in the viewmodel so that you can use it later:

public class MainPageViewModel
    {
  private readonly INavigationService navigationService;

  public List CarsList { get; set; }

  public MainPageViewModel(INavigationService navigation, Cars cars)
        {
  navigationService = navigation;
  CarsList = cars.Items;
        }
    }

Note that I am assigning cars.Items to the CarsList property, not just the value of cars. This was another area where I got stuck for a while. I was initially databinding directly to the Cars object, but I later realized that the Cars object contains a list; it is not in and of itself a list.

Binding the list to the view

The final step is to configure the view to display the data from the list. I’m using a ListBox with a ListBox.ItemTemplate. Caliburn.Micro handles the heavy lifting here again. I gave the ListBox an x:Name that matches the property name of the list that you’re binding to—CarsList in this case. This binds the ListBox to the list, but I still need to indicate how the properties of each list item should be displayed.
I created several TextBox elements in the DataTemplate and bound the Text properties to the various properties of the Car object:

<ListBox x:Name="CarsList">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding Make}" />
                <TextBlock Text="{Binding Model}"
                           Margin="15, 0, 0, 0"/>
                <TextBlock Text="{Binding Year}"
                           Margin="15, 0, 0, 0" />
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

Troubleshooting databinding

Currently, the Windows Phone 7 tools don’t provide much in the way of debugging for databinding. Silverlight 5 will include a debug mode, and I’m hoping this will make it into Windows Phone sooner rather than later. In the meantime, here are a few tips that have helped me at various times:

  • Make a quick demo project. This is probably my number one troubleshooting tip of all time. Create a new project and use it to implement the one feature you’re working on. You won’t be worried about messing up your real app code, and you also are able to more easily focus on solving the problem at hand.
  • Confirm that your objects are being populated appropriately. Step through your viewmodel code in debug mode and make sure that properties actually contain the values you think they do. Sometimes the problem isn’t the binding.
  • Use logging. Caliburn.Micro has a nice logging feature, which I’ve enabled in this sample project. It writes to the Visual Studio output console as it attempts to databind each element in your XAML page. You’ll see messages like No convention applied to ApplicationTitle. and Added convention binding for CarsList. that can help you figure out whether the framework is finding the properties you intend to bind to. Rob has an excellent write-up on how to enable logging on his blog.
  • Finally, make sure that you’re binding to the object at the correct level. If you have a list contained in an object, you need to expose the list itself somehow before you can bind to it.

Download the sample code

The sample project is posted in my SkyDrive. 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.

About these ads

1 Comment

Filed under MVVM, Windows Phone 7

One response to “Databinding WP7 lists to singletons with Caliburn.Micro

  1. Pingback: Databinding WP7 lists to singletons with Caliburn.Micro | www.nalli.net

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