Part 17 - Understanding Data Binding, Data Sources and Data Contexts

Play Part 17 - Understanding Data Binding, Data Sources and Data Contexts
Sign in to queue

Description

Continuing our examination of the Hub App Template, we’ll learn how the HubPage.xaml page and the ItemPage.xaml binds to the data model we learned about in the previous lesson.

Binding, also known as data binding, is common in the programming world. It is a convenient way of tying the presentation layer of your application to the data model. You can declaratively define the data source (most likely a collection of objects) for a given control, say, a grid or a list, and then tell the control to bind to the data source. It then takes care of creating a visual representation of each item in the data source as a row, a list item, etc.

When building Phone apps in XAML, you use the Binding syntax we learned about previously. We saw how to bind to a static resource for the purpose of styling.

You bind to data in a similar way using a similar syntax.

<TextBlock Text="{Binding Title}" … />

Let’s start from the top.

The Page class, in this case, the HubPage class defines private field called “defaultViewModel”. This will be set to an object graph of Groups and related Items. That private field is exposed publicly through a read-only property called “DefaultViewModel”. The important thing is that we’re creating an instance of an ObservableDictionary<string, object>(). This class is something the template developers added to the template — it is not part of the Phone API. You can see its definition in the \Common folder. It’s not all that important to understand how it works under the hood. All I really care about is what the name implies.

We’ve not talked about the term Observable yet, but we will soon. For now, just focus on the fact that we’re working with a dictionary style collection. Recall from the C# Fundamentals for Absolute Beginners series that a dictionary is a specialized collection that allows you to create a key and an associated value. This will be important in just a moment.

As we learned about in an earlier lesson regarding the NavigationHelper class, the navigateionHelper_LoadState event handler is triggered when the page is navigated to. Let’s look at how it’s defined in the HubPage.xaml.cs:

        private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
        {
            // TODO: Create an appropriate data model for your problem domain to replace the sample data
            var sampleDataGroups = await SampleDataSource.GetGroupsAsync();
            this.DefaultViewModel["Groups"] = sampleDataGroups;
        }

This code:

this.DefaultViewModel[”Groups”]

… adds a new item to the dictionary with the key “Groups”.

What is going into the dictionary? The Groups” that are retrieved using the SampleDataSource.GetGroupsAsync().

You may wonder, why are they using a Dictionary if all they need is one item?  Again, I would remind you this is a template.  You could add more things to the Dictionary, then pull out what you need in the binding expression in XAML.  But you can only set the data context for the page to one object, so you would use this as the container for as much data for the various hub sections as possible.

What will we do with this dictionary once it is filled up with data? Our goal is to get the various GridViews in our XAML to bind to this data.

Look at the top of the HubPage.xaml page:

<Page
 x:Class="HubAppTemplate.HubPage"
 . . .
 DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"

Here we’re setting the DataContext of the entire Page, binding it to the DefaultViewModel of the current class (i.e., the {RelativeSource Self} means “you can find this class relative to myself, in my own class definition”).

A DataContext is defined by a parent and then utilized by the children. In this example, the parent is the Page class, and the children are the various controls that will take the data from the DefaultViewModel and bind to it.

So, given that the DataContext for the Page is set to the DefaultViewModel property, the ObservableDictionary already full of data, we can now revisit the XAML we looked at a couple of lessons ago (I’ll remove all of the XAML that is not important):

<HubSection Header="SECTION 2" DataContext="{Binding Groups[0]}" …>
 <DataTemplate>
 <GridView ItemsSource="{Binding Items}"
            ItemTemplate="{StaticResource Standard200x180TileItemTemplate}">
 ..
 </GridView>
 </DataTemplate>
</HubSection>

As you can see, I’ve removed A LOT of the XAML in order to clarify this example. Again, we’re working with the second panel, or rather, HubSection. The DataContext for the HubSection is Section2Items, the ItemsSource is the Items property. If you compare this to DefaultViewModel, it has a dictionary element called Section2Items which is of type SampleDataGroup. So, Section2Items (an instance of SampleDataGroup) has an Items property of type ObservableCollection<SampleDataItem>.

Now, the ItemTemplate comes into play. The ItemTemplate of the GridView is used to render each item that is displayed in the GridView. So, in our case, each item in the Items property collection of SampleDataItem will be rendered using the Standard200x180TileItemTemplate defined near the top of the file. Again, I’ve removed almost all the XAML except for those parts that are important for this discussion:

