Привязка к иерархическим данным и создание представления основных и подробных данных

Примечание. См. также Пример основных и подробных данных.

Вы можете создать многоуровневое представление основных и подробных данных (также известное как список-подробности) иерархических данных путем привязки элементов управления элементами управления к экземплярам CollectionViewSource , связанным в цепочке. В этом разделе мы используем расширение разметки {x:Bind}, где это возможно, и более гибкое (но менее производительное) расширение разметки {Binding} при необходимости.

Одна из распространенных структур для приложений универсальная платформа Windows (UWP) — переход к различным страницам сведений при выборе в главном списке. Это полезно, если требуется предоставить богатое визуальное представление каждого элемента на каждом уровне иерархии. Другим вариантом является отображение нескольких уровней данных на одной странице. Это полезно, если вы хотите отобразить несколько простых списков, которые позволяют пользователю быстро детализировать интересующий элемент. В этом разделе описывается, как реализовать это взаимодействие. Экземпляры CollectionViewSource отслеживают текущий выбор на каждом иерархическом уровне.

Мы создадим представление иерархии спортивной команды, которая организована в списки для лиг, подразделений и команд, и включает представление сведений о команде. При выборе элемента из любого списка последующие представления обновляются автоматически.

master/details view of a sports hierarchy

Необходимые компоненты

В этом разделе предполагается, что вы знаете, как создать базовое приложение UWP. Инструкции по созданию первого приложения UWP см. в статье "Создание первого приложения UWP с помощью C# или Visual Basic".

Создание проекта

Создайте проект пустого приложения (универсального приложения 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 в качестве пути для привязки, хотя это можно сделать, если есть какая-либо неоднозначность. Например, ContentControl, представляющий представление группы, имеет свойство Content, привязанное к TeamsCollectionViewSource. Однако элементы управления в DataTemplate привязываются к свойствам Team класса, так как CollectionViewSource автоматически предоставляет выбранную команду из списка команд при необходимости.