Шаблон данных, описываемый элементом 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:
Определим в коде 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"} }; } } }
В итоге отображение списка объектов будет изменяться в зависимости от ширины экрана.