WPF: пользовательские элементы

Автор работы: Пользователь скрыл имя, 19 Октября 2012 в 19:18, реферат

Описание

Что собой представляют пользовательские элементы в WPF
Хотя пользовательский элемент можно построить в любом проекте WPF, обычно такие элементы размещаются в специально выделенной сборке — библиотеке классов (DLL). Это позволяет разделять работу с множеством приложений WPF.

Работа состоит из  1 файл

Реферат WPF.docx

— 194.52 Кб (Скачать документ)

Надежные команды

Описанный выше прием представляет собой совершенно законный способ подключения  команд к элементам управления, но элементы WPF и профессиональные элементы управления его не используют. Они  применяют более надежный подход, присоединяя статические обработчики команд с помощью метода CommandManager.RegisterClassCommandBinding().

Основная проблема, связанная  с реализацией, продемонстрированной в предыдущем разделе, заключается  в том, что она использует общедоступную  коллекцию CommandBindings. Это делает ее несколько  хрупкой, поскольку клиент может  легко модифицировать коллекцию CommandBindings.

Подобное невозможно в  случае применения метода RegisterClassCommandBinding(). И именно такой подход используют элементы управления WPF. Например, если посмотреть на коллекцию CommandBindings из TextBox, то в ней не будет никаких привязок для жестко закодированных команд Undo, Redo, Cut, Сору и Paste, поскольку они зарегистрированы как привязки классов. Техника довольно проста. Вместо создания привязок команд в конструкторе экземпляра, их понадобится  создавать в статическом конструкторе с помощью примерно такого кода:

CommandManager.RegisterClassCommandBinding(typeof(ColorPickerUserControl),

      new CommandBinding(ApplicationCommands.Undo,

      UndoCommand_Executed, UndoCommand_CanExecute));

Хотя этот код изменен  незначительно, все же он демонстрирует  существенный сдвиг. Поскольку ссылки на методы UndoCommand_Executed() и UndoCommand_CanExecute() присутствуют в конструкторе, оба  они должны быть статическими. Чтобы  извлечь данные экземпляра (такие  как информацию о текущем и предшествующем цветах), нужно привести отправителя события к объекту ColorPicker и использовать его.

Ниже приведен пересмотренный код обработки команд:

private static void UndoCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)

        {

            ColorPickerUserControl colorPicker = (ColorPickerUserControl)sender;

            e.CanExecute = colorPicker.previousColor.HasValue;

        }

        private static void UndoCommand_Executed(object sender, ExecutedRoutedEventArgs e)

        {

            ColorPickerUserControl colorPicker = (ColorPickerUserControl)sender;           

            colorPicker.Color = (Color)colorPicker.previousColor;

        }

Кстати, этот подход не ограничивается командами. Если хотите привязать логику обработки событий к элементу управления, можете воспользоваться  обработчиком событий класса с методом EventManager.RegisterClassHandler(). Обработчики событий  класса всегда вызываются перед обработчиками  событий экземпляра, позволяя легко  подавлять события.

UserControl

Пользовательские элементы предлагают довольно-таки безболезненный, но в некотором отношении ограниченный способ создания специальных элементов  управления. Чтобы понять — почему, давайте присмотримся к тому, как  функционирует UserControl.

"За кулисами" класс  UserControl работает во многом подобно  классу ContentControl, от которого он  унаследован. В действительности  ключевых отличий немного:

  • Класс UserControl изменяет некоторые значения по умолчанию. А именно: устанавливает IsTabStop и Focusable в false (так что он не занимает отдельного места в последовательности обхода по клавише <ТаЬ>), а также устанавливает HorizontalAlignment и VerticalAlignment в Stretch (вместо Left и Тор), в результате заполняя все доступное пространство.
  • Класс UserControl применяет новый шаблон элемента управления, состоящий из элемента Border, который упаковывает ContentPresenter. Элемент ContentPresenter хранит в себе содержимое, которое добавляется посредством кода разметки.
  • Класс UserControl изменяет источник маршрутизированных событий. Когда событие распространяется пузырьком или туннелируется от элемента управления, находящегося внутри пользовательского элемента, к элементу, находящемуся вне его, источник изменяется и указывает на пользовательский элемент управления вместо первоначального элемента. Это немного повышает степень инкапсуляции.

