- •А.А. Волосевич
- •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. ШАблоны элементов управления
Практическое использование привязки данных
Построим пример разметки XAML, в котором привязка соединяет два свойства разных элементов управления. Для привязки данных определено расширения разметки {Binding}, что позволяет настроить привязку декларативно. Сделаем так, чтобы при перемещении ползунка слайдера автоматически менялся размер строки текста1:
<StackPanel>
<Slider Name="slider" Margin="3" Minimum="1" Maximum="40" />
<TextBlock Margin="10" Text="Sample Text" Name="lblText"
FontSize="{Binding ElementName=slider, Path=Value}"/>
</StackPanel>
Рис. 36. Привязка для элементов управления.
Привязку можно выполнить не только декларативно, но и программно (а в некоторых ситуациях это единственный вариант). Ниже приведён код программной привязки, эквивалентной примеру разметки XAML:
Binding binding = new Binding();
binding.Source = slider;
binding.Path = new PropertyPath("Value");
lblText.SetBinding(TextBlock.FontSizeProperty, binding);
Заданную привязку можно удалить в коде, используя два статических метода класса BindingOperations. Метод ClearBinding() принимает ссылку на свойство зависимостей, которое имеет привязку, подлежащую удалению, а ClearAllBindings() удаляет все привязки элемента:
BindingOperations.ClearAllBindings(lblText);
Следующий пример XAML демонстрирует двунаправленную привязку: при перемещении ползунка слайдера меняется текст в TextBox, а корректное изменение текста (допустимы вещественные числа) ведёт к перемещению ползунка. Обратите внимание на установку UpdateSourceTrigger=PropertyChanged. Это обеспечивает мгновенные изменения позиции ползунка прямо при вводе текста.
<StackPanel>
<Slider Name="slider" Minimum="1" Maximum="40" Value="10" />
<TextBox Name="txtSize"
Text="{Binding ElementName=slider, Path=Value,
Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
</StackPanel>
Если UpdateSourceTrigger установить в UpdateSourceTrigger.Explicit, требуется написать код, вызывающий для обновления метод UpdateSource() у объекта BindingExpression, связанного с элементом:
// получить выражение привязки для текстового поля
BindingExpression binding =
txtSize.GetBindingExpression(TextBox.TextProperty);
// обновить связанный источник (Slider)
binding.UpdateSource();
Рассмотрим сценарии, в которых источником привязки является не элемент WPF, а обычный объект. В этом случае вместо ElementName используется одно из описанных ниже свойств:
-
Source – свойство привязки, указывающее на объект-источник.
-
RelativeSource – свойство привязки, которое указывает на источник относительно текущего элемента. Это подход, часто применяемый при разработке шаблонов элементов управления и шаблонов данных.
-
DataContext – наследуемое свойство FrameworkElement. Если источник не указан через Source или RelativeSource, WPF выполняет поиск по дереву элементов, начиная с текущего элемента. В качестве источника данных будет использоваться первый DataContext, не равный null.
Следующий пример разметки XAML демонстрирует использование свойства Source для связывания с объектом класса Person. Так как объект-источник должен существовать на момент выполнения привязки, этот объект помещается в словарь ресурсов окна.
<Window x:Class="WpfApplication.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:WpfApplication">
<Window.Resources>
<local:Person x:Key="person" Name="Mr. Smith" Age="27.3" />
</Window.Resources>
<StackPanel>
<TextBox Text="{Binding Source={StaticResource person},
Path=Name}" />
</StackPanel>
</Window>
public class Person
{
public string Name { get; set; }
public double Age { get; set;}
public string Address { get; set; }
}
Модификация примера использует для размещения объекта привязки свойство DataContext у контейнера StackPanel:
<!--пропущена настройка окна и объявление ресурса1-->
<StackPanel DataContext="{StaticResource person}">
<TextBox Text="{Binding Name}"/>
</StackPanel>
Свойство привязки RelativeSource позволяет указать объект-источник на основе его отношения к целевому объекту. Например, можно использовать RelativeSource для привязки элемента к самому себе или к родительскому элементу, находящемуся на неизвестное число шагов вверх по дереву элементов. Для установки свойства Binding.RelativeSource используется объект класса RelativeSource. При этом можно использовать как специальное расширение разметки, так и синтаксис, основанный на элементах-свойствах.
<!-- используем элемент-свойство XAML -->
<TextBlock>
<TextBlock.Text>
<Binding Path="Title">
<Binding.RelativeSource>
<RelativeSource Mode="FindAncestor"
AncestorType="{x:Type Window}" />
</Binding.RelativeSource>
</Binding>
</TextBlock.Text>
</TextBlock>
<!-- используем расширение разметки -->
<TextBlock Text="{Binding Path=Title,
RelativeSource={RelativeSource FindAncestor,
AncestorType={x:Type Window}}}" />
В табл. 13 перечислены четыре возможных режима для RelativeSource.
Таблица 13
Значения перечисления RelativeSourceMode
Имя |
Описание |
Self |
Выражение привязывает к другому свойству того же элемента |
FindAncestor |
Выражение привязывает к родительскому элементу. WPF будет производить поиск вверх по дереву элементов, пока не найдёт нужного родителя. Чтобы специфицировать родителя, необходимо также установить свойство AncestorType для указания типа родительского элемента. Можно использовать свойство AncestorLevel, чтобы пропустить определённое число вхождений указанного элемента |
PreviousData |
Выражение привязывает к предыдущему элементу данных в списке |
TemplatedParent |
Выражение привязывает к элементу, к которому применён шаблон. Этот режим работает, только если привязка расположена внутри шаблона элемента управления или шаблона данных |
Если при привязке к объекту .NET необходимо, чтобы привязка отслеживала изменения в объекте, можно поступить одним из следующих способов:
-
Сделать отслеживаемые свойства объекта-источника свойствами зависимостей, а сам объект наследовать от DependencyObject.
-
Инициировать при изменении свойства события ИмяСвойстваChanged.
-
Реализовать в объекте-источнике интерфейс INotifyPropertyChanged, содержащий событие PropertyChanged. Это событие нужно инициировать всякий раз, когда свойство изменяется, передавая имя свойства.
Ниже приведена реализация класса Person, использующая третий подход:
public class Person : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected virtual void OnPropertyChanged(string propertyName)
{
if (PropertyChanged != null)
PropertyChanged(this,
new PropertyChangedEventArgs(propertyName));
}
private string _name;
public string Name
{
get {return _name}
set {
if (value != _name)
{
_name = value;
OnPropertyChanged("Name");
}
}
} // остальные свойства надо реализовать аналогично
}