<DataTemplate x:Key="Standard200x180TileItemTemplate">
 <Grid>
 . . .
 <Image Source="{Binding ImagePath}"
 AutomationProperties.Name="{Binding Title}" />
 
 <TextBlock Text="{Binding Title}" />
 </Grid>
</DataTemplate>

For each SampleDataItem in the DefaultViewModel[”Section2Items”]’s Items property, The Image’s Source property is set to the SampleDataItem’s ImagePath. The TextBlock’s Text property is set to the SampleDataItem’s Title.

A couple of take aways from this first example:

(1) Keep in mind that the DataContext keeps flowing downward. In other words, the Page’s data context flows into the HubSection. The HubSection’s data context flows into the GridView, etc. Understanding the hierarchy of the binding will allow you to keep things straight in your mind.

(2) When you’re first getting started, it’s useful to stay close to examples like the ones in this template. If you can merely modify the existing example until you’re comfortable with the hierarchy of controls and settings, you’ll be more productive up front. You’ll see how I am able to utilize this Hub App Template to create an entire app in just a few lessons. Make small changes to these templates — you may even keep the original file in tact and use it for reference. Create a second page and copy parts from the HubPage.xaml into your new page until you’re comfortable with the relationships between the various parts.

Let’s take a look at a couple of more examples.

I’ll back up to the first HubSection which displays the Groups in a list:

Here’s the XAML that accomplishes this, removing all code that doesn’t deal with data binding:

<HubSection x:Uid="Section1Header"
 DataContext="{Binding Groups}">
 <DataTemplate>
  . . .
 </DataTemplate>
</HubSection>

The HubSection’s DataContext is set to the DefaultViewModel’s “SectionGroups” property.

private async void navigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
 var sampleDataGroups = await SampleDataSource.GetGroupsAsync();
 this.DefaultViewModel["Groups"] = sampleDataGroups;

GetGroupsAsync() merely returns the list of Groups. Easy enough.

What about the DataTemplate?

<DataTemplate>
  <ListView ItemsSource="{Binding}"
            IsItemClickEnabled="True"
            ItemClick="GroupSection_ItemClick"
            ContinuumNavigationTransitionInfo.ExitElementContainer="True">
    <ListView.ItemTemplate>
      <DataTemplate>
        <StackPanel>
          <TextBlock Text="{Binding Title}"
                     Style="{ThemeResource ListViewItemTextBlockStyle}" />
        </StackPanel>
      </DataTemplate>
    </ListView.ItemTemplate>
  </ListView>
</DataTemplate>

The big differences between the Section 2 and Section 1:

(a) Section 1 uses a ListView instead of a GridView, so all items flow vertically in one list, not in rows and columns like we saw in the GridView.

(b) In Section 1, each individual item is styled in the body of the DataTemplate, it doesn’t use a StaticResource binding expression.

(c) It uses the binding expression {Binding} because it is binding to the same data context as the entire page — the list of Group objects, whereas Section 2 is binding to a subset of the page’s data context, just the first Group object, Groups[0].  More about that in a moment.

The rest of the HubSections use similar techniques.  Before I leave the HubPage.xaml, I do want to explain one last curiosity.  How is it that the designer view displays the actual data?  How it is loading what seems to be the actual data from the SampleData.json?  The key is in the Page’s declaration:

<Page
    . . .
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:data="using:HubAppTemplate.Data"
    DataContext="{Binding DefaultViewModel, RelativeSource={RelativeSource Self}}"
    d:DataContext="{Binding Source={d:DesignData Source=/DataModel/SampleData.json, Type=data:SampleDataSource}}"
    mc:Ignorable="d">

Any time you see the d: prefix applied, think “design time”.  For reasons I don’t want to explain in detail at the moment, that last half of the <HubSection /> snippet (above) loads sample data (from the SampleData.json file) and sets it to the data source.  It’s a convoluted string of binding statements and parameters, but in a nut shell that is what’s happening.  In other words, when you see data in Visual Studio (or another tool like Blend), that is being loaded as a result of that command.  Remember, XAML is used to create instances of data.  At compilation, that XAML is converted into Intermediate Language code and at run time, it is executed just like the other C# code we wrote.  But at DESIGN TIME, Visual Studio (and Blend) need a special set of commands to take the data from our JSON file (or other data source) and use it for display.

