- •А.А. Волосевич
- •1. ОбщееописаниеWpf
- •2. ПРостейшее ПриложениеWpf
- •4. Базовые концепции wpf Иерархия классов
- •Свойства зависимостейи присоединённые свойства
- •Маршрутизируемыесобытия
- •Многопоточность в wpf
- •5. СтруктуРа Оконного приложения wpf
- •Класс Window
- •КлассApplication
- •6. Компоновка
- •Размер и выравнивание
- •Основные контейнеры компоновки
- •Прокрутка и декорирование содержимого
- •7. Обзор элеменТов управления
- •Цвета и окантовка:
- •Шрифт содержимого:
- •Элементы управления содержимым
- •Списковые элементы управления
- •Прочие элементы управления
- •8. Фигуры
- •9. Цвет,кисти, прозрачность Представление цвета в wpf
- •Лучшие кисти
- •Прозрачность
- •10. Трансформации и эффекты
- •11. Классы drawing иvisual
- •12. Ресурсы
- •Двоичные ресурсы
- •Логические ресурсы
- •13. Привязка данных Базовые концепции привязки данных
- •Практическое использование привязки данных
- •Конвертеры значений
- •Проверка данных
- •14. Стилии триггеры
- •15. ПрИвязкакколлекциямиШаблоныданных
- •16. Представления Данных
- •17. ШАблоны элементов управления
15. ПрИвязкакколлекциямиШаблоныданных
Рассмотрим следующий пример. Пусть имеется класс, описывающий задание для программиста, и классс набором заданий:
publicclassTask
{
publicstring Name { get; set; }
publicstring Note { get; set; }
publicint Priority { get; set; }
publicTaskType Type { get; set; }
}
publicenumTaskType { Coding, Testing, Support }
publicclassTasks : List<Task>
{
public Tasks()
{
Add(newTask{ Name = "Data loading",
Note = "Need to test work with DB",
Priority = 2, Type = TaskType.Testing });
Add(newTask{ Name = "Log class",
Note = "Finish this work",
Priority = 2, Type = TaskType.Coding });
Add(newTask{ Name = "IoC Usage",
Note = "Find more info",
Priority = 4, Type = TaskType.Coding });
Add(newTask{ Name = "Urgent bug fixing",
Note = "Problem with class C",
Priority = 1, Type = TaskType.Support });
Add(newTask{ Name = "UI development",
Note = "Make markup for Main Window",
Priority = 1, Type = TaskType.Coding });
Add(newTask{ Name = "Help Doc",
Note = "Write technical documentation",
Priority = 3, Type = TaskType.Support });
Add(newTask{ 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
{
publicpartialclassMainWindow : Window
{
public MainWindow()
{
InitializeComponent();
lstTasks.ItemsSource = newTasks();
}
}
}
Рис. 39.Привязка списку, форма «главная-подробности».
Поддержка, которую даёт интерфейс IEnumerable, ограничена привязкой только для чтения – изменения, производимые в коллекции после привязки, в списковом элементе управления не отображаются. Чтобы включить отслеживание изменений, нужно использовать коллекцию с интерфейсом INotifyCollectionChanged. WPF включает единственную коллекцию, реализующую этот интерфейс, – это класс ObservableCollection<T>.
// изменённыйклассMainWindow
publicpartialclassMainWindow : Window
{
privatereadonlyObservableCollection<Task> tasks;
public MainWindow()
{
InitializeComponent();
tasks = newObservableCollection<Task>(newTasks());
lstTasks.ItemsSource = tasks;
}
}
Изменим внешний вид списка заданий при помощи шаблона данных. Шаблоны данных (datatemplates) – механизм для настройки отображенияобъектов определённого типа. В 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():
publicclassTaskTemplateSelector : DataTemplateSelector
{
publicoverrideDataTemplate SelectTemplate(object item,
DependencyObject container)
{
if (item != null&& item isTask)
{
var task = item asTask;
Window window = Application.Current.MainWindow;
return task.Priority == 1?
window.FindResource("importantTask")asDataTemplate :
window.FindResource(newDataTemplateKey(typeof(Task)))
asDataTemplate;
}
returnnull;
}
}
ЗатемможнообъявитьобъектTaskTemplateSelectorвкачествересурсаиназначитьэтотресурссвойствуListBox.ItemTemplateSelector. ОбъектListBoxвызываетметодSelectTemplate()длякаждогоэлементавбазовойколлекции.
<ListBoxName="lstTasks" Width="250"
HorizontalContentAlignment="Stretch"
ItemTemplateSelector="{StaticResource selector}"/>
Рис. 41.Селектор шаблонов в действии.
При работе с иерархическими элементами управления (например, TreeView) вместо шаблона данных на основе DataTemplate следует использовать HiererchicalDataTemplate.У такого шаблона имеется свойство ItemsSource, которое нужно связать с дочерней коллекцией, и свойство ItemTemplate – дочернийшаблон данных (DataTemplate или HiererchicalDataTemplate).