Моя вариация на тему SegmentedControl в Windows Phone

в 9:23, , рубрики: .net, windows phone, XAML, интерфейс, интерфейсы, разработка под windows phone, метки: , ,

Здравствуйте, уважаемыее!

Я пишу приложение-клиент под Windows Phone для одного стартапа, по просьбе заказчика взяв за образец дизайна уже готовое приложение для iPhone. Анонс приложения состоится несколько позже, когда оно будет готово и доступно для загрузки, а в сегодняшней статье я хотел бы рассказать о том, как решил один из вопросов дизайна.

В приложении-образце в нескольких местах используется элемент управления UISegmentedControl из iOS. У него есть несколько вариантов отображения, но в данном приложении используется примерно вот такой:

Моя вариация на тему SegmentedControl в Windows Phone

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

Идея

Единственное, что практически сразу пришло в голову, — это должны быть RadioButton, каким-то образом стилизованные. Но я абсолютно не представлял себе, как это должно выглядеть. В интерфейсе Windows Phone, по большому счёту, нет понятия объёмных элементов управления, поэтому SegmentedControl в том виде, в каком он присутствует на iOS — активная кнопка «вдавлена», а неактивные нет, — совершенно не подходит для интерфейса WP.

В процессе поиска ответа я наткнулся на статью о том, как реализовать на Windows Phone CheckBox/RadioButton в стиле Windows 8, то есть что-то подобное:

Моя вариация на тему SegmentedControl в Windows Phone

Такой вариант мне очень понравился, поскольку он, во-первых, достаточно приятно выглядит, а во-вторых, не противоречит дизайну WP, поскольку используется как минимум при выборе фотографий в галерее:

Моя вариация на тему SegmentedControl в Windows Phone

Таким образом, нужно было стилизовать RadioButton в подобном виде с некоторыми изменениями, которые отвечали бы моим потребностям. А поскольку к указанной статье автор любезно прикрепил XAML с получившимися у него стилями, я решил воспользоваться ими (тем более, что до дизайнера мне, пожалуй, далеко).

Как оказалось, в позаимствованных стилях прямоугольная обводка RadioButton акцентным цветом появляется вместе с уголком и галкой только при выборе, т.е. если RadioButton не выбрана, то обводки вокруг неё нет:

Моя вариация на тему SegmentedControl в Windows Phone

К тому же, мне хотелось изменить и цвет текста на неактивных кнопках, чтобы он был… ну, неактивным.

Реализация

Поскольку мне нужна была только RadioButton, то стили для CheckBox из заимствованного XAML я тут же удалил… Поэтому если кому-то они будут нужны, то в моей реализации, приложенной к статье чуть ниже, вы их не найдёте. Но я постараюсь подробнее описать, что же я поменял в заимствованном стиле для RadioButton, так что воспроизвести это для CheckBox не составит труда.

Сначала нужна была обводка для неактивной кнопки, поэтому в стиле, в Grid (сетке) с именем «CheckElements», появился такой вот Border, по образу и подобию обводки для активной кнопки:

<Border x:Name="NormalBorder" BorderThickness="3" BorderBrush="{StaticResource PhoneDisabledBrush}" />

Ресурс кисти

Надо признаться, я был несколько удивлён следующими строками в описании активной обводки:

<Border.BorderBrush>
    <SolidColorBrush Color="{StaticResource PhoneAccentColor}"/>
</Border.BorderBrush>

— ведь вместо этого великолепия можно было использовать ресурс кисти и написать просто BorderBrush="{StaticResource PhoneAccentBrush}".

Кроме того, для состояния Disabled, когда RadioButton не выбрана, я добавил анимацию (по сути просто триггер) перекрашивания текста кнопки в неактивный цвет:

<VisualState x:Name="Disabled">
    <Storyboard>
        ...
        <ObjectAnimationUsingKeyFrames Storyboard.TargetProperty="Foreground"
                                       Storyboard.TargetName="ContentContainer">
            <DiscreteObjectKeyFrame KeyTime="0"
                                    Value="{StaticResource PhoneDisabledBrush}" />
        </ObjectAnimationUsingKeyFrames>
    </Storyboard>
</VisualState>