Why do we need a design time experience that displays data visually at all?  We don’t.  If you were creating your own app, you could skip this step.  However, it is convenient when you want to use the Visual Studio designer to preview what the content will look like when properly styled.  Just know this … if you change from the SampleData.json file and use some other technique or some other data source for your app, you may get weird results at design time.  The same general principles apply here that we’ve been learning about in this lesson.

Before we conclude our examination of Binding, Data Contexts and Data Sources, let’s move on to the ItemPage.xaml.  When you click on a single item in the HubSections (all except the first HubSection which will open SectionPage.xaml) you will navigate to the ItemPage.xaml.  It, too, has a DefaultViewModel that is populated when the page is navigated to:

private ObservableDictionary defaultViewModel = new ObservableDictionary();

private async void NavigationHelper_LoadState(object sender, LoadStateEventArgs e)
{
  // TODO: Create an appropriate data model for your problem domain to replace the sample data
  var item = await SampleDataSource.GetItemAsync((string)e.NavigationParameter);
  this.DefaultViewModel["Item"] = item;
}

The most important part of this is (1) it calls GetItemAsync to load the data for a single SampleDataItem, and (2) the DefaultViewModel only has one dictionary key called “Item”.

In the page’s XAML, the Page’s DataContext is set to the DefaultViewModel as we would expect:

<Page
  x:Name="pageRoot"
  x:Class="HubAppTemplate.ItemPage"
  DataContext="{Binding DefaultViewModel.Item, RelativeSource={RelativeSource Self}}"

… and the only element on the templated page that binds to the data source is the TextBlock used for the title of the page:

<Grid x:Name="LayoutRoot" >
  . . .
  <StackPanel />
    . . .
    <TextBlock Text="{Binding Title}" />

Obviously, the intent here is to merely provide the framework that you would use to add more detail to this page.  The most important aspect of this is the binding syntax, in this case used to navigate from Item (the only key in the DefaultViewModel, which is of type SampleDataItem) to its Title property.

Let me take a moment to talk about a few popular binding expressions.  There are so many permutations of this that I couldn’t possibly talk about them all.  Let me just review a few of the basics.

Basic Binding

{Binding} - Bind to the current DataContext.  You see this when the current control, say, a ListView control, binds to the parent Page’s DataContext.  You would then expect the ListView’s ItemTemplate to bind to properties of the DataContext.

{Binding MyProperty} - Bind to the MyProperty property of the current DataContext.

{Binding MyObject.MyProperty} - Bind to the MyProperty property of MyObject.  MyObject should be a property of the current DataContext.

{Binding ElementName=MyTextBox, Path=Text} - Bind to the Text property of another XAML element named MyTextBox.

{Binding MyObject RelativeSource={RelativeSource Self}} - Bind to MyObject which should be a member of the current class.  So, I would expect MyObject to be an object or object graph defined as a partial class for this Page.

In addition to these there are many more advanced scenarios.  If you want to see more examples and see them in context, check out:

https://code.msdn.microsoft.com/windowsapps/Data-Binding-7b1d67b5

Admittedly, it is a Windows App Store project, however most of the ideas transfer over to binding with the Phone.

I’d also recommend that you read all the articles here:

https://msdn.microsoft.com/en-us/library/ms750612(v=vs.110).aspx

Admittedly, this is for Windows Presentation Foundation, however many of the ideas transfer over to binding with the Phone.

My final word on binding; in our examples so far, we’ve only seen one side of data binding — from a data source to the XAML.  However, you can also bind in a two-way fashion allowing changes to the data via input controls like a TextBox, ComboBox, CheckBox, RadioButton, etc. to update the underlying data source and anything binding to it, in turn.  In fact, this two-way binding is a basic tenet of a pattern known as MVVM, or rather, Model-View-ViewModel which we’ll examine in the next lesson.

Recap

We covered a lot of ground in this lesson.  Binding, and specifically data binding, is a key concept in building data driven applications based on a data model.  It is how you marry the data stored in a data file (like the SampleData.json) to the user interface.  My recommendation is to keep your structure as flat as possible as you’re getting started to ensure that you don’t confuse yourself as you set the DataContext for your app’s object graph.

Embed

Download

Download this episode

The Discussion

  • User profile image
    Phong Dao

    Hello Bob,

    Thank for you series. It's very good for me but I have problems I dont know how to reslove it.

    How to limit Items when binding? Full data have 10 items but I only want show 6 items and add button view more and when click view more navigate to page show full items.

Add Your 2 Cents