Всем привет, сегодня поговорим о шаблонах элементов управления wpf. Я думаю вы встречали приложения в которых при выборе из меню можно менять внешний вид элементов, они могли стать другим цветом, поменять формы. При чем при изменении, однотипные элементы приобретали одни черты визуального оформления. Так давайте разберем каким образом это делается в wpf и вы сможете, создав один раз шаблон, адаптировать его в других своих приложениях, может да же захотите воспользоваться чужим шаблоном и подключить его в своем проекте.
Логическое дерево
Логическое дерево — набор элементов управления, которые были размещены на окне. Все функциональные возможности, вроде наследования свойств, маршрутизации событий и наследование стилей работает через логическое дерево.
Визуальное дерево
Визуальное дерево – расширенная версия логического дерева. В нем элементы разбиваются на более мелкие фрагменты. Например, кнопка разбивается на три составные – ButtonChrome, ontentPresenter,
TextBlock.
Визуальное дерево:
- Позволяет заменить один из элементов с помощью стилей
- Разрабатывать шаблоны элементов управления.
- Каждый элемент управления имеет встроенное средство, определяющее способ его визуализации (как группу более фундаментальных элементов). Это средство называется шаблоном элемента управления (control template) и определяется с помощью блока XAML-разметки.
- Каждый элемент управления WPF спроектирован как не имеющий внешнего вида, в том смысле, что его внешний вид может быть полностью переопределен. Неизменным остается лишь поведение элемента управления, которое жестко привязано к классу элемента (хотя часто оно может быть тонко настроено с помощью свойств).
Создание шаблона (в ресурсах):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<Application x:Class="WPF_Дизайн_Стили.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPF_Дизайн_Стили" StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <ControlTemplate TargetType="Button" x:Key="MyButton"> <Border CornerRadius="25" BorderBrush="Green" BorderThickness="2" Background="LightBlue" Height="40" Width="100" > <ContentControl Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Кнопка" /> </Border> </ControlTemplate> </ResourceDictionary> </Application.Resources> </Application> |
Пример того как использовать в разметке:
1 |
<Button Template="{StaticResource MyButton}" Width="111" Height="66"/> |
Так же мы можем добавить триггеры, и обработать их, вот пример того как это можно сделать:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
<Application.Resources> <ResourceDictionary> <ControlTemplate TargetType="Button" x:Key="MyButton"> <Border Name="Border" CornerRadius="25" BorderBrush="Green" BorderThickness="2" Background="LightBlue" Height="40" Width="100" > <ContentControl Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Кнопка" /> </Border> <ControlTemplate.Triggers> <!--При наведении мыши--> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="Border" Property="Background" Value="DarkRed"></Setter> </Trigger> <!--При нажатии--> <Trigger Property="IsPressed" Value="True"> <Setter TargetName="Border" Property="Background" Value="DarkRed"></Setter> </Trigger> <!--Если кнопка отключена--> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="Border" Property="TextBlock.Foreground" Value="Gray" /> <Setter TargetName="Border" Property="Background" Value="MistyRose" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </ResourceDictionary> </Application.Resources> |
В начале статьи я описал случай когда можно, с помощью выбора в меню изменить стиль окна. Сильно пример я не буду усложнять, оставим одну кнопку на форму, при нажатии на которую она будет изменять свой стиль через шаблон.
И так добавим на пустую форму кнопку.
Xaml у меня вышел вот такой:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<Window x:Class="WPF_Дизайн_Стили.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:WPF_Дизайн_Стили" mc:Ignorable="d" Title="MainWindow" Height="382" Width="621.333" > <Window.Resources> </Window.Resources> <Button Content="Button" HorizontalAlignment="Center" VerticalAlignment="Center" Height="50" Width="110" Click="Button_Click" Grid.Column="1" /> </Window> |
Создадим в проекте 2 элемента, Словарь ресурсов WPF, я им дал имена DictionaryTemplate.xaml и DictionaryTemplate2.xaml, располагаются они у меня в папке Resources
Внутри файлов в xaml разметки я добавил шаблон меняющий стиль кнопок.
DictionaryTemplate.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPF_Дизайн_Стили"> <ControlTemplate TargetType="Button" x:Key="GradientButtonTemplate"> <Border Name="Border" CornerRadius="25" BorderBrush="Yellow" BorderThickness="2" Background="LightBlue" Height="30" Width="90" > <ContentControl Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Кнопка" /> </Border> <ControlTemplate.Triggers> <!--При наведении мыши--> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="Border" Property="Background" Value="Blue"></Setter> </Trigger> <!--При нажатии--> <Trigger Property="IsPressed" Value="True"> <Setter TargetName="Border" Property="Background" Value="DarkBlue"></Setter> </Trigger> <!--Если кнопка отключена--> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="Border" Property="TextBlock.Foreground" Value="Black" /> <Setter TargetName="Border" Property="Background" Value="MistyRose" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> <Style TargetType="{x:Type Button}"> <Setter Property="Control.Template" Value="{StaticResource GradientButtonTemplate}"></Setter> </Style> </ResourceDictionary> |
DictionaryTemplate2.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPF_Дизайн_Стили"> <ControlTemplate TargetType="Button" x:Key="GradientButtonTemplate"> <Border Name="Border" CornerRadius="25" BorderBrush="LightGreen" BorderThickness="2" Background="LightBlue" Height="30" Width="90" > <ContentControl Margin="5" HorizontalAlignment="Center" VerticalAlignment="Center" Content="Кнопка" /> </Border> <ControlTemplate.Triggers> <!--При наведении мыши--> <Trigger Property="IsMouseOver" Value="True"> <Setter TargetName="Border" Property="Background" Value="Green"></Setter> </Trigger> <!--При нажатии--> <Trigger Property="IsPressed" Value="True"> <Setter TargetName="Border" Property="Background" Value="DarkGreen"></Setter> </Trigger> <!--Если кнопка отключена--> <Trigger Property="IsEnabled" Value="False"> <Setter TargetName="Border" Property="TextBlock.Foreground" Value="Black" /> <Setter TargetName="Border" Property="Background" Value="MistyRose" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> <Style TargetType="{x:Type Button}"> <Setter Property="Control.Template" Value="{StaticResource GradientButtonTemplate}"></Setter> </Style> </ResourceDictionary> |
Добавим в разметку в файле App.xaml, где нам надо указать какой шаблон будем подключать.
App.xaml
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<Application x:Class="WPF_Дизайн_Стили.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:WPF_Дизайн_Стили" StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Resources/DictionaryTemplate.xaml"></ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Application.Resources> </Application> |
Наш шаблон начал работать. Однако при нажатии кнопки ничего не происходит, так добавим немного сишарпного кода, перейдем в фаил MainWindow
MainWindow.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
using System; using System.Windows; namespace WPF_Дизайн_Стили { /// <summary> /// Логика взаимодействия для MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } bool flag = false; private void Button_Click(object sender, RoutedEventArgs e) { string path = string.Empty; if (flag) { path = "Resources/DictionaryTemplate.xaml"; } else { path = "Resources/DictionaryTemplate2.xaml"; } ResourceDictionary resourceDictionary = new ResourceDictionary(); resourceDictionary.Source = new Uri(path, UriKind.Relative); Application.Current.Resources.MergedDictionaries[0] = resourceDictionary; flag = !flag; } } } |
Если бы мы использовали не App.xaml для вызова нашего шаблона, а MainWindows.xaml то нужно было бы подкорректировать путь следующим образом.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
using System; using System.Windows; namespace WPF_Дизайн_Стили { /// <summary> /// Логика взаимодействия для MainWindow.xaml /// </summary> public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); } bool flag = false; private void Button_Click(object sender, RoutedEventArgs e) { string path = string.Empty; if (flag) { path = "Resources/DictionaryTemplate.xaml"; } else { path = "Resources/DictionaryTemplate2.xaml"; } ResourceDictionary resourceDictionary = new ResourceDictionary(); resourceDictionary.Source = new Uri(path, UriKind.Relative); this.Resources.MergedDictionaries[0] = resourceDictionary; flag = !flag; } } } |
Вот такой результат должен получится, при запуске проекта: