WPF has a really nice feature in the DataTemplateSelector that helps us use different DataTemplate's within the same ItemsControl depending on the data being bound. Out of the box this functionality isn't included with Windows Phone, but it can be implemented in a number of different ways. In this post I'm going to show how we can use Caliburn Micro to achieve the same functionality.
Caliburn Micro is really optimised and designed for a "View Model first" approach, by this we pass the framework the view model we wish to display, it then uses conventions to determine the view for that view model and binds them together.
Windows Phone 7 and it's requirements around the navigation frame and pages enforces a "View first" approach, this doesn't mean we can't use the "View Model" first within our pages.
When Caliburn applies it's conventions to a ItemsControl (the base class of controls such as ListBox and Pivot) checks to see if the ItemTemplate has been set. If one hasn't Caliburn will set a very simple but powerful DataTemplate that looks like:
<DataTemplate>
<ContentControl cal:View.Model="{Binding}" VerticalContentAlignment="Stretch" HorizontalContentAlignment="Stretch" IsTabStop="False" />
</DataTemplate>
By binding the View Model to the Content Control tells Caliburn to locate the appropriate View and instantiate it within the Control Control. The important thing to think about here is that if the view model type is different for items in the ListBox then the views created by Caliburn will be different.
For our example we'll have an activity stream with three different sorts of activities, announcements, status updates and new image galleries.
First we'll create Caliburn View Models for each of our three activities (AnnouncementViewModel, StatusViewModel, GalleryViewModel), we'll have a shared base View Model for common data (ActivityViewModel). On our activity page the view model will expose an observable collection of our base activity view model, on view model initialisation we'll populate the collection with a variety of activities.
public class GalleryViewModel : ActivityViewModel
{
private string title;
private Uri image;
public string Title
{
get { return title; }
set
{
title = value;
NotifyOfPropertyChange(() => Title);
}
}
public Uri Image
{
get { return image; }
set
{
image = value;
NotifyOfPropertyChange(() => Image);
}
}
}
public class AnnouncementViewModel : ActivityViewModel
{
private string headline;
public string Headline
{
get { return headline; }
set
{
headline = value;
NotifyOfPropertyChange(() => Headline);
}
}
}
public class StatusViewModel : ActivityViewModel
{
private string username;
private string text;
public string Username
{
get { return username; }
set
{
username = value;
NotifyOfPropertyChange(() => Username);
}
}
public string Text
{
get { return text; }
set
{
text = value;
NotifyOfPropertyChange(() => Text);
}
}
}
public class ActivityListViewModel : Screen
{
public ActivityListViewModel()
{
Activities = new BindableCollection<ActivityViewModel>();
}
protected override void OnInitialize()
{
Activities.Add(new AnnouncementViewModel
{
Headline = "To Do Today 1.5 Released",
OccuredOn = DateTime.Now
});
Activities.Add(new GalleryViewModel
{
Title = "Lorem Pixum",
Image = new Uri("http://lorempixum.com/g/140/140/technics/", UriKind.Absolute),
OccuredOn = DateTime.Today.AddDays(-1)
});
Activities.Add(new StatusViewModel
{
Username = "@nigel-sampson",
Text = "Up late working on client apps.",
OccuredOn = new DateTime(2011, 07, 11, 11, 00, 00)
});
}
public IObservableCollection<ActivityViewModel> Activities
{
get;
set;
}
}
Now to create the views for our various activity, rather than the standard Windows Phone view of a PhoneApplicationPage our views should be a UserControl. Remember to follow the conventions Caliburn has so that the view can be located correctly (AnnouncementView, StatusView, GalleryView).
Here's an example of one of the the templates, the AnnouncementView.
<UserControl x:Class="CompiledExperience.Phone.Demo.Examples.Views.AnnouncementView"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
d:DesignHeight="140" d:DesignWidth="456">
<StackPanel x:Name="LayoutRoot" Margin="0,6">
<TextBlock x:Name="Headline" Style="{StaticResource PhoneTextLargeStyle}"/>
<StackPanel Orientation="Horizontal">
<TextBlock Text="occurred on" Style="{StaticResource PhoneTextSubtleStyle}"/>
<TextBlock x:Name="OccuredOn" Style="{StaticResource PhoneTextSubtleStyle}" Margin="0"/>
</StackPanel>
</StackPanel>
</UserControl>
Once the views have been created we're pretty much done, by convention Caliburn will bind our Activities list to the ListBox and creates instances of our views based on the view models and we're done.
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="COMPILED EXPERIENCE" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="templates" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<ListBox x:Name="Activities"/>
</Grid>
</Grid>
What's really good about this approach is that you don't need to try and build a single data template that needs to deal with all types of content we need to display. Each view can be completely independent of the other views. On a higher level a similar approach can be used to separate the different sections of a Pivot or Panorama.
One of the best things I find in the MVVM framework Caliburn Micro is the convention based binding for properties and methods. They allow a lot of binding between your view and view model to happen "auto-magically" based on the same of elements in your view.
If you haven't read the documentation I highly recommend reading All about Conventions as it explains a lot of the internal plumbing that makes the conventions work.
You can add more conventions really easily through the ConventionManager class, these are usually set up in the Configure method of your Bootstrapper. For instance if we wanted to add the standard convention for buttons we'd use the following code.
AddElementConvention<ButtonBase>(ButtonBase.ContentProperty, "DataContext", "Click");
The first parameter is the property on the element to bind to if we match against a property on the view model. For our button example if we had a string property on the ViewModel it would be the text of the Button.
The second is the property to use if we refer to the element as a parameter of an action, currently this isn't possible in Windows Phone at the moment (this looks to be changing in Mango but I haven't tested it yet) so can be ignored for now.
The third is the name of the event to trigger any actions specified on the element or for ones matched by convention, for our Button it makes sense that the Click event should trigger the action.
One other nice part of the ConventionManager in Caliburn is that you can provide conventions for base types and any that inherit from that type will also inherit it's conventions. This makes writing up all the conventions for a library considerably easier and that some of the base conventions in Caliburn will already suffice.
By default Caliburn has a lot of conventions built in, you can see most of them in the article linked above. What it's missing are conventions for more of the phone only controls, including the Silverlight Toolkit. This makes sense as we shouldn't expect to have Caliburn depend upon those assemblies just for some conventions.
I thought I'd provide some of the conventions I've used in projects recently.
// Phone Controls (from the Caliburn Sample
ConventionManager.AddElementConvention<Pivot>(ItemsControl.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if(ConventionManager.GetElementConvention(typeof(ItemsControl)).ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager.ConfigureSelectedItem(element, Pivot.SelectedItemProperty, viewModelType, path);
ConventionManager.ApplyHeaderTemplate(element, Pivot.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
ConventionManager.AddElementConvention<Panorama>(ItemsControl.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
if(ConventionManager.GetElementConvention(typeof(ItemsControl)).ApplyBinding(viewModelType, path, property, element, convention))
{
ConventionManager.ConfigureSelectedItem(element, Panorama.SelectedItemProperty, viewModelType, path);
ConventionManager.ApplyHeaderTemplate(element, Panorama.HeaderTemplateProperty, viewModelType);
return true;
}
return false;
};
// Silverlight Toolkit
ConventionManager.AddElementConvention<AutoCompleteBox>(AutoCompleteBox.ItemsSourceProperty, "SelectedItem", "SelectionChanged").ApplyBinding =
(viewModelType, path, property, element, convention) =>
{
ConventionManager.ConfigureSelectedItem(element, AutoCompleteBox.SelectedItemProperty, viewModelType, path);
return true;
};
ConventionManager.AddElementConvention<DateTimePickerBase>(DateTimePickerBase.ValueProperty, "Value", "ValueChanged");
ConventionManager.AddElementConvention<PerformanceProgressBar>(PerformanceProgressBar.IsIndeterminateProperty, "IsIndeterminate", "Loaded");
ConventionManager.AddElementConvention<ToggleSwitch>(ToggleSwitch.IsCheckedProperty, "IsChecked", "Checked");
ConventionManager.AddElementConvention<MenuItem>(ItemsControl.ItemsSourceProperty, "DataContext", "Click");
// Maps Conventions
ConventionManager.AddElementConvention<MapItemsControl>(ItemsControl.ItemsSourceProperty, "DataContext", "Loaded");
ConventionManager.AddElementConvention<Pushpin>(ContentControl.ContentProperty, "DataContext", "MouseLeftButtonDown");
I've received a few emails over the last week on how I created the in app videos for my apps To Do Today and Left to Spend so I thought I'd go over the process I used.
To capture the video I used the Expression Encoder Screen Capture tool that comes as part of Expression Encoder. I then drew out the capture area to just the emulator screen without the chrome. I then ran through the demo of the app (multiple takes due to miss taps), once happy with the recording I then transferred the recording to Expression Encoder itself.

I used the following settings for encoding the video, the first time I encoded one I also specified a player which I'd use for all the videos. The settings for the player are included below.
- Output Format: Windows Media
- Video: VC-1 Advanced
- Width: 240
- Height: 400
- Video Aspect Ratio: Source
- Resize Mode: Letterbox
- Template: Expression
- Scale Mode: Stretch to Fill
Now that I have the video and player I need the static chrome for the phone. I pulled this image out of the Metro guidelines photoshop templates and use CSS to wrap the player element with the chrome as a background image.
I'm also using JQuery to embed the Silverlight with a technique in a previous post of mine Using JQuery to emebed Silverlight.
$(function () {
$("#phone-slider-slides").silverlight("/clientbin/phonemediaplayer.xap", {
width: 240,
height: 400,
enableGPUAcceleration: true,
initParams: "playerSettings = <Playlist>" +
"<AutoLoad>true</AutoLoad>" +
"<AutoPlay>true</AutoPlay>" +
"<DisplayTimeCode>false</DisplayTimeCode>" +
"<EnableCachedComposition>true</EnableCachedComposition>" +
"<EnableOffline>true</EnableOffline>" +
"<EnablePopOut>true</EnablePopOut>" +
"<StretchNonSquarePixels>StretchToFill</StretchNonSquarePixels>" +
"<Items>" +
"<PlaylistItem>" +
"<Title>To Do Today : Compiled Experience : .NET Development in New Zealand</Title>" +
"<Height>400</Height>" +
"<IsAdaptiveStreaming>false</IsAdaptiveStreaming> " +
"<MediaSource>http://compiledexperience.com/content/video/to-do-today.wmv</MediaSource>" +
"<VideoCodec>VC1</VideoCodec>" +
"<Width>240</Width>" +
"<AspectRatioWidth>0.6</AspectRatioWidth>" +
"<AspectRatioHeight>1</AspectRatioHeight>" +
"</PlaylistItem>" +
"</Items>" +
"</Playlist>"
});
});
Overall it's a pretty simple process once you have the tools in front of you. Hope this helps people start creating web pages to showcase your Windows Phone apps.
After what's felt like an eternity I'd proud to announce my second Windows Phone 7 application Left to Spend passed certification and is now available to download. It's taken a lot longer than I expected mainly due to being very picky around design and usability. Not being a designer means I end up doing a lot more revisions than say with code.
Left to Spend is a small efficient budget / allowance tracking application, I often try to keep myself to a daily / weekly budget but find myself unable to know if I'm sticking to it without carrying cash. Left to Spend lets you specify your budget as an amount, a period of time (daily, weekly, fortnightly or monthly) and whether you want unspent amounts to roll into the next day.
There are quite a few budgeting applications already in the Marketplace, so why did I built another? Well to be honest most of the ones I downloaded and trialled weren't particularly good, any applications that don't do a good job of following the Metro UI patterns I tend to throw out pretty quickly. Some were also overly complicated, if I can't open up the application and enter a purchase quickly then what's the point?
On a side note a big thanks to Ben Tan the designer who did the icon for Left to Spend, I love the way it looks when pinned to your home screen. Roll on Mango when I'll update the app to a full live tile!

I'd love for you to go trial Left to Spend and send me your thoughts and feedback.
A common usage pattern for a ListBox is to use it as a navigation list, when you select the item the application navigates to a new page. This usually involves either wiring into the ListBox SelectionChanged event, or using some sort of GestureTrigger.
Why not build the navigation functionality directly into the ListBox itself. Each item bound the NavigationList will expose a property with the uri. Much like the charting controls we'll then supply a Binding to the list that will be used for each item when it's selected.
public class ApplicationViewModel : PropertyChangedBase
{
private int id;
private string name;
public ApplicationViewModel(int id, string name)
{
this.id = id;
this.name = name;
}
public int Id
{
get { return id; }
set
{
id = value;
NotifyOfPropertyChange("Id");
NotifyOfPropertyChange("Uri");
}
}
public Uri Uri
{
get
{
return new Uri("/Views/NavigationListBox/DetailsView.xaml?Id=" + Id, UriKind.Relative);
}
}
public string Name
{
get { return name; }
set
{
name = value;
NotifyOfPropertyChange("Name");
}
}
}
<toolkit:NavigationListBox x:Name="Apps" NavigateUriBinding="{Binding Uri}">
<toolkit:NavigationListBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name}" Style="{StaticResource PhoneTextLargeStyle}"/>
</DataTemplate>
</toolkit:NavigationListBox.ItemTemplate>
<toolkit:NavigationListBox.EmptyContent>
<TextBlock Text="No Apps" Style="{StaticResource PhoneTextLargeStyle}"/>
</toolkit:NavigationListBox.EmptyContent>
</toolkit:NavigationListBox>
Our NavigationList will obviously inherit from the ListBox we created in the last post. We'll then expose a property NavigateUriBinding, this property is actually of type Binding. It's also important that it's not a DependencyProperty, just a regular property, otherwise when we set the binding in xaml we'll create a binding to the property rather than setting the property to the binding (a slightly confusing concept I know). The best way to explain it is that while we're setting the binding once on the list we'll we be using it on each item in the list. This means that we need to have actual Binding object rather than a DependencyProperty bound to a value.
We'll create an attached dependency property at the same time, this is where we'll be setting the NavigateUri for each ListItem. We'll then override the PrepareContainerForItemOverride, this is where the ListBox creates the ListItem for each item bound, we'll then set the binding of our attached property to the binding set in NavigateUriBinding.
public class NavigationListBox : ListBox
{
public static readonly DependencyProperty NavigateUriProperty =
DependencyProperty.RegisterAttached("NavigateUri", typeof(Uri), typeof(NavigationListBox), null);
public NavigationListBox()
{
SelectionChanged += OnSelectionChanged;
}
public Binding NavigateUriBinding
{
get; set;
}
public static Uri GetNavigateUri(DependencyObject obj)
{
return (Uri)obj.GetValue(NavigateUriProperty);
}
public static void SetNavigateUri(DependencyObject obj, Uri value)
{
obj.SetValue(NavigateUriProperty, value);
}
protected override void PrepareContainerForItemOverride(DependencyObject element, object item)
{
base.PrepareContainerForItemOverride(element, item);
var frameworkElement = element as FrameworkElement;
if(frameworkElement == null || NavigateUriBinding == null)
return;
frameworkElement.SetBinding(NavigateUriProperty, NavigateUriBinding);
}
private void OnSelectionChanged(object sender, SelectionChangedEventArgs e)
{
if(SelectedItem == null || ItemContainerGenerator == null)
return;
var container = ItemContainerGenerator.ContainerFromItem(SelectedItem);
var uri = GetNavigateUri(container);
SelectedItem = null;
var frame = Application.Current.RootVisual as PhoneApplicationFrame;
if(frame == null || uri == null)
return;
frame.Navigate(uri);
}
}
There isn't an override for the SelectionChanged event so we'll create a handler in the constructor of the control. If an item has been selected we'll retrieve the value of our attached property and it's not null we'll navigate to that control. Easy!
In a bid to learn Mercurial at the same time I've uploaded the source for the controls so far (with a few other little bits and pieces) to Bitbucket, you find it all at Compiled Experience Toolkit. Sorry for the delay.
Download the source.