绑定分层数据并创建主视图/详细信息视图

注意 另请参阅 母版/细节示例

通过将项控件绑定到以链形式连接的 CollectionViewSource 实例,可以创建分层数据的多层主/详细(也称为列表-详情)视图。 在本主题中,我们尽可能使用 {x:Bind} 标记扩展,必要时使用性能较低但更灵活的 {Binding} 标记扩展

通用 Windows 平台(UWP)应用的一个常见结构是当用户在主列表中做出选择时导航到不同的详细信息页面。 如果要在层次结构中的每个级别提供每个项的丰富视觉表示形式,这非常有用。 另一个选项是在单个页面上显示多个级别的数据。 如果要显示几个简单的列表,让用户快速向下钻取到感兴趣的项,这非常有用。 本主题介绍如何实现此交互。 CollectionViewSource 实例负责跟踪每个分层级别的当前选择。

我们将创建一个体育队伍层次结构的视图,该结构按联赛、分区和球队进行组织,并包括一个球队详细信息视图。 从任何列表中选择项时,后续视图会自动更新。

体育层次结构的主视图/详细信息视图

先决条件

本主题假定你知道如何创建基本的 UWP 应用。 有关创建第一个 UWP 应用的说明,请参阅 使用 C# 或 Visual Basic创建第一个 UWP 应用。

创建项目

创建新的 空白应用程序(Windows 通用) 项目。 将其命名为“MasterDetailsBinding”。

创建数据模型

向项目添加新类,将其命名为ViewModel.cs,并将此代码添加到项目中。 这将是您的绑定源类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

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
                   };
        }
    }
}

创建视图

接下来,从表示您页面代码的类中公开绑定源类。 为此,我们将 LeagueList 类型的属性添加到 MainPage

namespace MasterDetailsBinding
{
    /// <summary>
    /// An empty page that can be used on its own or navigated to within a Frame.
    /// </summary>
    public sealed partial class MainPage : Page
    {
        public MainPage()
        {
            this.InitializeComponent();
            this.ViewModel = new LeagueList();
        }
        public LeagueList ViewModel { get; set; }
    }
}

最后,将 MainPage.xaml 文件的内容替换为以下标记,该标记声明三个 CollectionViewSource 实例,并将其绑定到链中。 然后,随后的控件可以根据层次结构中的级别绑定到相应的 CollectionViewSource

<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>
        <CollectionViewSource x:Name="Leagues"
            Source="{x:Bind ViewModel}"/>
        <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="{ThemeResource 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>

请注意,通过直接绑定到 CollectionViewSource,意味着当绑定中不能直接找到集合路径时,你是希望绑定到绑定中的当前项。 无需将 CurrentItem 属性指定为绑定的路径,但如果存在任何歧义,则可以执行此操作。 例如,用于表示团队视图的 ContentControlContent 属性被绑定到 TeamsCollectionViewSource。 但是,DataTemplate 中的控件 绑定到 Team 类的属性,因为 CollectionViewSource 在必要时会自动从团队列表中提供当前选定的团队。