Silverlight Toolkit ExpanderView – Flat Objects

Before anyone posts, I know this is not the intended usage of the control. I did, however, have a requirement to show a list of simple objects and some of their properties in an expander view, so I’m going to explain one of many possible ways to do it.

My ViewModel contains an ObservableCollection<ServiceAddOn>, where my ServiceAddOn class looks like this.

public class ServiceAddOn : INotifyPropertyChanged
{
    string name;
    string cost;
    DateTime startDate;
    DateTime expiryDate;
    // encapsulation for public access to the 4 locals
    // INotifyPropertyChanged implementation
}

Don’t worry about the encapsulation or INotifyPropertyChanged implementation – they are as simple as it gets.

Now, I want to create an ListBox of ExpanderView controls that will display the information like this :


My Item Name
- Cost : £123.99
- Start Date : 1/1/2012
- Expiry Date : 1/1/2013
Second Item
ThirdItem

That is where the problems start. As far as I can tell (with admittedly limited research into the issue) there is no way to bind to properties of an object in both the HeaderTemplate and the ItemTemplate – something needed to be done to expose these properties as a collection. Since I was not able to change the inplementation of the ServiceAddOn class, I decided the best way would be to write a converter. Before starting that, though, I needed a class to hold each of myt exposed properties. It looks like this :

public class ExposedProperty
{
    public string Key { get; set; }
    public string Value { get; set; }
}

Again, it is about as simple as it gets. :)

Now, the converter:

public class ExposePropertyConverter : IValueConverter
{

    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        ServiceAddOn obj = value as ServiceAddOn;
        if (null == obj) return null;
        ObservableCollection<ExposedProperty> values = new ObservableCollection<ExposedProperty>();
        values.Add(new ExposedProperty { Key = "Cost", Value = obj.Cost });
        values.Add(new ExposedProperty { Key = "Start Date", Value = obj.StartDate.Date.ToShortDateString() });
        values.Add(new ExposedProperty { Key = "Expiry Date", Value = obj.ExpiryDate.Date.ToShortDateString() });

        return values;
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        throw new NotImplementedException();
    }
}

So now, it is just a case of a bit of plumbing…

Add the namespace reference to the XAML, and declare a converter instance

<phone:PhoneApplicationPage 
    x:Class="MyExampleApp.MainPage"
    ...snip...
    xmlns:conv="clr-namespace:MyExampleApp.Converters"
    ...snip...
    >
    <phone:PhoneApplicationPage.Resources>
        <conv:ExposePropertyConverter x:Key="GetMyProperties" />
    </phone:PhoneApplicationPage.Resources>

and then wire up the Listbox / ExpanderView combo… (note tht my collection of ServiceAddOn objects is called “DisplayItems”)

<ListBox ItemsSource="{Binding DisplayItems}">
    ...snip standard plumbing...
    <ListBox.ItemTemplate>
        <DataTemplate>
             <toolkit:ExpanderView 
                 ItemsSource="{Binding Converter={StaticResource GetMyProperties}}"
                 Header="{Binding}"
                 NonExpandableHeader="{Binding}"
                 Expander="{Binding}">
                 <toolkit:ExpanderView.HeaderTemplate>
                     <DataTemplate>
                         <TextBlock Text="{Binding Name}" />
                     </DataTemplate>
                 </toolkit:ExpanderView.HeaderTemplate>
                 <toolkit:ExpanderView.ItemTemplate>
                     <DataTemplate>
                         <TextBlock>
                             <Run Text="{Binding Key}" />
                             <Run Text=" : " />
                             <Run Text="{Binding Value}" />
                         </TextBlock>
                     </DataTemplate>
                 </toolkit:ExpanderView.ItemTemplate>
             </toolkit:ExpanderView>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

The “magic” happens on line 6, where I bind the ItemsSource of the ExpanderView control to “{Binding Converter={StaticResource GetMyProperties}}”.

That’s it.

Next steps for this may be to make the Converter more generic, and possibly use reflection to get the properties instead of hard coding them, but for now this is good enough.

If you have any comments about a much simpler way to bind to flat, single-level objects in an ExpanderView control. I’d love to hear them.