Практическая работа № 6
Тема: Привязка данных. Использование стилей.
Триггеры в WPF-приложениях.
В WPF привязка (binding) является мощным инструментом программирования, без которого не обходится ни одно серьезное приложение.
Привязка подразумевает взаимодействие двух объектов: источника и приемника. Объект- приемник создает привязку к определенному свойству объекта-источника. В случае модификации объекта-источника, объект-приемник также будет модифицирован.
Рассмотрим пример приложения из двух элементов управления: ползунка (Slider) и текстового блока (TextBlock). При изменении положения ползунка размер шрифта текстового блока
должен меняться. Такое поведение можно реализовать за счет обработки события изменения положения ползунка ValueChaned:
Пример№1 Код XAML
Slider Minimum="8" Maximum="30" ValueChanged="Slider_ValueChanged" /
StackPanel Margin="0,30,0,0"
TextBlock x:Name="Message" FontSize="20"
Пример WPF-приложения для демонстрации привязки данных
TextBlock
StackPanel
Код C#
private void Slider_ValueChanged(object sender, RoutedPropertyChangedEventArgsdouble e)
{
if (Message != null)
Message.FontSize = ((Slider)sender).Value;
}
Как видно из исходного кода, возникает необходимость проверки существования объекта Message, т.к. первый вызов обработчика Slider_ValueChanged происходит в момент обработки элемента Slider XAML-файла, когда элемент TextBlock еще не обработан и, соответственно, объект Message еще не создан. Второй проблемой является несоответствие начального значения ползунка и начального размера шрифта.
Для решения поставленной задачи с помощью привязки данных, необходимо указать в качестве значения свойства FontSize текстового блока следующее выражение привязки:
{Binding ElementName=SliderFontSize, Path=Value}
Выражение привязки данных задается в виде расширения разметки XAML в фигурных скобках. Составляющие выражения привязки:
Binding – означает, что будет создан объект класса System.Windows.Data.Binding
ElementName – имя исходного объекта,
Path – имя свойства (или путь до свойства) исходного объекта. Пример пути до свойства: Background.Opacity
Пример№2
Slider Minimum="8" Maximum="30" ValueChanged="Slider_ValueChanged" /
Stackflanel Margin="0,30,0,0"
TextBlock x:
ame="Message" FontSize="{Binding Element ame=SliderFontSize, flath=Value}" Пример WflF-приложения для демонстрации привязки данных
TextBlock
Stackflanel
В данном примере отсутствуют проблемы, обнаруженные в предыдущем примере. Начальные значения связанных свойств будут согласованы даже в том случае, если элемент TextBlock будет предшествовать элементу Slider.
Задание 1
Проверьте реакцию среды разработки на неверные значения параметров ElementName и Path. Проанализируйте сообщения, которые выводятся в окне вывода (Вид → Вывод) при построении и при запуске приложения.
Режимы привязки
В выражении привязки с помощью параметра Mode можно задать одно из следующих пяти значений режима привязки:
OneWay – целевое свойство обновляется при изменении исходного свойства.
OneTime – первоначально значение исходного свойства копируется в целевое свойство, но дальнейшие изменения исходного свойства не учитываются.
TwoWay - целевое свойство обновляется при изменении исходного свойства, исходное свойство обновляется при изменении целевое свойства.
OneWayToSource – исходное свойство обновляется при изменении целевое свойства.
Default – значение по умолчанию. Если целевое свойство устанавливается пользователем (например, TextBox.Text, Slider.Value, CheckBox.IsChecked, …), то это TwoWay, в остальных случаях – это OneWay.
Пример выражения привязки с параметром Mode:
{Binding ElementName=slider1, Path=Value, Mode=OneTime}
Задание 2
Запустите приложение со следующим XAML-кодом:
TextBox x:Name="t1" /
TextBox x:Name="t2" Text="{Binding ElementName=t1, Path=Text}" /
Slider x:Name="slider1" /
Slider x:Name="slider2" Value="{Binding ElementName=slider1, Path=Value}" /
Определите различие в поведении полей t1 и t2 и модифицируйте код, чтобы устранить это различие.
Задание 3
Дополните пример №2 текстовым полем ввода TextBox, в котором пользователь может ввести размер шрифта, и задайте выражения привязки таким образом, чтобы значение ползунка, текст текстового поля и размер шрифта текстового блока соответствовали друг другу.
Стили в WPF
Стили позволяют определить набор некоторых свойств и их значений, которые потом могут применяться к элементам в xaml. Стили хранятся в ресурсах и отделяют значения свойств
элементов от пользовательского интерфейса. Также стили могут задавать некоторые аспекты поведения элементов с помощью триггеров. Аналогом стилей могут служить каскадные
таблицы стилей (CSS), которые применяются в коде html на веб-страницах.
Зачем нужны стили? Стили помогают создать стилевое единообразие для определенных элементов. Допустим, у нас есть следующий код xaml:
Пример 1 Код XAML |
StackPanel Orientation="Horizontal" VerticalAlignment="Top" Button Background="DarkBlue" Foreground="White" FontFamily="Verdana" Padding="5" Margin="5"ОткрытьButton Button Background="DarkBlue" Foreground="White" FontFamily="Verdana" Padding="5" Margin="5"ОбработатьButton Button Background="DarkBlue" Foreground="White" FontFamily="Verdana" Padding="5" Margin="5"СохранитьButton Button Padding="5" Margin="5"ЗакрытьButton StackPanel |
Результат |
|
Для трех элементов управления повторяются одни и те же атрибуты с одинаковыми значениями. Как при программировании наличие повторяющегося кода является плохим стилем, так и при разработке WPF- интерфейса повторение участков XAML-кода не приветствуется. В данном случае правильным решением является определение внешнего вида кнопки с помощью стиля и задание этого стиля для трех кнопок. По функциональности стили похожи на каскадные таблицы стилей CSS для HTML-файлов.
Обычно стили, как и другие ресурсы приложения, определяются в ресурсах окна:
Window.Resources
...
Style
Style
...
Window.Resources
Свойство Resources объявлено для класса FrameworkElement, поэтому ресурсы можно объявить практически для любого элемента управления:
StackPanel.Resources
...
Style
Style
...
StackPanel.Resources
Button.Resources
...
Style
Style
...
Button.Resources
Область действия стиля, объявленного в ресурсах какого-либо элемента управления, распространяется только на этот элемент управления и его дочерние элементы управления. Следует учесть, что статический ресурс должен быть определен в коде разметки перед ссылкой на него.
Стиль определяется с помощью элемента Style и может использоваться для определения:
значений атрибутов;
обработчиков событий;
триггеров, меняющих атрибуты элемента управления при возникновении каких- либо событий илипри изменении каких-либо свойств;
шаблонов, переопределяющих внешний вид элементов управления.
Ключевые свойства, определенные в классе Style:
TargetType – тип элемента, для которого определяется данный стиль;
BasedOn – родительский стиль (позволяет задавать иерархические стили);
Setters – коллекция объектов Setter или EventSetter, которые предназначены для установлениязначений свойств и обработчиков событий;
Triggers – коллекция триггеров;
Resourses – коллекция ресурсов, которые необходимо использовать с ресурсами.
Объект Setter определяет значение одного свойства элемента управления:
Setter Property="НАЗВАНИЕ_СВОЙСТВА" Value="ЗНАЧЕНИЕ" /
Рассмотрим модифицированную версию примера 1 с использованием стилей:
Пример 2 Код XAML |
Window.Resources Style TargetType="Button" Style.Setters Setter Property="Background" Value="DarkBlue" / Setter Property="Foreground" Value="White" / Setter Property="FontFamily" Value="Verdana" / Setter Property="Padding" Value="5" / Setter Property="Margin" Value="5" / Style.Setters Style Window.Resources StackPanel Orientation="Horizontal" VerticalAlignment="Top" ButtonОткрытьButton ButtonОбработатьButton ButtonСохранитьButton Button Padding="5" Margin="5"ЗакрытьButton StackPanel |
Результат |
|
Допускается не указывать элемент Style.Setters:
Style TargetType="Button"
Setter Property="Background" Value="DarkBlue" /
Setter Property="Foreground" Value="White" /
Setter Property="FontFamily" Value="Verdana" /
Setter Property="Padding" Value="5" /
Setter Property="Margin" Value="5" /
Style
В данном примере стиль был применен ко всем кнопкам окна (тип элементов управления определен ватрибуте TargetType). Если для элемента Style определить атрибут x:Key с именем стиля, то данный стиль будет определен только к тем кнопкам, для которых указано имя стиля в атрибуте Style с помощьюрасширения разметки StaticResource:
Style="{StaticResource ResourceKey=DocButton}"
Пример стиля, который применяется к трем кнопкам и не применяется к кнопке «Закрыть».
Пример 3 Код XAML |
Window.Resources Style TargetType="Button" x:Key="DocButton" Setter Property="Background" Value="DarkBlue" / Setter Property="Foreground" Value="White" / Setter Property="FontFamily" Value="Verdana" / Setter Property="Padding" Value="5" / Setter Property="Margin" Value="5" / Style Window.Resources StackPanel Orientation="Horizontal" VerticalAlignment="Top" Button Style="{StaticResource ResourceKey=DocButton}"ОткрытьButton Button Style="{StaticResource ResourceKey=DocButton}"ОбработатьButton Button Style="{StaticResource ResourceKey=DocButton}"СохранитьButton Button Padding="5" Margin="5"ЗакрытьButton StackPanel |
Результат |
|
Допускается вместо {StaticResource ResourceKey=DocButton} указывать {StaticResource DocButton}, т.к.
ResourceKey является единственным параметром для расширения разметки StaticResource.
Задание 1
Проверьте, какое значение имеет больший приоритет: значение свойства, указанное в стиле, илизначение атрибута элемента.
Свойства BasedOn класса Style позволяет определять иерархические стили. В этом свойстве с помощью расширения разметки StaticResource указывается родительский стиль. Дочерний стиль наследует все свойства родительского стиля, которые он может дополнить или переопределить. Пример определения дочернего стиля ActiveDocButton на основе родительского стиля DocButton.
Пример 4 Код XAML |
Window.Resources Style TargetType="Button" x:Key="DocButton" Setter Property="Background" Value="DarkBlue" / Setter Property="Foreground" Value="White" / Setter Property="FontFamily" Value="Verdana" / Setter Property="Padding" Value="5" / Setter Property="Margin" Value="5" / Style Style BasedOn="{StaticResource DocButton}" TargetType="Button" x:Key="ActiveDocButton" Setter Property="Background" Value="DarkRed" / |
Style Window.Resources StackPanel Orientation="Horizontal" VerticalAlignment="Top" Button Style="{StaticResource ActiveDocButton}"ОткрытьButton Button Style="{StaticResource DocButton}"ОбработатьButton Button Style="{StaticResource DocButton}"СохранитьButton Button Padding="5" Margin="5"ЗакрытьButton StackPanel |
Результат |
|
Объект EventSetter определяет имя функции-обработчика для события:
EventSetter Event="НАЗВАНИЕ_СОБЫТИЯ" Handler="ИМЯ_ФУНКЦИИ" /
Пример задания одного обработчика для всех кнопок окна:
Пример 5 Код XAML |
Window.Resources Style TargetType="Button" Style.Setters Setter Property="Margin" Value="5" / EventSetter Event="Click" Handler="Button_Click" / Style.Setters Style Window.Resources StackPanel Orientation="Horizontal" VerticalAlignment="Top" ButtonОткрытьButton ButtonОбработатьButton ButtonСохранитьButton StackPanel |
Код C# |
private void Button_Click(object sender, RoutedEventArgs e) { MessageBox.Show("Button is clicked"); } |
Результат |
|
Задание 2
Модифицируйте WPF-приложение, разработанное в 3-ей лабораторной работе: используйте стили дляоднотипных элементов управления.
Задание 3
Разработайте приложение MultiEdit для одновременной работы с несколькими текстами. Окно должно быть разделено на две части с одинаковыми градиентами. В каждой части окна должно быть несколько многострочных текстовых полей: одно из них большого размера с крупным шрифтом, а остальные маленького размера с мелким шрифтом. То текстовое окно, в котором пользователь набирает текст, должно быть большим, остальные текстовые поля должны быть маленькими. Внешний вид однотипных элементов управления должен определяться с помощью стилей.
Изменить стиль элемента управления в коде можно следующим образом:
(sender as FrameworkElement).Style = (Style)Resources["ИМЯ_СТИЛЯ"];