Адаптивные триггеры и шаблоны данных

Последнее обновление: 25.11.2017

Шаблон данных, описываемый элементом DataTemplate, имеет одно важное ограничение - мы не можем применять визуальные состояния Visual State к элементам, которые определены в шаблоне DataTemplate. Это может быть критичным обстоятельством, если мы создаем адаптивное приложение, которое должно по-разному отображать данные в зависимости от ширины экрана.

К счастью, эта проблема легко решается с помощью создания своего визуального компонента, в который выносится шаблон данных. Возьмем проект из прошлой темы, где у нас был определен класс Phone:

public class Phone
{
    public int Id { get; set; }
    public string Title { get; set; } // модель телефона
    public string Company { get; set; } // производитель
    public string ImagePath { get; set; } // путь к изображению
}

Добавим в этот проект новый элемент по типу User Control, который назовем PhoneTemplate:

DataTemplate и UserControl в UWP

Определим в коде PhoneTemplate.xaml следующую разметку:

<UserControl
    x:Class="DataApp.PhoneTemplate"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="using:DataApp"
    xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
    xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
    mc:Ignorable="d"
    d:DesignHeight="300"
    d:DesignWidth="400">

    <StackPanel Margin="10">
        <VisualStateManager.VisualStateGroups>
            <VisualStateGroup>
                <VisualState x:Name="Desktop">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="600" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="phoneImage.Width" Value="150" />
                        <Setter Target="phoneImage.Height" Value="100" />
                        <Setter Target="phoneImage.HorizontalAlignment" Value="Center" />
                        <Setter Target="phoneTitle.HorizontalAlignment" Value="Center" />
                        <Setter Target="phoneCompany.HorizontalAlignment" Value="Center" />
                        <Setter Target="phoneTitle.FontSize" Value="16" />
                        <Setter Target="phoneCompany.FontSize" Value="16" />
                    </VisualState.Setters>
                </VisualState>
                <VisualState x:Name="Mobile">
                    <VisualState.StateTriggers>
                        <AdaptiveTrigger MinWindowWidth="0" />
                    </VisualState.StateTriggers>
                    <VisualState.Setters>
                        <Setter Target="phoneImage.Width" Value="100" />
                        <Setter Target="phoneImage.Height" Value="70" />
                        <Setter Target="phoneImage.HorizontalAlignment" Value="Left" />
                        <Setter Target="phoneTitle.HorizontalAlignment" Value="Left" />
                        <Setter Target="phoneCompany.HorizontalAlignment" Value="Left" />
                        <Setter Target="phoneTitle.FontSize" Value="14" />
                        <Setter Target="phoneCompany.FontSize" Value="14" />
                    </VisualState.Setters>
                </VisualState>
            </VisualStateGroup>
        </VisualStateManager.VisualStateGroups>
        
        <Image Name="phoneImage" Source="{x:Bind Phone.ImagePath}" />
        <TextBlock Name="phoneTitle" Text="{x:Bind Phone.Title}" />
        <TextBlock Name="phoneCompany" Text="{x:Bind Phone.Company}" />
    </StackPanel>
</UserControl>

Фактически мы определяем в xaml нашего компонента весь тот код, который представляет DataTemplate или шаблон отображения одного элемента Phone. И также здесь определены визуальные состояния для окна приложения шириной до 600 единиц и больше 600 единиц.

Кроме того, здесь привязка элементов идет к свойствам объекта Phone, который надо еще определить. Для этого перейдем к коду c# нашего компонента - файлу PhoneTemplate.xaml.cs и определим в нем следующее содержимое:

using Windows.UI.Xaml.Controls;

namespace DataApp
{
    public sealed partial class PhoneTemplate : UserControl
    {
        public Phone Phone { get { return this.DataContext as Phone; } }
        public PhoneTemplate()
        {
            this.InitializeComponent();
        }
    }
}

Во-первых, здесь определяется свойство Phone, которое представляет объект Phone. Причем данные для этого свойства извлекаются из контекста данных DataContext. Контекст данных будет устанавливаться из MainPage.

Теперь применим наш компонент PhoneTemplate в коде главной страницы. Для этого изменим MainPage.xaml следующим образом:

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

    <Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">

        <ListBox Name="phonesList" ItemsSource="{x:Bind Phones}">
            <ListBox.ItemTemplate>
                <DataTemplate x:DataType="local:Phone">
                    <local:PhoneTemplate />
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</Page>

Передаваемый в DataTemplate объект Phone как и будет представлять контекст данных для компонента PhoneTemplate.

Код MainPage.xaml.cs остается прежним:

using System.Collections.ObjectModel;
using Windows.UI.Xaml.Controls;

namespace DataApp
{
    public sealed partial class MainPage : Page
    {
        public ObservableCollection<Phone> Phones { get; set; }

        public MainPage()
        {
            this.InitializeComponent();

			Windows.UI.ViewManagement.ApplicationView appView = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView();
            appView.SetPreferredMinSize(new Size(400, 300));	
			
            Phones = new ObservableCollection<Phone>
            {
                new Phone {Id=1, ImagePath="/Assets/iphone6s.jpg", Title="iPhone 7", Company="Apple" },
                new Phone {Id=2, ImagePath="/Assets/lumia950.jpg", Title="HP Elite z3", Company="HP" },
                new Phone {Id=3, ImagePath="/Assets/mate8.jpg", Title="Huawei P10", Company="Huawei" },
                new Phone {Id=4, ImagePath="/Assets/galaxys6.jpg", Title="Galaxy S8", Company="Samsung"}
            };
        }
    }
}

В итоге отображение списка объектов будет изменяться в зависимости от ширины экрана.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850