How to bind to hierarchical data and create a master/details view (XAML)

Applies to Windows and Windows Phone

You can make a multi-level master/details view of hierarchical data by binding list controls to CollectionViewSource instances.

One common structure for Windows Runtime apps is to navigate to different details pages when a user makes a selection in a master list. This is useful when you want to provide a rich visual representation of each item at every level in a hierarchy. The Grid Application and Split Application templates in Visual Studio demonstrate how to use this technique.

Another option is to display multiple levels of data on a single page. This is useful when you want to display a few simple lists that let people quickly drill down to an item of interest. This topic describes how to implement this technique by using multiple CollectionViewSource instances. These instances keep track of the current selection at each level.

The following example creates a view of a sports hierarchy that is organized into lists for leagues, divisions, and teams, and includes a team details view. When you select an item from any list, the subsequent views update automatically.

master/details view of a sports hierarchy

Roadmap: How does this topic relate to others? See:

Prerequisites

This topic assumes that you can create a basic Windows Runtime app using C++, C#, or Visual Basic. For instructions on creating your first Windows Runtime app, see Create your first Windows Store app using C# or Visual Basic.

Instructions

Step 1: Create the project

Create a new Windows Runtime app named "MasterDetailsBinding", by using the Blank Application template.

Step 2: Create the data model

Add a new class file to your project and replace its contents with the following code.

This code shows a simple data model that also generates sample data for display in the designer and at run time.


using System;
using System.Collections.Generic;
using System.Linq;

namespace MasterDetailsBinding
{
    public class Team
    {
        public string Name { get; set; }
        public int Wins { get; set; }
        public int Losses { get; set; }
    }

    public class Division
    {
        public string Name { get; set; }
        public IEnumerable<Team> Teams { get; set; }
    }

    public class League
    {
        public string Name { get; set; }
        public IEnumerable<Division> Divisions { get; set; }
    }

    public class LeagueList : List<League>
    {
        public LeagueList()
        {
            this.AddRange(GetLeague().ToList());
        }

        public IEnumerable<League> GetLeague()
        {
            return from x in Enumerable.Range(1, 2) select new League
            {
                Name = "League " + x,
                Divisions = GetDivisions(x).ToList()
            };
        }

        public IEnumerable<Division> GetDivisions(int x)
        {
            return from y in Enumerable.Range(1, 3) select new Division
            {
                Name = String.Format("Division {0}-{1}", x, y),
                Teams = GetTeams(x, y).ToList()
            };
        }

        public IEnumerable<Team> GetTeams(int x, int y)
        {
            return from z in Enumerable.Range(1, 4) select new Team
            {
                Name = String.Format("Team {0}-{1}-{2}", x, y, z),
                Wins = 25 - (x * y * z),
                Losses = x * y * z
            };
        }
    }
}


Step 3: Create the view

Replace the contents of the MainPage.xaml file with the following code.

This code shows the XAML that defines the view. The XAML first declares the sample data source (the local:LeagueList element) and three CollectionViewSource instances, and binds them together in a chain. The subsequent controls can then bind to the appropriate CollectionViewSource depending on the level in the hierarchy.


<Page
    x:Class="MasterDetailsBinding.MainPage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:MasterDetailsBinding"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d">

  <Page.Resources>

    <local:LeagueList x:Key="LeagueData"/>
    <CollectionViewSource x:Name="Leagues" 
      Source="{StaticResource LeagueData}"/>
    <CollectionViewSource x:Name="Divisions"
      Source="{Binding Divisions, Source={StaticResource Leagues}}"/>
    <CollectionViewSource x:Name="Teams"
      Source="{Binding Teams, Source={StaticResource Divisions}}"/>

    <Style TargetType="TextBlock">
      <Setter Property="FontSize" Value="15"/>
      <Setter Property="FontWeight" Value="Bold"/>
    </Style>

    <Style TargetType="ListBox">
      <Setter Property="FontSize" Value="15"/>
    </Style>

    <Style TargetType="ContentControl">
      <Setter Property="FontSize" Value="15"/>
    </Style>

  </Page.Resources>

  <Grid Background="{StaticResource ApplicationPageBackgroundThemeBrush}">

    <StackPanel Orientation="Horizontal">

      <!-- All Leagues view -->

      <StackPanel Margin="5">
        <TextBlock Text="All Leagues"/>
        <ListBox ItemsSource="{Binding Source={StaticResource Leagues}}" 
          DisplayMemberPath="Name"/>
      </StackPanel>

      <!-- League/Divisions view -->

      <StackPanel Margin="5">
        <TextBlock Text="{Binding Name, Source={StaticResource Leagues}}"/>
        <ListBox ItemsSource="{Binding Source={StaticResource Divisions}}" 
          DisplayMemberPath="Name"/>
      </StackPanel>

      <!-- Division/Teams view -->

      <StackPanel Margin="5">
        <TextBlock Text="{Binding Name, Source={StaticResource Divisions}}"/>
        <ListBox ItemsSource="{Binding Source={StaticResource Teams}}" 
          DisplayMemberPath="Name"/>
      </StackPanel>

      <!-- Team view -->

      <ContentControl Content="{Binding Source={StaticResource Teams}}">
        <ContentControl.ContentTemplate>
          <DataTemplate>
            <StackPanel Margin="5">
              <TextBlock Text="{Binding Name}" 
                FontSize="15" FontWeight="Bold"/>
              <StackPanel Orientation="Horizontal" Margin="10,10">
                <TextBlock Text="Wins:" Margin="0,0,5,0"/>
                <TextBlock Text="{Binding Wins}"/>
              </StackPanel>
              <StackPanel Orientation="Horizontal" Margin="10,0">
                <TextBlock Text="Losses:" Margin="0,0,5,0"/>
                <TextBlock Text="{Binding Losses}"/>
              </StackPanel>
            </StackPanel>
          </DataTemplate>          
        </ContentControl.ContentTemplate>
      </ContentControl>

    </StackPanel>

  </Grid>
</Page>


Whenever a binding specifies a property, it automatically uses the value from the currently selected item in the bound CollectionViewSource. For example, the ContentControl representing the team view has its Content property bound to the Teams CollectionViewSource. However, the controls in the DataTemplate bind to properties of the Team class because the CollectionViewSource automatically supplies the currently selected team from the teams list.

Related topics

Roadmaps
Roadmap for Windows Runtime apps using C# or Visual Basic
Roadmap for Windows Runtime apps using C++
Samples
XAML data binding sample
XAML GridView grouping and SemanticZoom sample
StorageDataSource and GetVirtualizedFilesVector sample
Reference
Binding
DataContext
DependencyProperty
CollectionViewSource
DataTemplate
Concepts
Quickstart: Data binding to controls
Data binding with XAML
Binding markup extension
Property-path syntax
RelativeSource markup extension
Dependency properties overview
Custom dependency properties
Attached properties overview
Create your first Windows Store app using C# or Visual Basic

 

 

Show:
© 2014 Microsoft