Расширение, изменение и создание элементов управления на платформе UWP. Часть 2

в 16:16, , рубрики: .net, uwp, Блог компании Mobile Dimension, разработка мобильных приложений, разработка под windows, элементы управления

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 1

Итак, мы снова рассказываем об элементах управления на платформе UWP.

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

Часть 2. Изменение существующих элементов управления

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

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 2

Уровни воздействия на элементы управления

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

Следующим уровнем воздействия является вмешательство в разметку шаблона существующего элемента управления.

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

Официальная документация Microsoft. На данной странице представлен список элементов управления, поставляемых с UWP по каждому из которых можно получить шаблон разметки
• Приложение Blend For Visual Studio, умеющее предоставлять шаблоны элементов управления

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 3
Создание копии шаблона элемента управления в Blend

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 4
Создание копии шаблона Button

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 5
Копия шаблона Button

Оба способа обладают своими преимуществами.

• Документация:

— более простой и быстрый доступ к шаблону,
— явное обращение внимания на используемые им ThemeResources и состояния VisualStateManager

• Blend:

— можно использовать как песочницу для работы с нужным шаблоном,
— легкий доступ к шаблонам дочерних объектов,
— дополнительные функции IDE облегчающие работу при создании анимации

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

Общее строение шаблона элемента управление

Ознакомимся с ним на примере шаблона элемента управления CheckBox.

<Style TargetType="CheckBox">
    <Setter Property="Background" Value="Transparent" />
    <Setter Property="Foreground" Value="{ThemeResource SystemControlForegroundBaseHighBrush}"/>
    <Setter Property="Padding" Value="8,5,0,0" />
    <Setter Property="HorizontalAlignment" Value="Left" />
    <Setter Property="VerticalAlignment" Value="Center" />
    <Setter Property="HorizontalContentAlignment" Value="Left"/>
    <Setter Property="VerticalContentAlignment" Value="Top"/>
    <Setter Property="FontFamily" Value="{ThemeResource ContentControlThemeFontFamily}" />
    <Setter Property="FontSize" Value="{ThemeResource ControlContentThemeFontSize}" />
    <Setter Property="MinWidth" Value="120" />
    <Setter Property="MinHeight" Value="32" />
    <Setter Property="UseSystemFocusVisuals" Value="True" />
    <Setter Property="Template">
        <Setter.Value>
            <ControlTemplate TargetType="CheckBox">
                <Grid Background="{TemplateBinding Background}"
                      BorderBrush="{TemplateBinding BorderBrush}"
                      BorderThickness="{TemplateBinding BorderThickness}">
                    <!--<VisualStateManager.VisualStateGroups>
                        ...
                    </VisualStateManager.VisualStateGroups>-->
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="20" />
                        <ColumnDefinition Width="*" />
                    </Grid.ColumnDefinitions>
                    <Grid VerticalAlignment="Top" Height="32">
                        <Rectangle x:Name="NormalRectangle"
                           Fill="Transparent"
                           Stroke="{ThemeResource SystemControlForegroundBaseMediumHighBrush}"
                           StrokeThickness="{ThemeResource CheckBoxBorderThemeThickness}"
                           UseLayoutRounding="False"
                           Height="20"
                           Width="20" />
                        <FontIcon x:Name="CheckGlyph"
                          FontFamily="{ThemeResource SymbolThemeFontFamily}"
                          Glyph=""
                          FontSize="20"
                          Foreground="{ThemeResource SystemControlHighlightAltChromeWhiteBrush}"
                          Opacity="0" />
                    </Grid>
                    <ContentPresenter x:Name="ContentPresenter"
                              ContentTemplate="{TemplateBinding ContentTemplate}"
                              ContentTransitions="{TemplateBinding ContentTransitions}"
                              Content="{TemplateBinding Content}"
                              Margin="{TemplateBinding Padding}"
                              HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}"
                              VerticalAlignment="{TemplateBinding VerticalContentAlignment}"
                              Grid.Column="1"
                              AutomationProperties.AccessibilityView="Raw"
                              TextWrapping="Wrap" />
                </Grid>
            </ControlTemplate>
        </Setter.Value>
    </Setter>
</Style>

Копия шаблона CheckBox

В данном шаблоне можем выделить несколько составляющих его частей:

• Набор простых сеттеров вида <Setter Property="Property_Name" Value="Property_Value" />. Они выполняют функцию указания значений по умолчанию свойств элемента управления. В частности, например, по умолчанию CheckBox не может иметь ширину меньшую, чем 120. На этом примере видно, что некоторые проблемы в процессе верстки могут идти из шаблона по умолчанию, который может «мешать» достижению требуемого результата.

• Список простых сеттеров заканчивается сеттером <Setter Property="Template">…</Setter> в котором и определяется каркас строения элемента управления. Этот каркас можно разбить на две основных части:

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

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 6
Список визуальных состояний CheckBox

Ознакомившись со списком визуальных состояний элемента управления CheckBox, видим, что в нем определенно по 4 визуальных состояния для каждого из 3 значений свойства IsChecked: true, false, null.

Так, например, переход в визуальное состояние UncheckedPointerOver устанавливает цвет обводки элемента NormalRectangle в значение SystemControlHighlightBaseHighBrush, берущееся из темы приложения.

