Wensus Analytics

WinRT XAML Hub Control: Getting Started Tutorial

screenshot_06272013_100012

In this tutorial we’re going to create a simple XAML app using the new Hub control. The tutorial is also going to show how SemanticZoom can be used with the Hub.

The Hub control is one of the new additions in Windows 8.1 XAML SDK and as described by the MSDN, Hub is “a new control for XAML that lets you easily create the hub design pattern that reflects the proper design guidelines and behavior”.

Why to use Hub control?

The MSDN again does a great job of summing up the reasoning behind a hub page and the hub control:

Hub pages are the user”s entry point to your app. They display content in a rich, panning view that lets users get a glimpse of what”s new and exciting, and then dig deeper into your app”s content. The hub displays different categories of content, each of which maps to your app”s section pages. Each section should bubble up content or functionality. The Hub should offer a lot of visual variety, engage users, and draw them in to different parts of the app.

For example the Weather and the Travel apps use hub pages as their starting points:

image

Getting started

In this tutorial we’re going to start from the “Blank App (XAML)” template. The aim is to create a hub page for a movie app using the new control.

screenshot_06272013_100012

So let’s start by creating the new app from the Blank App template. The template contains only a single empty page, MainPage.xaml, which we’re going to start editing:

image

As a test, if we insert the Hub control inside the Grid and set it’s background to Red, we can see that the new control fills the whole layout container:

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

        <Hub Background="Red"></Hub>

    </Grid>

image

As we have a working Hub control, it’s time to add some content to it. Let’s start from the header.

Setting the header

The header in Hub control is visible all the time, even when the user scrolls horizontally. It usually includes the app’s name but it can also contain other controls like the back button. The header can be configured using the Header-property:

        <Hub Background="Red">

            <Hub.Header>
                <Grid>

                </Grid>
            </Hub.Header>

        </Hub>

In this tutorial we only want to show the app’s name inside the header:

            <Hub.Header>
                <Grid>
                    <TextBlock Text="The Movie App" />
                </Grid>
            </Hub.Header>

image

As we can see, there’s no need to define style for the TextBlock. The header automatically sets the font’s size and weight and also the padding. Because the only thing we need in the header is the app’s name, we can configure the header even more easily:

        <Hub Background="Red" Header="The Movie App">

        </Hub>

Now that we have the header configured, it’s time add some actual content into the hub.

Adding content into Hub control: The HubSection control

The Hub control itself only shows the header. We need to add HubSection controls inside the Hub control to add the actual content.  This is similar to the Windows Phone app’s, where we have the Panorama and the PanoramaItem controls.

In our app, we want to display four hub sections:

  • The second section just displays a static image from a movie
  • The first section shows pictures of the most popular movies
  • The third section list some movies near you
  • The last section provides movie trailers

Let’s add these sections now:

        <Hub Background="Red" Header="The Movie App">
            <HubSection Header="Popular"></HubSection>

            <HubSection></HubSection>

            <HubSection Header="Near You"></HubSection>

            <HubSection Header="Trailers"></HubSection>
        </Hub>

And here’s the result:

image

Let’s add some content into the HubSections, starting from the second one (the one which displays the static movie image).

Full height image in HubSection

We want to display an image in the HubSection. And we want it to use all the available space. If we add the image as a content, this is what happens:

image

The image doesn’t use the whole space. The trick is to set the image as the background of the HubSection:

        <Hub Background="Red" Header="The Movie App">
            <HubSection Header="Popular"></HubSection>

            <HubSection Width="500">
                <HubSection.Background>
                    <ImageBrush ImageSource="/Assets/poster.jpg" Stretch="UniformToFill"/>
                </HubSection.Background>
            </HubSection>

            <HubSection Header="Near You"></HubSection>

            <HubSection Header="Trailers"></HubSection>
        </Hub>

Also notice that we set the Width of the HubSection. Here’s the result:

image

Now it looks much better. One HubSection down, three to go.

Adding content to HubSection

We still have three empty HubSections. But filling them is easy, as we can use the standard GridView and ListView controls. A HubSection can contain any content you like: Buttons, UserControls, Grids, StackPanels etc. But what is little peculiar is that the HubControl doesn’t have a Content-property: You set the content by setting its DataTemplate. Here’s a small example:

            <HubSection Header="Popular">
                <DataTemplate>
                    <StackPanel>
                        <Button Content="Button inside a HubSection"/>
                    </StackPanel>
                </DataTemplate>
            </HubSection>

image

Filling the remaining HubSections with GridViews and ListViews is outside the scope if this tutorial, but the source code shows how it’s done. One important point though: A control which is inside a HubSection cannot be directly accessed from code-behind!

For example, here’s a HubSection which contains a single ListView:

            <HubSection Header="Trailers">
                <DataTemplate>
                    <ListView x:Name="MovieTrailers">

                    </ListView>
                </DataTemplate>
            </HubSection>

