Главная » WPF » Шаблоны WPF

0

Изменение внешнего вида элемента управления может оказаться нетривиальной задачей. В Windows Forms или User32 для изменения вида кнопки нужно переопре# делить метод OnPaint или обработать сообщение WM_PAINT и самостоятельно на# писать код, который будет рисовать пиксели. В HTML требуется создать из отдель# ных изображений элемент  управления, который  будет выглядеть  как кнопка, или

2  Похоже, без исключений никак не обойтись.

написать специальную разметку. Проблема во всех этих случаях заключается в том, что модель программирования, применяемая при создании  и изменении внешнего вида элемента управления, существенно  отличается  от модели его использования.

Вместо того чтобы заново писать весь код отображения, часто применяют дру# гое решение: настройку  с помощью  свойств.  В HTML  для изменения внешнего вида элемента можно воспользоваться каскадными таблицами  стилей. Проблема однако в том, что от автора элемента требуется  представить  все возможные  наст# ройки  в виде свойств,  к которым  можно применить  CSS#стили. В современных реализациях CSS имеется обширная  библиотека  свойств для изменения границ, цветов и форматирования текста, но модифицировать отображение  путем встав# ки произвольного содержимого  нельзя.

WPF позволяет  изменять  способ визуализации элементов  управления в ши# роких пределах с помощью свойств, но, кроме того, есть возможность полностью модифицировать внешний  вид любого элемента.  Мы хотели, чтобы весь спектр средств настройки был декларативным, но при этом сохранялась последователь# ная модель программирования. В результате  остановились на системе шаблонов. В дереве отображения кнопки Button (рис. 3.6) присутствует элемент ButtonChrome, не являющийся ни кнопкой, ни объектом ContentPresenter, ни со# держимым. Он был создан шаблоном кнопки.

Выше при обсуждении  модели содержимого  мы ввели в рассмотрение  три ти# па элементов  управления: рисовальщики, менеджеры  размещения и составные элементы. Все составные элементы не только реализуют представление содержи# мого, но и поддерживают шаблоны. Шаблон  позволяет  создать конкретный на# бор элементов  (например, ButtonChrome) для реализации отображения. Состав# ные элементы  наследуют классу Control и получают от него свойство Template.

Рис. 3.6. Дерево отображения элемента Button (обратите внимание на

ButtonChrome)

Рис. 3.7. Кнопка с шаблоном)прямоугольником и ее дерево отображения

Для начала рассмотрим  код простой кнопки, с которой мы уже неоднократно встречались:

<Button>

My Button

</Button>

Чтобы изменить  ее внешний вид, не затрагивая содержимого, можно было оп# ределить  новый шаблон. Шаблон  представляет собой фабрику  для создания  но# вого  дерева  отображения. Есть  две разновидности шаблонов:  ControlTemplate (он создает дерево отображения для элемента  управления) и DataTemplate (соз# дает дерево отображения для данных, мы будем говорить о таких шаблонах в гла# ве 6). Чтобы определить  новый шаблон для элемента  управления, нужно задать целевой тип и дерево отображения. Целевой  тип сообщает шаблону о том, к объ# ектам какого типа он будет применяться:

<Button>

<Button.Template>

<ControlTemplate TargetType=’{x:Type Button}’>

<Rectangle Fill=’Red’ Width=’75’ Height=’23’ />

</ControlTemplate>

</Button.Template> My Button

</Button>

В этом примере дерево отображения представляет собой один красный прямо# угольник. На рис. 3.7 показано, как выглядят сгенерированный элемент и его де# рево отображения.

Стоит отметить отсутствие  объектов ButtonChrome, ContentPresenter и TextBlock,  которые мы видели раньше. И тем не менее кнопка вполне работоспо# собна. Чтобы  убедиться  в этом, напишем  код, который  будет изменять  шаблон при нажатии  кнопки.  Прежде  всего, определим  обработчик  события,  в котором новый шаблон создается и ассоциируется с кнопкой.

void ChangeIt(object sender, RoutedEventArgs e) {

ControlTemplate template = new ControlTemplate(typeof(Button));

// Это пустой шаблон, который ничего не делает

((Button)sender).Template = template;

}