Вдобавок я удалил все упоминания эллипсов под названиями «CheckBackground» и «CheckMark», поскольку они оставались от оригинального стиля RadioButton и в новом стиле были не нужны. Это же позволило убрать разделение на колонки в Grid с содержимым кнопки.

Не потребовавшаяся распорка

При использовании заимствованного стиля, если RadioButton была не выбрана (было задано свойство IsChecked="false") и если эта RadioButton не находилась на одной строке с выбранными кнопками, её высота была меньше, чем высота выбранной кнопки. Поскольку изначально автор этих стилей хотел создать радио-кнопки для картинок, как, например, в галерее Windows Phone, для него это проблемой не было, но для меня, в качестве содержимого RadioButton задающего только текст, это было неприемлемо. Сначала я добавил ещё один Border, служащий «распоркой» в случае, если высота RadioButton меньше высоты уголка с галкой, но потом необходимость в нём отпала…

Сейчас, при написании этой статьи, я понимаю, что дальше совершил ошибку. Но поскольку опыта в стилях у меня не так много (повторюсь, от дизайна я ещё далёк), я взял да и удалил все упоминания об элементе Border с именем «border_copy», заменив его на, как мне показалось, абсолютно такой же просто «border». Это было ошибкой, но это позволило мне впоследствии ещё раз переосмыслить стиль RadioButton'а, который я хотел бы применить.

Результаты и ошибки

После осуществления этих манипуляций. изображённый выше элемент управления приобрёл следующий вид:

Моя вариация на тему SegmentedControl в Windows Phone

В дизайнере Visual Studio всё выглядело отлично, однако тесты на аппарате показали, что если нажать на уже активный RadioButton, цветная обводка на нём пропадает и из элементов активной кнопки остаётся только уголок с галкой. Это как раз и стало результатом ошибки с удалением «border_copy»: он использовался в визуальном состоянии Pressed, в то время как просто «border» был задействован в состоянии Checked.

Всё дело в том, что в стилях XAML нельзя дважды задать одно и то же значение одному и тому же свойству одного и того же элемента. В моей ситуации происходило следующее: в состоянии Checked элементу «border» устанавливался параметр Visibility="Visible", а при повторном нажатии на активную кнопку в состоянии Pressed происходило то же самое! Это и приводило к ошибке.

Пока я это выяснял, я обнаружил, что стиль написан не очень «красиво»: много лишнего. Во-первых, в каких-либо состояниях задавать элементам их исходные свойства нет необходимости. Т.е., если у элемента «border» изначально задано Visibility="Collapsed", то в состоянии Unchecked задавать ему заново такое же значение просто не нужно. Это позволило здорово «причесать» код. Во-вторых, судя по примеру на MSDN, в оригинальном стиле для сокрытия и отображения элементов используется не свойство Visibility="Collapsed"/"Visible", а свойство Opacity=0/1. Это позволило ещё больше облагородить код за счёт использования DoubleAnimation вместо ObjectAnimationUsingKeyFrames, а также отказаться от использования «распорки», описанной под спойлером выше, поскольку Path уголка с галкой всегда находился где-то рядом, только был прозрачен.

В итоге появилась одна обводка для состояния Pressed, которая после небольшого переосмысления стала выглядеть так:

<Border x:Name="PressedBorder"
        BorderThickness="3"
        BorderBrush="{TemplateBinding Foreground}"
        Opacity="0" />

Переосмысление коснулось в том числе и свойства BorderBrush, которое стало привязано не к акцентному цвету системы, а к цвету Foreground радио-кнопки.

Наконец, поскольку в Windows Phone 7.x нет ресурса «PhoneRadioCheckBoxBorderBrush», я заменил его на «PhoneForegroundBrush», поскольку «PhoneRadioCheckBoxBorderBrush» в WP8 — это кисть цвета «PhoneForegroundColor».

В конечном итоге получился вполне рабочий и, как мне кажется, даже аккуратный стиль, позволивший мне создать подобие SegmentedControl для Windows Phone. Да, у каждой радио-кнопки задано свойство Margin="-12,0", чтобы они плотно прилегали друг к другу.

Моя вариация на тему SegmentedControl в Windows Phone

Автор: SgtRiggs91

Источник

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


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js