Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Windows Presentation Foundation.docx
Скачиваний:
43
Добавлен:
02.11.2018
Размер:
1.15 Mб
Скачать

15. ПрИвязка к коллекциям и шаблоны данных

Рассмотрим следующий пример. Пусть имеется класс, описывающий задание для программиста, и класс с набором заданий:

public class Task

{

public string Name { get; set; }

public string Note { get; set; }

public int Priority { get; set; }

public TaskType Type { get; set; }

}

public enum TaskType { Coding, Testing, Support }

public class Tasks : List<Task>

{

public Tasks()

{

Add(new Task{ Name = "Data loading",

Note = "Need to test work with DB",

Priority = 2, Type = TaskType.Testing });

Add(new Task{ Name = "Log class",

Note = "Finish this work",

Priority = 2, Type = TaskType.Coding });

Add(new Task{ Name = "IoC Usage",

Note = "Find more info",

Priority = 4, Type = TaskType.Coding });

Add(new Task{ Name = "Urgent bug fixing",

Note = "Problem with class C",

Priority = 1, Type = TaskType.Support });

Add(new Task{ Name = "UI development",

Note = "Make markup for Main Window",

Priority = 1, Type = TaskType.Coding });

Add(new Task{ Name = "Help Doc",

Note = "Write technical documentation",

Priority = 3, Type = TaskType.Support });

Add(new Task{ Name = "New project!",

Note = "Plan the meeting",

Priority = 1, Type = TaskType.Support });

}

}

Необходимо создать приложение WPF, отображающее задания. Список заданий будет показан в элементе ListBox, связанном с объектом класса Tasks. Класс ItemsControl, являющийся предком всех списковых элементов управления, определяет два свойства, которые будут использоваться при привязке:

  • ItemsSource – указывает на коллекцию, содержащую все объекты, которые будут показаны в списке. В свойство ItemsSource можно поместить любой объект, реализующий интерфейс IEnumerable или его универсальную версию.

  • DisplayMemberPath – путь к свойству, которое будет применяться для создания отображаемого текста каждого элемента коллекции.

Детальная информация о выбранном задании будет отображаться в наборе текстовых полей. Для этого применяется привязка к свойству DataContext контейнера, обрамляющего поля.

<Window x:Class="TaskView.MainWindow" Title="List of Tasks"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

<StackPanel Margin="5" Orientation="Horizontal">

<ListBox Name="lstTasks" DisplayMemberPath="Name" Width="250"/>

<Grid Margin="5" DataContext="{Binding ElementName=lstTasks,

Path=SelectedItem}">

<Grid.ColumnDefinitions>

<ColumnDefinition Width="Auto" />

<ColumnDefinition Width="7" />

<ColumnDefinition Width="200" />

</Grid.ColumnDefinitions>

<Grid.RowDefinitions>

<RowDefinition Height="Auto" />

<RowDefinition Height="11" />

<RowDefinition Height="Auto" />

<RowDefinition Height="11" />

<RowDefinition Height="Auto" />

</Grid.RowDefinitions>

<TextBlock Grid.Row="0" Grid.Column="0" Text="Name:" />

<TextBox Grid.Row="0" Grid.Column="2"

Text="{Binding Name}" />

<TextBlock Grid.Row="2" Grid.Column="0" Text="Note:" />

<TextBox Grid.Row="2" Grid.Column="2"

Text="{Binding Note}" />

<TextBlock Grid.Row="4" Grid.Column="0" Text="Priority:" />

<TextBox Grid.Row="4" Grid.Column="2"

Text="{Binding Priority}" />

</Grid>

</StackPanel>

</Window>

// файл Code Behind

namespace TaskView

{

public partial class MainWindow : Window

{

public MainWindow()

{

InitializeComponent();

lstTasks.ItemsSource = new Tasks();

}

}

}

Рис. 39. Привязка списку, форма «главная - подробности».

Поддержка, которую даёт интерфейс IEnumerable, ограничена привязкой только для чтения – изменения, производимые в коллекции после привязки, в списковом элементе управления не отображаются. Чтобы включить отслеживание изменений, нужно использовать коллекцию с интерфейсом INotifyCollectionChanged. WPF включает единственную коллекцию, реализующую этот интерфейс, – это класс ObservableCollection<T>.

// изменённый класс MainWindow

public partial class MainWindow : Window

{

private readonly ObservableCollection<Task> tasks;

public MainWindow()

{

InitializeComponent();

tasks = new ObservableCollection<Task>(new Tasks());

lstTasks.ItemsSource = tasks;

}

}