Имея  шаблон,  нужно  заполнить дерево  отображения. Оно  задается  с по# мощью свойства VisualTree  объекта ControlTemplate, которое имеет тип Framework ElementFactory. Шаблон можно применять к нескольким элементам управления, но каждый  элемент  должен  встречаться в дереве не более одного раза. Чтобы решить эту проблему, объект FrameworkElementFactory конструи# рует новый экземпляр дерева отображения для каждого  элемента,  использую# щего данный шаблон. Ниже мы говорим, что кнопка должна отображаться в ви# де эллипса:

void ChangeIt(object sender, RoutedEventArgs e) {

ControlTemplate template = new ControlTemplate(typeof(Button));

// Шаблон «создать невидимый эллипс»

template.VisualTree = new FrameworkElementFactory(typeof(Ellipse));

((Button)sender).Template = template;

}

Далее мы хотим задать некоторые свойства эллипса. Для установки свойств вызывается метод SetValue,  так как мы по сути дела создаем список свойств, которым нужно будет присвоить  значения  при создании эллипса:

void ChangeIt(object sender, RoutedEventArgs e) {

ControlTemplate template = new ControlTemplate(typeof(Button));

// Шаблон «создать синий эллипс»

template.VisualTree = new FrameworkElementFactory(typeof(Ellipse)); template.VisualTree.SetValue(Ellipse.FillProperty, Brushes.Blue); template.VisualTree.SetValue(Ellipse.WidthProperty, 75); template.VisualTree.SetValue(Ellipse.HeightProperty, 23); ((Button)sender).Template = template;

}

Возможно, вы обратили внимание, что, не привязав разметку к объектной  мо# дели, мы нарушили один из принципов языка XAML. В данном случае код, экви# валентный определению  шаблона, очень сильно отличается  от разметки.  Поэто# му мы решили  отступить  от догмы, но сделать разметку  более удобной. Эта раз# метка заново интерпретируется при каждом использовании шаблона. Последний шаг – связать обработчик  события с кнопкой:

<Button … Click=’ChangeIt’>

</Button>

На рис. 3.8 показан результат  нажатия  кнопки.

Рис. 3.8. Кнопка и ее дерево отображения после применения нового шаблона

Добавив  немного  эстетических  деталей  и включив  объект  ContentPresenter, мы можем сделать так, чтобы кнопка была похожа на кнопку:

<ControlTemplate TargetType=’{x:Type Button}’>

<Border CornerRadius=’4’ BorderThickness=’3’>

<Border.BorderBrush>

<LinearGradientBrush EndPoint=’0,1’>

<LinearGradientBrush.GradientStops>

<GradientStop Offset=’0’ Color=’#FFFFFFFF’ />

<GradientStop Offset=’1’ Color=’#FF777777’ />

</LinearGradientBrush.GradientStops>

</LinearGradientBrush>

</Border.BorderBrush>

<Border.Background>

<LinearGradientBrush EndPoint=’0,1’>

<LinearGradientBrush.GradientStops>

<GradientStop Offset=’0’ Color=’#FF777777’ />

<GradientStop Offset=’1’ Color=’#FFFFFFFF’ />

</LinearGradientBrush.GradientStops>

</LinearGradientBrush>

</Border.Background>

<ContentPresenter HorizontalAlignment=’Center’ VerticalAlignment=’Center’ />

</Border>

</ControlTemplate>

Включенный в шаблон объект ContentPresenter по умолчанию ищет свойство Content у элемента,  к которому  применен  шаблон,  и отображает  его значение. Чтобы  визуализировать рамку,  мы заменили  стандартный для  кнопки  элемент ButtonChrome обобщенным  элементом  Border.  Результат работы этой програм# мы куда больше напоминает кнопку (рис. 3.9).

А что произойдет,  если в этом шаблоне изменить  цвет фона кнопки?

Рис. 3.9. Кнопка с более эстетичным шаблоном и ее дерево отображения

Источник: К. Андерсон  Основы  Windows Presentation Foundation. Пер. с англ. А. Слинкина — М.: ДМК Пресс, 2008 — 432 с.: ил.

По теме:

  • Комментарии