Even though we’ve given the ListView a name, we cannot access it from the code-behind:

image

This is because the control is inside a DataTemplate. One easy way to get around this limitation is to hook into the ListView’s Loaded-event. Here’s the XAML:

                    <ListView x:Name="MovieTrailers" Loaded="MovieTrailers_Loaded">

                    </ListView>

And the code behind:

        private void MovieTrailers_Loaded(object sender, RoutedEventArgs e)
        {
            var listView = (ListView)sender;
            listView.ItemsSource = trailers;
        }

Header navigation

Our hub page only shows few trailers so the actual app probably has a dedicated page for the trailers. We want to enable the navigation so that the user can click the “Trailers” header to move forward. This feature is built into the Hub control.

First thing we need to to is to enable the header clicking for the HubSection, by setting its IsHeaderInteractive property to true:

            <HubSection Header="Trailers" Width="550" IsHeaderInteractive="True" >
                <DataTemplate>
                    <ListView Loaded="MovieTrailers_Loaded" 
                              SelectionMode="None" 
                              ItemTemplate="{StaticResource Standard130ItemTemplate}"/>
                </DataTemplate>
            </HubSection>

What’s great is that this automatically adds the navigation indicator to the header:

image

Now to handle the click, we must attach to Hub’s (not HubSection’s) SectionHeaderClick-event:

<Hub Header="The Movie App" Background="{StaticResource BackgroundBrush}" SectionHeaderClick="Hub_SectionHeaderClick">

The clicked HubSection can be read from the event’s arguments:

        private void Hub_SectionHeaderClick(object sender, HubSectionHeaderClickEventArgs e)
        {
            var clickedSection = e.Section;
            if (clickedSection.Name == "TrailersSection")
            {
                // Add navigation logic
            }
        }

Semantic zoom

Last thing we’re going to add to the hub is semantic zoom. The Hub control supports semantic zoom, meaning it can be used as a Zoomed in view.

First thing we need to do is to add the SemanticZoom control to the page:

    <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">
        <SemanticZoom x:Name="Zoom">
            <SemanticZoom.ZoomedInView></SemanticZoom.ZoomedInView>

            <SemanticZoom.ZoomedOutView></SemanticZoom.ZoomedOutView>
        </SemanticZoom>

        <Hub x:Name="Hub" Header="The Movie App" Background="{StaticResource BackgroundBrush}" SectionHeaderClick="Hub_SectionHeaderClick">

Then we move the whole Hub inside the ZoomedInView:

image

Also we need a ZoomedOutView. For that, let’s create a simple GridView:

            <SemanticZoom.ZoomedOutView>

                <GridView VerticalAlignment="Center" Margin="200,0,0,0" Loaded="GridView_Loaded">
                    <GridView.ItemTemplate>
                        <DataTemplate>
                            <StackPanel >
                                <TextBlock HorizontalAlignment="Center" Text="{Binding}" Style="{StaticResource SubheaderTextBlockStyle}" />
                            </StackPanel>
                        </DataTemplate>
                    </GridView.ItemTemplate>

                    <GridView.ItemContainerStyle>
                        <Style TargetType="GridViewItem">
                            <Setter Property="Margin" Value="4" />
                            <Setter Property="Padding" Value="5" />
                            <Setter Property="HorizontalContentAlignment" Value="Left" />
                            <Setter Property="VerticalContentAlignment" Value="Center" />
                        </Style>
                    </GridView.ItemContainerStyle>

                    <GridView.ItemsPanel>
                        <ItemsPanelTemplate>
                            <WrapGrid ItemWidth="400" ItemHeight="70" Orientation="Horizontal" VerticalChildrenAlignment="Center" MaximumRowsOrColumns="3"></WrapGrid>
                        </ItemsPanelTemplate>
                    </GridView.ItemsPanel>
                </GridView>

            </SemanticZoom.ZoomedOutView>

Only one small thing left: We have to provide some items for this GridView. We do that in the GridView_Loaded:

        private void GridView_Loaded(object sender, RoutedEventArgs e)
        {
            var sections = this.Hub.Sections;
            var headers = new List<string>();

            foreach(var item in sections)
            {
                var section = (HubSection)item;
                var header = (string)section.Header;
                if (string.IsNullOrWhiteSpace(header))
                    continue;

                headers.Add(header);
            }

            ((GridView)this.Zoom.ZoomedOutView).ItemsSource = headers;
        }

The code loops through all the HubSections. It takes the sections header from each of them and add its to the “headers”-list. This list is then set as the ItemsSource for our ZoomedOutView’s GridView. And that’s it. We can now zoom out:

image

And click any of the items to zoom into that section.

Final touches and the source code

At this point we’ve realized that the app looks better if we put the static picture as the first HubSection. Also, the red background can be replaced with a little less stressful color.

Full source code for the tutorial is available from GitHub (folder hubcontrol-getting-started). The app works with the preview release of Visual Studio 2013 and Windows 8.1.