Например, при обработке  события UIElement.MouseLeftButtonDown в контейнере компоновки, содержащем в себе созданный  ранее указатель цвета, будет  получено событие, когда выполняется  щелчок кнопкой мыши внутри Rectangle. Однако источником этого события будет  не Rectangle, а объект ColorPicker, содержащий этот Rectangle. Если создать тот же самый  указатель цвета как обычный  элемент с содержимым, то этого  не будет — в данном случае на вас возлагается обязанность  перехватывать событие в элементе управления, обрабатывать его и возбуждать повторно.

Наиболее существенное отличие  пользовательских элементов от других типов специальных элементов  управления заключается в способе  их проектирования. Как и все элементы управления, пользовательские элементы имеют шаблон. Однако этот шаблон изменяется редко. Вместо этого создается код  разметки как часть класса специального пользовательского элемента управления, и этот код разметки обрабатывается с применением метода InitializeComponent() при создании элемента. С другой стороны, элемент управления, не имеющий  внешнего вида, не имеет и разметки. Все, что ему нужно — это  шаблон.

Обычный ContentControl имеет следующий  упрощенный шаблон:

<ControlTemplate TargetType="ContentControl">

   <ContentPresenter

      ContentTemplate="{TemplateBindmg ContentControl.ContentTemplate}"

      Content="{TemplateBinding ContentControl.Content} " />

</ControlTemplate>

Этот шаблон всего лишь наполняет полученным содержимым и  применяет необязательный шаблон содержимого. Свойства вроде Padding, Background, HorizontalAlignment и VerticalAlignment не дают никакого эффекта, если только явно не привязать их.

UserControl имеет похожий шаблон, но с несколькими дополнительными  тонкостями. Наиболее очевидно то, что он добавляет элемент Border и привязывает его свойства  к свойствам BorderBrush, BorderThickness, Background и Padding пользовательского элемента  управления, чтобы они что-нибудь  делали. Вдобавок ContentPresenter внутри привязывается  к свойствам выравнивания.

Формально шаблон пользовательского  элемента управления может быть изменен. В действительности можно переместить  весь код разметки в шаблон, лишь слегка видоизменив ее. Но на самом  деле делать это незачем: если необходим  более гибкий элемент управления, в котором внешний вид отделен  от интерфейса, предоставляемого классом  этого элемента, то лучше создать  собственный элемент управления, лишенный внешнего вида.

Создание элемента управления, лишенного внешнего вида

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

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

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

Рефакторинг кода указателя цвета

Превратить рассмотренный  указатель цвета в элемент  управления без внешнего вида не так  трудно. Первый шаг прост — нужно  всего лишь изменить объявление класса, как показано ниже:

public class ColorPicker : System.Windows.Controls.Control

{ ... }

В этом примере класс ColorPicker наследуется от Control. Класс FrameworkElement не подходит, поскольку указатель  цвета требует взаимодействия с  пользователем, а другие высокоуровневые  классы не могут точно описать  его поведение. Например, указатель  цвета не позволяет вставлять  в него другое содержимое, а потому ContentControl тоже не годится.

Код внутри класса ColorPicker точно  такой же, как код пользовательского  элемента управления (за исключением  того факта, что из конструктора понадобится  удалить вызов InitializeComponent()). Вы следуете тому же подходу для определения  свойств зависимости и маршрутизируемых событий. Единственное отличие связано с необходимостью сообщения WPF о том, что для класса элемента управления будет предоставлен новый стиль. Этот стиль будет обеспечен новым шаблоном элемента. (Если пропустить этот шаг, будет использован шаблон, определенный в базовом классе.)

Чтобы сообщить WPF о том, что  предоставляется новый стиль, следует  вызвать метод OverrideMetadata() в статическом конструкторе класса. Этот метод вызывается на свойстве DefaultStyleKeyProperty, которое является свойством зависимости, определяющим стиль по умолчанию для элемента управления. Необходимый код выглядит так:

DefaultStyleKeyProperty.OverrideMetadata(typeof(ColorPicker),

        new FrameworkPropertyMetadata(typeof(ColorPicker)));

Можно предоставить другой тип, если нужно использовать шаблон другого класса элемента управления, но почти всегда для каждого из собственных пользовательских элементов  управления будет создаваться специфический  стиль.

Рефакторинг кода разметки указателя цвета

После добавления вызова OverrideMetadata понадобится просто подключить правильный стиль. Этот стиль должен быть помещен  в словарь ресурсов по имени generic.xaml, который следует сохранить в папке Themes проекта. Таким образом, этот стиль будет распознан как стиль по умолчанию для элемента управления. Для добавления файла generic.xaml выполните следующие шаги:

  • Щелкните правой кнопкой мыши на проекте библиотеки классов в окне Solution Explorer и выберите в контекстном меню пункт Add --> New Folder (Добавить --> Новая папка).
  • Назовите новую папку Themes (темы).
  • Щелкните правой кнопкой мыши на папке Themes и выберите в контекстном меню пункт Add --> New Item (Добавить --> Новый элемент).
  • В диалоговом окне Add New Item (Добавление нового элемента) выберите вариант XML file template (Шаблон в виде файла XML), введите имя generic.xaml и щелкните на кнопке Add (Добавить).

Часто библиотека пользовательских элементов управления содержит несколько  таких элементов. Чтобы держать  их стили отдельно для облегчения редактирования, файл generic.xaml часто  использует слияние словарей ресурсов. В следующей разметке показано содержимое файла generic.xaml, который извлекает  ресурсы из ресурсного словаря ColorPicker.xaml в той же папке Themes библиотеки элементов  управления по имени CustomControls:

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    <ResourceDictionary.MergedDictionaries>

        <ResourceDictionary Source="/ColorPicker;component/themes/ColorPicker.xaml"></ResourceDictionary>

    </ResourceDictionary.MergedDictionaries>

</ResourceDictionary>

Стиль пользовательского  элемента управления должен использовать атрибут TargetType для автоматического  присоединения себя к указателю  цвета.

Стиль можно использовать для установки любых свойств в классе элемента управления (независимо от того, наследуются они от базового класса или добавлены вами). Однако наиболее полезная задача, которую выполняет стиль — это применение нового шаблона, определяющего визуальное представление по умолчанию для элемента управления.

Преобразовать обычный код  разметки (подобный тому, что используется для нашего указателя цвета) в  шаблон элемента управления очень легко. Необходимо принять во внимание описанные  ниже соображения:

  • При создании выражений привязки, которые связываются со свойствами родительского класса элемента управления, нельзя использовать свойство ElementName. Вместо него нужно применять свойство RelativeSource для указания того, что необходимо привязаться к родительскому элементу управления. Если все, что необходимо — это однонаправленная привязка, обычно можно использовать облегченное расширение разметки TemplateBinding вместо полноценного Binding.
  • Присоединять обработчики событий в шаблоне элемента управления не допускается. Вместо этого потребуется назначить элементам узнаваемые имена и присоединять к ним обработчики событий программно в конструкторе элемента управления.
  • Не именуйте элемент в шаблоне элемента управления, если только не хотите присоединить обработчик событий для программного взаимодействия с ним. При необходимости именования элемента называйте его в стиле PART_ИмяЭлемента.

Принимая во внимание перечисленные  соображения, можно создать следующий  шаблон для указателя цвета:

Информация о работе WPF: пользовательские элементы