Одним из больших преимуществ, которые WPF дает разработчикам, является возможность настроить внешний вид элемента управления в WPF без потери встроенной в него функциональности.
Рассмотрим два ползунка на рис. 16.16: верхний имеет стиль по умолчанию, а нижний является стилизованным ползунком. Функциональность их одинакова. Мы просто изменили внешний вид элемента управления.
Style — это реальный класс (в пространстве имен System. Windows), который используется в сочетании с элементом управления; он группирует значения свойств для того, чтобы вы могли один раз настроить их, а затем применять к элементам управления в массовом порядке (вместо того, чтобы настраивать их отдельно для каждого экземпляра элемента управления). Предположим, что ваше приложение использует красивый плавно меняющийся серый фон для кнопок. Кроме того, каждая кнопка имеет белую границу и отображает текст шрифтом Segoe. Мы можем манипулировать каждым из этих аспектов отображения при помощи свойств кнопки; но делать это для каждой кнопки вам быстро надоест, style позволяет нам один раз настроить все эти свойства, а затем дать ссылку в каждой кнопке на эти свойства (присвоив кнопке данный стиль).
Вот определение style в XAML:
<Style x:Key="GradientButton" TargetType="Button">
<Setter Property="Margin" Value="2"/>
<Setter Property="BorderBrush" Value="White" />
<Setter Property="FontFamily" Value="Segoe UI"/>
<Setter Property="FontSize" Value="12px"/>
<Setter Property="FontWeight" Value="Bold"/>
<Setter Property="Foreground" Value="White" />
<Setter Property="Background" >
<Setter.Value> r
<LinearGradientBrush StartPoint="0,0" EndPoint="0,1" >
<GradientStop Color="Gray" Offset="0.2"/>
<GradientStop Color="DarkGray" Offset="0.85"/>
<GradientStop Color="Gray" Offset="l"/>
</LinearGradientBrush>
</Setter.Value>
</Setter>
</Style>
Присвоить кнопке этот стиль просто:
<Button Style="{StaticResource GradientButton}" Height="38"
Name="buttonl"
Width="100">0K</Button>
Все это очень хорошо работает для наборов свойств. Но что происходит в том случае, когда мы хотим подстроить атрибут, который не представлен как свойство? Что, если бы мы захотели придать нашей кнопке овальную форму вместо стандартной прямоугольной? Поскольку класс Button не предоставляет такого свойства, которое мы могли бы использовать для изменения формы фона, то у нас вроде бы нет шансов.
Здесь на сцену выходит концепция шаблонов. Шаблоны позволяют полностью заменить визуальное дерево любого элемента управления, предоставляя вам полный контроль над
всеми аспектами пользовательского интерфейса элемента управления. Визуальное дерево в WPF— это иерархия элементов управления (наследующих от класса visual), которые обеспечивают окончательную визуализацию элемента управления. Хороший обзор визуальных и логических деревьев WPF можно найти по адресу:
Примечание
Ранее мы упоминали, что элементы управления в WPF не имеют "наружности”; свидетельством этого являются шаблоны. Функциональность элемента управления существует отдельно от его визуального дерева. Вид по умолчанию для всех элементов управления обеспечивается целым набором шаблонов, по одному на каждую тему Windows. Это означает, что элементы управления WPF могут автоматически участвовать в любой теме операционной системы, которую вы используете.
Шаблоны создаются при помощи класса ControlTemplate. Внутри этого класса (или элемента, если мы реализуем шаблон на XAML) нам нужно нарисовать визуализации, которые будут представлять кнопку. Класс Rectangle в WPF может быть использован для рисования нашей основной формы фона. При помощи настройки свойств Radiusx и RadiusY мы можем смягчить обычные 90-градусные углы до желаемой эллиптической формы:
<Rectangle RadiusX="25" RadiusY="25" Width="100" Height="50"
Stroke="Black" StrokeThickness="l">
Мы можем также добавить кнопке еще несколько визуальных аспектов (таких как градиентная заливка):
<Rectangle.Fill>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop 0ffset="0" Color="Gray" />
<GradientStop Offset="l" Color="LightGray" />
</LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Fill>
Совет
Для проверки внешнего вида наберите XAML-код формы кнопки в редакторе XAML и подстройте его как хотите. После того как вас все устроит, вы сможете скопировать XAML-код в шаблон. Хорошим инструментом для конструирования пользовательских интерфейсов является Microsoft Expression Blend, но в простых случаях написания XAML-кода вручную или использования визуального конструктора Visual Studio должно быть вполне достаточно.
Текст внутри кнопки легко визуализируется при помощи объекта TextBlock:
<TextBlock Canvas.Тор="5" Height="40" Width="100" FontSize="20"
TextAlignment="Center">OK</TextBlock>
После того как мы удовлетворимся внешним видом, можем "шаблонизировать" его при помощи вкладывания всего этого внутрь элемента ControlElement.
Поскольку нам нужно будет в дальнейшем ссылаться на этот шаблон, то мы дадим ему ключ/имя:
<ControlTemplate х:Key="OvalButtonTemplate">
И наконец, мы встраиваем все это в виде ресурса. Ресурс — это просто объект .NET (написанный на XAML или на коде), который предназначен для совместного использования другими объектами. В данном конкретном случае мы хотим иметь возможность использовать этот шаблон с любой кнопкой. Ресурсы в проекте WPF можно объявлять на любом уровне: мы можем объявлять ресурсы, которые принадлежат общему окну или любому элементу этого окна (такому как панель Grid), либо мы можем хранить все наши ресурсы в ResourceDictionary, чтобы иметь возможность ссылаться на них из любого класса нашего проекта. Для нашего примера мы остановимся на простом ресурсе, определенном в нашем родительском окне (в нижеследующем коде это будет элемент Window.Resources).
Вс.е это сведено воедино в листинге 16.2, а на рис. 16.17 показана полученная в итоге кнопка.
xmlns="
Background="#F8F8F8">
<Window.Resources>
<ControlTemplate x:Key="OvalButtonTemplate">
<Canvas Width="100" Height="25" Margin="2">
<Rectangle x:Name="BaseRectangle" Canvas.Top="0" RadiusX="25" RadiusY="25" Width="100" Height="40" Stroke="DarkGray" StrokeThickness="l">
<Rectangle.Fill>
<LinearGradientBrush>
<LinearGradientBrush.GradientStops>
<GradientStop 0ffset="0" Color="Gray" />
<GradientStop Offset="l" Color="LightGray" /> </LinearGradientBrush.GradientStops>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<TextBlock Canvas.Тор="5м Height="40M Width="100" FontSize="20"
TextAlignment="Center,,>OK</TextBlock>
</Canvas>
</ControlTemplate>
</Window.Resources>
<Canvas>
<Button Canvas.Left="49" Canvas.Top="44м Height="38" Name="buttonl" Width="93" Template="{StaticResource OvalButtonTemplate)" />
</Canvas>
</Window>