Несмотря на то, что стили существенно облегчают манипулирование внешним видом элементов управления, гораздо более сильным средством в плане визуализации являются шаблоны. В отличие от стилей шаблоны помогают полностью менять модель визуализации элемента. Чтобы понять шаблоны, необходимо осознать общую концепцию визуализации в WPF.
Визуализация в WPF тесно связана с такими понятиями как логическое и визуальное дерево. Эти деревья являются своего рода каркасом приложения. Так мы можем представить приложение как некий набор вложенных элементов. Возьмем, к примеру, следующую простейшую разметку xaml:
<Window x:Class="ControlsTemplateApp.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:ControlsTemplateApp" mc:Ignorable="d" Title="MainWindow" Height="250" Width="300"> <Grid> <Button x:Name="myButton">Hello</Button> </Grid> </Window>
Структуру элементов здесь можно представить следующей схемой:
То есть в Window есть Grid, в Gride - элемент Button, в кнопке в качестве содержимого установлен некоторый текст в виде объекта String. В итоге получается некое дерево элементов, которое называется логическим. В WPF оно представлено классом System.Windows.LogicalTreeHelper. Логическое дерево имеет дело с визуализацией как таковой, оно образует модель доступа к дочерним элементам.
От него отличается визуальное дерево, представленное классом System.Windows.Media.VisualTreeHelper. Так, визуальное дерево для вышеприведенной разметки xaml будет выглядеть следующим образом:
Визуальное дерево получается гораздо сложнее, оно показывает, как с визуальной точки зрения устроен элемент, из каких частей он состоит.
Visual Studio имеет встроенные средства для просмотра визуального дерева элементов. Для этого нам надо запустить проект в режиме отладки и в меню выбрать пункт Debug -> Windows -> Live Visual Tree. После нажатия на этот пункт Visual Studio откроет окно с визуальным деревом, в котором мы можем посмотреть, как устроен элемент:
Если мы нажмем правой кнопкой мыши на элемент в этом дереве и в контекстном меню выберем пункт Show Properties, справа в Visual Studio откроется окно свойств для этого элемента.
Визуальное дерево элемента управления опредлеляет, как будет выглядеть этот элемент или иными словами его шаблон. Шаблон элемента - это своего рода визуальный скелет элемента управления. Например, для элемента Button упрощенно шаблон выглядит следующим образом:
<Style TargetType="Button"> <Setter Property="SnapsToDevicePixels" Value="true"/> <Setter Property="OverridesDefaultStyle" Value="true"/> <Setter Property="FocusVisualStyle" Value="{StaticResource ButtonFocusVisual}"/> <Setter Property="MinHeight" Value="23"/> <Setter Property="MinWidth" Value="75"/> <Setter Property="Template"> <Setter.Value> <ControlTemplate TargetType="Button"> <Border x:Name="Border" CornerRadius="2" BorderThickness="1" Background="{StaticResource NormalBrush}" BorderBrush="{StaticResource NormalBorderBrush}"> <ContentPresenter Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" RecognizesAccessKey="True"/> </Border> <ControlTemplate.Triggers> <Trigger Property="IsKeyboardFocused" Value="true"> <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DefaultedBorderBrush}" /> </Trigger> <Trigger Property="IsDefaulted" Value="true"> <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DefaultedBorderBrush}" /> </Trigger> <Trigger Property="IsMouseOver" Value="true"> <Setter TargetName="Border" Property="Background" Value="{StaticResource DarkBrush}" /> </Trigger> <Trigger Property="IsPressed" Value="true"> <Setter TargetName="Border" Property="Background" Value="{StaticResource PressedBrush}" /> <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource PressedBorderBrush}" /> </Trigger> <Trigger Property="IsEnabled" Value="false"> <Setter TargetName="Border" Property="Background" Value="{StaticResource DisabledBackgroundBrush}" /> <Setter TargetName="Border" Property="BorderBrush" Value="{StaticResource DisabledBorderBrush}" /> <Setter Property="Foreground" Value="{StaticResource DisabledForegroundBrush}"/> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </Setter.Value> </Setter> </Style>
Полное описание шаблона кнопки можно найти по ссылке https://msdn.microsoft.com/en-us/library/ms753328%28v=vs.100%29.aspx.
Здесь мы можем увидеть, что сам шаблон задается с помощью стиля при определении сеттера для свойства Template. Чтобы определить сам, шаблон, применяется элемент ControlTemplate. И как раз в этом элементе мы можем увидеть и элемент Border, и элемент ContentPresenter, которые составляют визуальное дерево кнопки.