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

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

Описание

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

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

Реферат WPF.docx

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

Определение маршрутизируемых событий

Также может понадобиться добавить маршрутизируемые события, которые  позволяют уведомлять потребителя  элемента управления о том, что что-то произошло. В примере с указателем цвета удобно иметь событие, возбуждаемое при изменении цвета. Хотя это  событие может быть определено как  обычное событие .NET, применение маршрутизируемого  события позволит организовать пузырьковое  распространение и туннелирование, так что события смогут обрабатывать более высокоуровневые родители вроде содержащего элемент окна.

Как и в случае свойств  зависимости, первый шаг в определении  маршрутизируемого события —  это создание статического свойства для него, со словом Event, добавленным  в конец имени:

public static readonly RoutedEvent ColorChangedEvent;

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

// Регистрация маршрутизируемого события

ColorChangedEvent = EventManager.RegisterRoutedEvent("ColorChanged",RoutingStrategy.Bubble,

    typeof(RoutedPropertyChangedEventHandler), typeof(ColorPicker));

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

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

Определив и зарегистрировав  событие, понадобится создать стандартную  оболочку для события .NET, которая  примет событие. Эта оболочка события  может быть использована для присоединения (и удаления) слушателей события:

public event RoutedPropertyChangedEventHandler ColorChanged

{

     add { AddHandler(ColorChangedEvent, value); }

     remove { RemoveHandler(ColorChangedEvent, value); }

}

Вспомните, что обратный вызов OnColorChanged() инициируется при любой модификации свойства Color — будь то непосредственно или же при изменении компонентов цвета Red, Green и Blue.

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

В конце концов, в указателе  цвета будут работать четыре выражения  привязки данных. Три ползунка привязаны  к свойствам Red, Green и Blue, разрешая их изменение в диапазоне от 0 до 255 (допустимые значения для байта). Свойство Rectangle.Fill устанавливается  в SolidColorBrush, а свойство Color этой кисти  привязано к свойству Color пользовательского  элемента управления. Ниже приведен полный код разметки:

<UserControl x:Class="MyBaseUserControl.ColorPicker"

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

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

             Name="colorPicker">

    <Grid>

        <Grid.RowDefinitions>

            <RowDefinition Height="auto"></RowDefinition>

            <RowDefinition Height="auto"></RowDefinition>

            <RowDefinition Height="auto"></RowDefinition>

        </Grid.RowDefinitions>

        <Grid.ColumnDefinitions>

            <ColumnDefinition></ColumnDefinition>

            <ColumnDefinition Width="auto"></ColumnDefinition>

        </Grid.ColumnDefinitions>

        <Slider Name="sliderRed" Minimum="0" Maximum="255"

                Value="{Binding ElementName=colorPicker, Path=Red}"></Slider>

        <Slider Name="sliderGreen" Minimum="0" Maximum="255" Grid.Row="1"

                Value="{Binding ElementName=colorPicker, Path=Green}"></Slider>

        <Slider Name="sliderBlue" Minimum="0" Maximum="255" Grid.Row="2"

                Value="{Binding ElementName=colorPicker, Path=Blue}"></Slider>

        <Rectangle Grid.Column="1" Grid.RowSpan="3" Width="50" Stroke="Black" StrokeThickness="1"

                   StrokeDashArray="2 1">

            <Rectangle.Fill>

                <SolidColorBrush Color="{Binding ElementName=colorPicker,Path=Color}"></SolidColorBrush>

            </Rectangle.Fill>

        </Rectangle>

    </Grid>

</UserControl>

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

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

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

<Slider Name="sliderRed" Minimum="0" Maximum="255"

   Margin="{Binding ElementName=colorPicker,Path=Padding}"

   Value="{Binding ElementName=colorPicker,Path=Red}"></Slider>

Аналогично можно получить настройки рамки для Rectangle из свойств BorderThickness и BorderBrush класса UserControl. Опять-таки, это сокращение, которое может  быть оправдано при создании простых  элементов управления, но которое  можно усовершенствовать добавлением  дополнительных свойств (например, SliderMargin, PreviewBorderBrush и PreviewBorderThickness) или созданием  полноценного элемента управления, основанного  на шаблоне.

Именование пользовательских элементов управления

В приведенном примере  элементу UserControl верхнего уровня назначено  имя (colorPicker). Это позволяет писать простые выражения привязки данных, которые связывают свойства в  классе пользовательского элемента управления. Однако такой прием вызывает очевидный вопрос, а именно: что  происходит, когда в окне (или  на странице) создается экземпляр  пользовательского элемента управления, которому назначается новое имя?

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

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

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

