Using the Windows 8.1 Hub as an ItemsControl

This article presents a Windows 8.1 Hub control with ItemsSource and ItemTemplate properties, making it easily bindable and more MVVM-friendly. The Hub control has become the main host on the startup screen of many Windows Store apps: it’s flexible but still presents a standard look-and-feel with the title and section headers at the appropriate location, it nicely scrolls horizontally, and it comes with semantic zoom capabilities (well, at least with some help). Although it visually presents a list of items, it’s not an ItemsControl: the Hub control expects you to provide its sections and their corresponding data template more or less manually. Let’s solve that issue and create a Hub control that’s closer to an ItemsControl:

I initially started by hooking up attached properties to the existing Hub class, but then I discovered that this class is not sealed. So I created a subclass, named ItemsHub:

public class ItemsHub : Hub
{
	// ...
}

Then I just added some of the missing properties as dependency properties (using the propdp snippet): ItemTemplate as a DataTemplate, and ItemsSource as an IList.

public DataTemplate ItemTemplate
{
    get { return (DataTemplate)GetValue(ItemTemplateProperty); }
    set { SetValue(ItemTemplateProperty, value); }
}

public static readonly DependencyProperty ItemTemplateProperty =
    DependencyProperty.Register("ItemTemplate", typeof(DataTemplate), typeof(ItemsHub), new PropertyMetadata(null, ItemTemplateChanged));

public IList ItemsSource
{
    get { return (IList)GetValue(ItemsSourceProperty); }
    set { SetValue(ItemsSourceProperty, value); }
}

public static readonly DependencyProperty ItemsSourceProperty =
    DependencyProperty.Register("ItemsSource", typeof(IList), typeof(ItemsHub), new PropertyMetadata(null, ItemsSourceChanged));

When ItemTemplate is assigned or changed, we iterate over all Hub sections to apply the template to each of them:

private static void ItemTemplateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ItemsHub hub = d as ItemsHub;
    if (hub != null)
    {
        DataTemplate template = e.NewValue as DataTemplate;
        if (template != null)
        {
            // Apply template
            foreach (var section in hub.Sections)
            {
                section.ContentTemplate = template;
            }
        }
    }
}

When ItemsSource is assigned or changed, we repopulate the sections and their headers from the source IList, and re-apply the data template (you should not make assumptions on the order in which the dependency properties are assigned):

private static void ItemsSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    ItemsHub hub = d as ItemsHub;
    if (hub != null)
    {
        IList items = e.NewValue as IList;
        if (items != null)
        {
            hub.Sections.Clear();
            foreach (var item in items)
            {
                HubSection section = new HubSection();
                section.DataContext = item;
                section.Header = item;
                DataTemplate template = hub.ItemTemplate;
                section.ContentTemplate = template;
                hub.Sections.Add(section);
            }
        }
    }
}

Instead of defining a HeaderPath property, or creating a HeaderTemplate, I decided to fall back on the default template (a text block) and to assign the whole item to the section header. Now all you need to do to show a descent header is overriding the ToString method in the (View)Model class:

public override string ToString()
{
    return this.Name;
}

Here’s how to create an ItemsHub control in XAML and define the bindings to its new properties in a light-weight MVVM style:

<Page.DataContext>
    <local:MainPageViewModel />
</Page.DataContext>

<Page.Resources>
    <DataTemplate x:Key="DataTemplate">
        <Image Source="{Binding Image}" />
    </DataTemplate>
</Page.Resources>

<Grid>
    <local:ItemsHub Header="Hub ItemsControl Sample"
                    ItemTemplate="{StaticResource DataTemplate}"
                    ItemsSource="{Binding Manuals}" />
</Grid>

Here’s the result in the attached sample app. Each Hub section represents a business object (an Ikea Instruction Manual) from a collection in the ViewModel:

I focused on the properties that make the most sense in MVVM apps, but -as I mentioned- the framework Hub class is not sealed. So you can use this same technique to add other useful properties like Items and DataTemplateSelector.

Here’s the full code, it was written with Visual Studio 2013 for Windows 8.1: U2UConsult.WinRT.HubItemsControl.zip (759.57 kb)

Enjoy!
Diederik