<VisualState x:Name="UncheckedPointerOver">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames 
            Storyboard.TargetName="NormalRectangle"
            Storyboard.TargetProperty="Stroke">
            <DiscreteObjectKeyFrame 
                KeyTime="0"
                Value="{ThemeResource SystemControlHighlightBaseHighBrush}" />
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>
<VisualState x:Name="UncheckedPressed">
    <Storyboard>
        <ObjectAnimationUsingKeyFrames 
            Storyboard.TargetName="NormalRectangle"
            Storyboard.TargetProperty="Fill">
            <DiscreteObjectKeyFrame 
                KeyTime="0" 
                Value="{ThemeResource SystemControlBackgroundBaseMediumBrush}" />
        </ObjectAnimationUsingKeyFrames>
        <ObjectAnimationUsingKeyFrames 
            Storyboard.TargetName="NormalRectangle"
            Storyboard.TargetProperty="Stroke">
            <DiscreteObjectKeyFrame 
                KeyTime="0" 
                Value="{ThemeResource SystemControlHighlightTransparentBrush}" />
        </ObjectAnimationUsingKeyFrames>
        <DoubleAnimation
            Storyboard.TargetName="NormalRectangle"
            Storyboard.TargetProperty="StrokeThickness"
            To="{ThemeResource CheckBoxCheckedStrokeThickness}"
            Duration="0" />
    </Storyboard>
</VisualState>

Пара визуальных состояний CheckBox

Также обратим внимание, что визуальное состояние <VisualState x:Name="UncheckedNormal" />, указанного в коллекции, пусто. Причина этого в том, что механика работы менеджера визуальных состояний такова, что при переходе из состояния A в состояние B, те свойства, на которые оказывало влияние состояние A, но не оказывает состояние B, берут значения определенные как по умолчанию. Данная механика повторяет механику триггеров реализованную в WPF, но не вошедшую в UWP.

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

С шаблоном существующего элемента управления можно совершить следующие действия:

• Удаление «лишних» элементов
• Изменение требуемых элементов, уже определенных в нем
• Добавление новых элементов.

Модификация шаблона посредством удаления составляющих его элементов

Частой задачей, с которой мы сталкивались — убрать у элемента управления TextBox кнопку очистки введенного текста. Представим одно из возможных решений данной задачи. Получив шаблон, находим в нем элемент который необходимо удалить – <Button x:Name="DeleteButton" … />

<Button x:Name="DeleteButton"
                Grid.Row="1"
                Style="{StaticResource DeleteButtonStyle}"
                BorderThickness="{TemplateBinding BorderThickness}"
                Margin="{ThemeResource HelperButtonThemePadding}"
                IsTabStop="False"
                Grid.Column="1"
                Visibility="Collapsed"
                FontSize="{TemplateBinding FontSize}"
                MinWidth="34"
                VerticalAlignment="Stretch"/>

Кнопка на удаление

Также важно не забыть удалить все остальные места в разметке, которые обращаются тем или иным образом к данному элементу. Так удалению подвергаются: <Style x:Name="DeleteButtonStyle"/> и <VisualState x:Name="ButtonVisible"/>. Удаление последнего в общем позволяет нам и вовсе удалить весь <VisualStateGroup x:Name="ButtonStates"/>.

Определим получившемуся стилю x:Key — <Style x:Key="articleTextBox" TargetType="TextBox"> и применим к требуемому полю ввода текста <TextBox Style="{StaticResource articleTextBox}"/>

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 7
TextBox без кнопки очистки

Стоит отметить, что данный способ модификации шаблона не всегда срабатывает. В рамках следующей статьи будут рассмотрены внутренности элементов управления на уровне Control.cs и мы увидим, что порой не только в верстке определяются зависимости на элементы составляющие шаблон, но и в коде. Бывают случаи, когда модификация шаблона посредством удаления приводит к некорректной работе элемента управления либо и вовсе исключениям (exceptions).

Модификация шаблона посредством изменений составляющих его элементов

В нашей практике была ситуация, когда нам потребовалось внести следующие изменения в элемент управления CalendarDatePicker:

• Выделить хедеры выходных дней красным цветом
• Увеличить размер шрифта хедеров выходных дней
• Поменять вертикальные стрелки навигации на горизонтальные
• Поместить стрелку «влево» в левую часть панели, стрелку «вправо» в правую часть, а заголовок «год месяц декада» расположить посередине между этими стрелками

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 8
Внешний вид календаря CalendarDatePicker по умолчанию

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 9
Новый вид календаря CalendarDatePicker

• Получаем шаблон элемента управления CalendarDatePicker.

В процессе анализа шаблона обнаруживаем, что в него входит другой элемент управления, содержащий то, что мы ищем – CalendarView.

Расширение, изменение и создание элементов управления на платформе UWP. Часть 2 - 10
CalendarView входящий в шаблон CalendarDatePicker

• Получаем шаблон элемента управления CalendarView. В нем делаем следующие изменения:
• У кнопок <Button x:Name="PreviousButton"/> и <Button x:Name="NextButton"/> устанавливаем значения свойств Content в "" и "" соответственно
• У полей <TextBlock x:Name="WeekDay6"/> и <TextBlock x:Name="WeekDay7"/> устанавливаем значение свойства Foreground в Red
• В стиль <Style x:Key="WeekDayNameStyle"/> добавляем сеттер />
• В контейнере Grid содержащем <Button x:Name="PreviousButton"/> и <Button x:Name="NextButton"/> делаем очевидные изменения в верстке для выполнения поставленной задачи.

Готово. Таким образом, мы взяли уже готовый шаблон и привели его внешний вид в соответствие с требованиями.

Модификация шаблона посредством добавления в него новых элементов

Очевидно, что данный способ без доступа к Control.cs очень ограничен. Максимум что от него можно получить – добавление какой-то визуальной составляющей, которую можно разнообразить посредством состояний VisualStateManager. Это относится к третьему способу расширения существующих элементов управления.

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

Автор: MobileDimension

Источник

Поделиться

* - обязательные к заполнению поля