<Slider Name="sliderRed" Minimum="0" Maximum="255"

   Value="{Binding Path=Red, RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type UserControl}}}">

</Slider>

Использование элемента управления

Когда создание элемента управления завершено, использовать его очень  легко. Чтобы применить указатель  цвета в другом окне, понадобится  отобразить сборку и пространство имен .NET на пространство имен XML, как показано ниже:

<Window x:Class="WpfApplication1.MainWindow"

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

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

        xmlns:lib="clr-namespace:MyBaseUserControl;assembly=ColorPicker"

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

<lib:ColorPicker Name="colorPicker" Margin="5"

          ColorChanged="colorPicker_ColorChanged"></lib:ColorPicker>

Поскольку свойство Color использует тип данных Color, а тип Color декорирован  атрибутом TypeConverter, WPF знает, как использовать ColorConverter для превращения строкового наименования цвета в соответствующий  объект Color перед установкой его  в свойство Color.

Код, обрабатывающий событие ColorChanged, достаточно прост:

private void colorPicker_ColorChanged(object sender, RoutedPropertyChangedEventArgs e)

        {

            txb.Text = e.NewValue.ToString();

        }

На этом создание пользовательского  элемента управления завершено. 

ПОДДЕРЖКА КОМАНД

Многие элементы управления обладают встроенной поддержкой команд. Добавить такую поддержку к разрабатываемому элементу управления можно двумя  способами:

  • Добавить привязки команд, которые свяжут элемент управления с определенными командами. В результате элемент сможет реагировать на команды без необходимости в дополнительном кодировании.
  • Создать новый объект RoutedUICommand для команды как статическое поле элемента управления, а затем добавить привязку к этой команде. Это позволит элементу управления автоматически поддерживать команды, еще не определенные в базовом наборе классов команд.

В следующем примере используется первый подход для добавления поддержки  команды ApplicationCommands.Undo.

Чтобы поддержать средство Undo в указателе цвета, необходимо отслеживать предыдущий цвет в поле-члене  класса:

private Color? previousColor;

Тот факт, что это поле допускает null-значения, имеет смысл, поскольку при первом создании элемента управления никакого предыдущего цвета  еще не установлено. (Можно также  программно очищать значение предыдущего  цвета после действий, которые  должны быть необратимыми.) Когда цвет изменяется, необходимо запомнить старое значение. Это можно сделать, добавив  следующую строку в конец метода OnColorChanged():

colorPicker.previousColor = (Color)е.OldValue;

Теперь имеется инфраструктура, необходимая для поддержки команды Undo. Все, что остается — это создать  привязку команды, которая подключит  элемент управления к команде  и обработает события CanExecute и Executed.

Лучшее место для создания привязки команды — момент первоначального  создания элемента управления. Например, в следующем коде для добавления привязки команды ApplicationCommands.Undo используется конструктор указателя цвета:

public ColorPickerUserControl()

        {

            InitializeComponent();

            SetUpCommands();

        }

 

        private void SetUpCommands()

        {

            CommandBinding binding = new CommandBinding(ApplicationCommands.Undo,

               UndoCommand_Executed, UndoCommand_CanExecute);

 

            this.CommandBindings.Add(binding);

        }

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

private void UndoCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)

{

   e.CanExecute = previousColor.HasValue;

}

И, наконец, когда команда  выполняется, можно заменить цвет предыдущим значением:

private void UndoCommand_Executed (object sender, ExecutedRoutedEventArgs e)

{

   this.Color = (Color)previousColor;

}

Команда Undo инициируется двумя другими способами. Можно применить стандартную клавиатурную привязку <Ctrl+Z>, когда соответствующий элемент в пользовательском элементе управления имеет фокус, или же добавить кнопку в клиентскую панель, которая будет инициировать команду.

В любом случае текущее  значение цвета отбрасывается и  применяется предыдущее. В данном примере сохраняется только один уровень информации отмены. Однако легко создать стек отмены, который  будет хранить последовательность старых значений. Для этого понадобится  лишь сохранять значения Color в коллекции  соответствующего типа. Коллекция Stack<T> из пространства имен System.Collections.Generic — подходящий выбор, поскольку реализует алгоритм "последний вошел — первый вышел", который как раз подходит для получения наиболее свежего предыдущего объекта Color при выполнении операции отмены.

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