Все элементы управления списками имеют свойство ItemTemplate, которое позволяет задать свой шаблон отображения данных. В качестве значения оно принимает объект DataTemplate. В качестве самих данных, которые отображаются в DataTemplate, как правило, выступают объекты пользовательских классов, создаваемых самим разработчиком. Сам шаблон данных представляет разметку xaml, которая управляет визуализацией элемента.
Итак, добавим в проект следующий класс Phone, который будет представлять модель телефона:
public class Phone { public int Id { get; set; } public string Title { get; set; } // модель телефона public string Company { get; set; } // производитель public string ImagePath { get; set; } // путь к изображению }
В коде xaml определим шаблон данных для списка смартфонов:
<Window x:Class="DataApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DataApp" mc:Ignorable="d" Title="MainWindow" Height="250" Width="300"> <Grid> <ListBox x:Name="phonesList" SelectionChanged="phonesList_SelectionChanged"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Margin="5"> <Image Width="100" Height="75" Source="{Binding Path=ImagePath}" /> <TextBlock FontSize="16" Text="{Binding Path=Title}" HorizontalAlignment="Center" /> <TextBlock FontSize="16" Text="{Binding Path=Company}" HorizontalAlignment="Center" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Window>
Итак, здесь шаблон данных представлен следующей разметкой:
<DataTemplate> <StackPanel Margin="5"> <Image Width="100" Height="75" Source="{Binding Path=ImagePath}" /> <TextBlock FontSize="16" Text="{Binding Path=Title}" HorizontalAlignment="Center" /> <TextBlock FontSize="16" Text="{Binding Path=Company}" HorizontalAlignment="Center" /> </StackPanel> </DataTemplate>
Для каждого элемента применяется привязка к определенному свойству объекта Phone. То есть фактически в визуальном плане каждый элемент будет представлять собой StackPanel с набором элементов Image и TextBlock.
Далее создадим в коде C# коллекцию смартфонов и установим ее в качестве источника данных:
public partial class MainWindow : Window { public ObservableCollection<Phone> Phones { get; set; } public MainWindow() { InitializeComponent(); Phones = new ObservableCollection<Phone> { new Phone {Id=1, ImagePath="/Images/iphone6s.jpg", Title="iPhone 6S", Company="Apple" }, new Phone {Id=2, ImagePath="/Images/lumia950.jpg", Title="Lumia 950", Company="Microsoft" }, new Phone {Id=3, ImagePath="/Images/nexus5x.jpg", Title="Nexus 5X", Company="Google" }, new Phone {Id=4, ImagePath="/Images/galaxys6.jpg", Title="Galaxy S6", Company="Samsung"} }; phonesList.ItemsSource = Phones; } private void phonesList_SelectionChanged(object sender, SelectionChangedEventArgs e) { Phone p = (Phone)phonesList.SelectedItem; MessageBox.Show(p.Title); } }
Здесь создается 4 объекта Phone. Для каждого объекта устанавливаются необходимые значения. Причем свойство ImagePath указывает на путь к изображению в папке Images. Предполагается, что в проект добавлена папка Images, в которой есть четыре изображения для каждой из моделей.
И также здесь определяется обработчик выбора элемента в списке. Несмотря на то, что список представляет сложные данные, мы все равно сможем обработать выбор пользователя и получить выбранный объект. И так как DataTemplate отображает объекты Phone, то выделенный элемент мы можем привести к типу Phone.
В итоге получится следующее приложение:
Одним из преимуществ шаблонов данных является то, что их можно вынести во вне, например, в ресурсы. В этом случае мы повторно сможем использовать одни и тот же шаблон данных для разных элементов в разных частях программы:
<Window x:Class="DataApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DataApp" mc:Ignorable="d" Title="MainWindow" Height="250" Width="300"> <Window.Resources> <DataTemplate x:Key="listTemplate"> <StackPanel Margin="5"> <Image Width="100" Height="75" Source="{Binding Path=ImagePath}" /> <TextBlock FontSize="16" Text="{Binding Path=Title}" HorizontalAlignment="Center" /> <TextBlock FontSize="16" Text="{Binding Path=Company}" HorizontalAlignment="Center" /> </StackPanel> </DataTemplate> </Window.Resources> <Grid> <ListBox x:Name="phonesList" ItemTemplate="{StaticResource listTemplate}" SelectionChanged="phonesList_SelectionChanged" /> </Grid> </Window>
С помощью триггеров данных (data triggers) можно задать дополнительную логику визуализации, которая срабатывает, если свойство привязанного объекта принимает то или иное значение. Например, в случае с примером выше, допустим, мы хотим, если у отображаемого телефона свойство Company равно Microsoft, то этот элемент выделяется жирным или выделяется красным цветом. И для этого добавим в шаблон данных DataTemplate триггер данных:
<Window x:Class="DataApp.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DataApp" mc:Ignorable="d" Title="MainWindow" Height="250" Width="300"> <Window.Resources> <DataTemplate x:Key="listTemplate"> <StackPanel Margin="5"> <Border x:Name="phoneImageBorder"> <Image Width="100" Name="phoneImage" Height="75" Source="{Binding Path=ImagePath}" /> </Border> <TextBlock FontSize="16" Name="phoneTitle" Text="{Binding Path=Title}" HorizontalAlignment="Center" /> <TextBlock FontSize="16" Text="{Binding Path=Company}" HorizontalAlignment="Center" /> </StackPanel> <DataTemplate.Triggers> <DataTrigger Binding="{Binding Company}" Value="Microsoft"> <Setter TargetName="phoneTitle" Property="FontWeight" Value="Bold" /> <Setter TargetName="phoneImageBorder" Property="BorderBrush" Value="Red" /> <Setter TargetName="phoneImageBorder" Property="BorderThickness" Value="3" /> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </Window.Resources> <Grid> <ListBox x:Name="phonesList" ItemTemplate="{StaticResource listTemplate}" SelectionChanged="phonesList_SelectionChanged" /> </Grid> </Window>
Для визуализации изображение помещено в элемент Border, который имеет имя "phoneImageBorder". С помощью триггера данных у этого элемента изменяется ширин и цвет границы. Кроме того, меняется жирность шрифта, который выводит название модели:
<DataTrigger Binding="{Binding Company}" Value="Microsoft"> <Setter TargetName="phoneTitle" Property="FontWeight" Value="Bold" /> <Setter TargetName="phoneImageBorder" Property="BorderBrush" Value="Red" /> <Setter TargetName="phoneImageBorder" Property="BorderThickness" Value="3" /> </DataTrigger>
В итоге мы получим следующий визуальный эффект: