Автор работы: Пользователь скрыл имя, 19 Октября 2012 в 19:18, реферат
Что собой представляют пользовательские элементы в WPF
Хотя пользовательский элемент можно построить в любом проекте WPF, обычно такие элементы размещаются в специально выделенной сборке — библиотеке классов (DLL). Это позволяет разделять работу с множеством приложений WPF.
<ResourceDictionary xmlns="http://schemas.
xmlns:x="http://schemas.
xmlns:local="clr-namespace:
<Style TargetType="{x:Type local:ColorPicker}">
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type local:ColorPicker}">
<Border Background="{TemplateBinding Background}"
BorderBrush="{TemplateBinding BorderBrush}"
BorderThickness="{
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
<RowDefinition Height="Auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition></
<ColumnDefinition Width="Auto"></
</Grid.ColumnDefinitions>
<Slider Name="PART_RedSlider" Minimum="0" Maximum="255"
Margin="{TemplateBinding Padding}"></Slider>
<Slider Grid.Row="1" Name="PART_GreenSlider" Minimum="0" Maximum="255"
Margin="{TemplateBinding Padding}"></Slider>
<Slider Grid.Row="2" Name="PART_BlueSlider" Minimum="0" Maximum="255"
Margin="{TemplateBinding Padding}"></Slider>
<Rectangle Grid.Column="1" Grid.RowSpan="3"
Margin="{TemplateBinding Padding}"
Width="50" Stroke="Black" StrokeThickness="1">
<Rectangle.Fill>
<SolidColorBrush Color="{Binding Path=Color,
RelativeSource={RelativeSource TemplatedParent}}"></
</Rectangle.Fill>
</Rectangle>
</Grid>
</Border>
</ControlTemplate>
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
Как видите, некоторые выражения привязки заменены расширением TemplateBinding. Другие по-прежнему используют расширение Binding, но имеют свойство RelativeSource, указывающее на родителя шаблона (пользовательский элемент управления). Хотя и TemplateBinding, и Binding с RelativeSource из TemplatedParent служат одной и той же цели — извлечению данных из свойств пользовательского элемента управления, все же облегченный TemplateBinding более предпочтителен. Однако он не будет работать, если нужна двунаправленная привязка (как в случае ползунков) или когда осуществляется привязка к свойству класса, унаследованного от Freezable (вроде SolidColorBrush).
Поддержка визуальных состояний
Элемент управления ColorPicker демонстрирует
хороший пример проектирования элемента
управления. Поскольку его поведение
и внешний вид тщательно
Одной из причин простоты ColorPicker
является отсутствие в нем концепции
состояний. Другими словами, он не меняет
своего внешнего вида в зависимости
от наличия фокуса, наведенного курсора
мыши, доступности и т.п. Элемент
FlipPanel из следующего примера в этом
отношении несколько
Основная идея, положенная в основу FlipPanel, состоит в том, что он предоставляет две поверхности для размещения содержимого, из которых в каждый конкретный момент времени видимой является только одна. Чтобы увидеть другую, вам нужно "перевернуть" панель. Эффект "переворачивания" может быть настроен с помощью шаблона элемента управления, но эффект по умолчанию использует простое затухание, которое обеспечивает переход между передней и задней поверхностями:
В зависимости от приложения, элемент FlipPanel можно применять для комбинации формы ввода данных с некоторой полезной документацией, чтобы предоставить простой или более сложный вид одних и тех же данных либо соединить вместе вопрос и ответ в какой-то простой игре.
Переворачивание можно осуществлять программно (устанавливая свойство по имени IsFlipped) или же пользователь мог бы переворачивать панель, щелкая на подходящей кнопке (если только потребитель элемента управления не удалит ее из шаблона).
Ясно, что шаблон элемента управления должен указывать два отдельных раздела: области содержимого передней и задней стороны FlipPanel. Однако есть одна дополнительная деталь, а именно — элементу FlipPanel нужен какой-нибудь способ переключения между двумя состояниями: перевернутым и не перевернутым. Это можно было бы сделать, добавив триггеры к шаблону. Один триггер скрывал бы переднюю панель и показывал вторую по щелчку на кнопке, в то время как другой возвращал бы ее обратно. При этом можно использовать любую приемлемую анимацию.
Но за счет применения визуальных состояний потребителю элемента управления ясно указывается, что эти два состояния являются обязательной частью шаблона. Вместо написания триггеров для нужного свойства или события, потребитель элемента управления просто должен был бы оформить соответствующие анимации состояния — задача, которая еще более упрощается при использовании Expression Blend.
Начало проектирования класса FlipPanel
Если выделить суть, то FlipPanel окажется неожиданной простым элементом. Он состоит из двух областей содержимого, которые пользователь может наполнить единственным элементом (скорее всего, контейнером компоновки, содержащим набор элементов). Формально это значит, что элемент FlipPanel — не настоящая панель, потому что она не использует логики компоновки для организации группы дочерних элементов.
Однако это вряд ли вызовет
проблему, потому что структура FlipPanel
ясна и интуитивно понятна. FlipPanel также
включает в себя кнопку, позволяющую
переключаться между двумя
Хотя можно создать
специальный элемент
public class FlipPanel : Control
{...}
Первое, что понадобится сделать — это создать свойства для FlipPanel. Как почти все свойства в элементе WPF, это должны быть свойства зависимости. Ниже показано, как FlipPanel определяет свойство FrontContent, которое содержит элемент, отображаемый на передней поверхности:
public static readonly DependencyProperty FrontContentProperty =
DependencyProperty.Register("
public object FrontContent
{
get
{
return GetValue(FrontContentProperty)
}
set
{
SetValue(FrontContentProperty, value);
}
}
Свойство BackContent почти идентично:
public static readonly DependencyProperty BackContentProperty =
DependencyProperty.Register("
public object BackContent
{
get
{
return GetValue(BackContentProperty);
}
set
{
SetValue(BackContentProperty, value);
}
}
Остается добавить только одно существенное свойство: IsFlipped. Это свойство типа bool отслеживает текущее состояние FlipPanel (повернута панель или нет) и позволяет потребителю элемента управления переворачивать его программно:
public static readonly DependencyProperty IsFlippedProperty =
DependencyProperty.Register("
public bool IsFlipped
{
get
{
return (bool)GetValue(
}
set
{
SetValue(IsFlippedProperty, value);
ChangeVisualState(true);
}
}
Средство установки свойства IsFlipped вызывает специальный метод по имени ChangeVisualState(). Этот метод обеспечивает обновление изображения для соответствия текущему состоянию (повернута панель лицом или тылом). Код, который решает эту задачу, рассматривается чуть позже.
FlipPanel наследует почти
все необходимое от класса Control.
Исключением является лишь
public static readonly DependencyProperty CornerRadiusProperty =
DependencyProperty.Register("
public CornerRadius CornerRadius
{
get
{
return (CornerRadius)GetValue(
}
set
{
SetValue(CornerRadiusProperty, value);
}
}
Также нужно добавить стиль,
применяющий шаблон по умолчанию к FlipPanel.
Этот стиль помещается в словарь ресурсов
generic.xaml, как и в ColorPicker. Остается одна последняя
деталь. Чтобы заставить элемент управления
выбрать стиль по умолчанию из файла generic.xaml,
нужно вызвать метод DefaultStyleKeyProperty.
static FlipPanel()
{
DefaultStyleKeyProperty.
new FrameworkPropertyMetadata(
}
Выбор частей и состояний
Имея базовую структуру, можно идентифицировать части и состояния, которые будут использованы в шаблоне элемента управления. Ясно, что для FlipPanel требуются два состояния:
Вдобавок понадобятся две части:
FlipButton
Это кнопка, при щелчке на
которой видимость
FlipButtonAlternate
Это необязательный элемент,
работающий таким же образом, как FlipButton.
Его включение позволит потребителю
элемента управления использовать два
разных подхода в шаблоне
Можно было бы также добавить содержимое для фронтальной и обратной области. Однако элементу FlipPanel не нужно манипулировать этими областями непосредственно — до тех пор, пока шаблон включает анимацию, скрывающую или отображающую их в надлежащее время. (Другой вариант — определить эти части так, чтобы явно изменять их видимость в коде. В таком случае панель сможет переключаться между передней и задней областями содержимого, даже если никакая анимация не определена, скрывая одну часть и показывая другую. Для простоты в FlipPanel такой подход использоваться не будет.)
Чтобы анонсировать факт, что FlipPanel использует эти части и состояния, необходимо применить атрибут TemplatePart к классу элемента управления, как показано ниже:
[TemplatePart(Name = "FlipButton", Type = typeof(ToggleButton)),
TemplatePart(Name = "FlipButtonAlternate", Type = typeof(ToggleButton)),
TemplateVisualState(Name = "Normal", GroupName = "ViewStates"),
TemplateVisualState(Name = "Flipped", GroupName = "ViewStates")]
Части FlipButton и FlipButtonAlternate ограничены — каждая из них может быть только экземпляром ToggleButton либо экземпляром класса, унаследованного от ToggleButton.
Для обеспечения более гибкой поддержки шаблона применяйте наименее специализированный тип элемента. Например, лучше использовать FrameworkElement, чем ContentControl, если только не требуется какое-нибудь свойство или поведение, предоставляемое ContentControl.
Шаблон элемента управления по умолчанию
Теперь можно поместить
все части примера в шаблон
элемента управления по умолчанию. Корневой
элемент — это Grid с двумя строками,
удерживающий область содержимого
(в верхней строке) и кнопку для
переворачивания (в нижней строке).
Область содержимого