Добавление кода разметки

76

Теперь, когда общедоступный интерфейс пользовательского элемента управления определен, все, что осталось сделать — это написать код разметки, определяющий его внешний вид. В данном случае достаточно будет использовать базовый 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();
        }

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

Пройди тесты
Лучший чат для C# программистов