Изменим внешний вид списка заданий при помощи шаблона данных. Шаблоны данных (data templates) – механизм для настройки отображения объектов определённого типа. В WPF шаблон данных – это объект класса System.Windows.DataTemplate. Основное свойство шаблона – VisualTree. Оно содержит визуальный элемент, определяющий внешний вид шаблона. Часто этим визуальным элементом является контейнер компоновки. В разметке XAML для задания VisualTree достаточно поместить в DataTemplate дочерний элемент. При формировании VisualTree обычно используется привязка данных для извлечения информации из объекта, для которого применяется шаблон. Сам шаблон данных, как правило, размещают в ресурсах окна или приложения.

С учётом вышесказанного определим шаблон данных в ресурсах окна и используем свойство списка ItemTemplate для применения шаблона к каждому элементу списка:

<!-- определяем шаблон в ресурсах окна -->

<Window.Resources>

<DataTemplate x:Key="taskTemplate">

<Border Name="border" BorderBrush="Aqua" BorderThickness="1"

CornerRadius="2" Padding="5" Margin="5">

<TextBlock FontSize="14" FontWeight="Bold"

Text="{Binding Name}" />

</Border>

</DataTemplate>

</Window.Resources>

<!-- изменённая настройка ListBox -->

<ListBox Name="lstTasks" HorizontalContentAlignment="Stretch"

ItemTemplate="{StaticResource taskTemplate}" Width="250" />

Рис. 40. Список, к которому применён шаблон данных.

У класса DataTemplate имеется свойство DataType, определяющее тип данных, к которому будет применяться шаблон. Если задано это свойство, WPF будет использовать шаблон в любой ситуации, где до этого выводилась строка с результатом ToString():

<!-- не указываем ключ ресурса, если шаблон в ресурсах -->

<!-- подключено xmlns:TaskView="clr-namespace:TaskView" -->

<DataTemplate DataType="{x:Type TaskView:Task}">

<!-- описание шаблона не изменилось -->

</DataTemplate>

<!-- в ListBox не задаём свойство ItemTemplate -->

<ListBox Name="lstTasks" Width="250" HorizontalContentAlignment="Stretch"/>

В шаблон данных можно поместить триггеры данных. Следующий пример показывает использование триггера для того, чтобы изменить цвет окантовки задач из группы TaskType.Support:

<!-- эта разметка – часть шаблона DataTemplate -->

<DataTemplate.Triggers>

<DataTrigger Binding="{Binding Path=Type}" Value="Support">

<Setter TargetName="border" Property="BorderBrush"

Value="Brown" />

</DataTrigger>

</DataTemplate.Triggers>

Обратите внимание – элемент <Setter> содержит установку TargetName. Как ясно из контекста, TargetName используется, чтобы обратиться к именованному дочернему элементу визуального представления шаблона данных. Дочерний элемент должен быть описан до триггера.

В WPF списковые элементы управления поддерживают возможность выбора для объекта одного из нескольких шаблонов данных. Предположим, что в примере со списком заданий требуется особым образом отображать задания с приоритетом, равным 1. Определим в ресурсах окна ещё один шаблон данных:

<DataTemplate x:Key="importantTask">

<Border BorderBrush="Red" BorderThickness="2" CornerRadius="2"

Padding="5" Margin="5">

<TextBlock FontSize="18" Foreground="Red" Text="{Binding Name}"/>

</Border>

</DataTemplate>

Выбор шаблона выполняется программно, с помощью создания подкласса для DataTemplateSelector и переопределения метода SelectTemplate():

public class TaskTemplateSelector : DataTemplateSelector

{

public override DataTemplate SelectTemplate(object item,

DependencyObject container)

{

if (item != null && item is Task)

{

var task = item as Task;

Window window = Application.Current.MainWindow;

return task.Priority == 1 ?

window.FindResource("importantTask") as DataTemplate :

window.FindResource(new DataTemplateKey(typeof(Task)))

as DataTemplate;

}

return null;

}

}

Затем можно объявить объект TaskTemplateSelector в качестве ресурса и назначить этот ресурс свойству ListBox.ItemTemplateSelector. Объект ListBox вызывает метод SelectTemplate() для каждого элемента в базовой коллекции.

<ListBox Name="lstTasks" Width="250"

HorizontalContentAlignment="Stretch"

ItemTemplateSelector="{StaticResource selector}"/>

Рис. 41. Селектор шаблонов в действии.

При работе с иерархическими элементами управления (например, TreeView) вместо шаблона данных на основе DataTemplate следует использовать HiererchicalDataTemplate. У такого шаблона имеется свойство ItemsSource, которое нужно связать с дочерней коллекцией, и свойство ItemTemplate – дочерний шаблон данных (DataTemplate или HiererchicalDataTemplate).