Как стать автором
Обновить

WPF: контролы лишенные внешнего вида и неразрешимая задача выбора конфигурации темплейта

Уровень сложностиСредний
Время на прочтение9 мин
Количество просмотров2.9K

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

Возможно, кому‑то будет интересно сравнить то, что предлагает WPF, с концепциями реализованными в JavaFX.


Откуда берутся проблемы при работе с визуальными библиотеками

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

Для разработки собственных визуальных представлений для десктопных задач навигации по инженерным данным мы выбрали технологии WPF. Собственно, у нас не было большого выбора, поскольку проект реализуется под Windows, и большая часть кода выполнена на C#. По результатам того, что уже сделано, можно вполне уверенно сказать, что, несмотря на некоторые трудности освоения технологии WPF в начале, мы совершенно не разочаровались в ней. Более того, функциональность и возможности WPF поражают воображение, и это, зачастую, является причиной одной очень парадоксальной, как мне кажется, проблемы. Каждая конкретная задача визуализации, компоновки, использования комбинации визуальных управляющих элементов (контролов) имеет чуть ли не десятки решений, выбрать из которых одно, самое лучшее, кажется, не представляется возможным.

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

Актуальность технологии

Так же как язык С незаменим для разработки и обновления компонентов ядра операционной системы, так и WPF теперь не заменим (как мне кажется) для разработки визуального оформления — фронтенда операционной системы. Я даже не постесняюсь предположить, что WPF и XAML станут такой же бессмертной технологией, по крайней мере, в Windows, как и язык С на уровне ядра операционной системы.

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

DevExpress

Telerik

Xceed

caliburn.micro

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

Контролы лишенные внешнего вида

Наверно мало кто обращает внимание, что, во времена господства WinForm, все кнопки в основном были прямоугольными. А вот чтобы создать какую‑то особенную кастомную кнопку, надо было искать особенную визуальную библиотеку. WPF решает проблему определения формы кнопки и любого контрола, и даже целого окна кардинально и, видимо, навсегда.

Дополнение про «навсегда» здесь не означает, что не будет какой‑то новой технологии или фреймворка, или <‑придумайте свое название чего→ которые сделают процесс определения формы контрола более эффективным в каком‑то смысле. Слово «Навсегда» здесь означает, что эта проблема один раз уже решена, и вряд ли в ближайшей перспективе можно будет придумать какой‑то принципиально новый способ решения этой проблемы. Тем более что мы видим, что и в экосистеме вокруг Java, например, предлагается во многом похожая технология JavaFX.

Тут надо сделать одно пояснение относительно определения «лишенный внешнего вида» элемент. Элемент создается и предоставляется с внешним видом по умолчанию — то есть вы можете его сразу использовать с тем внешним видом, с которым он изначально разработан — концепция предоставляет вам возможность изменять внешний вид по своему усмотрению. Отсутствие внешнего вида надо понимать именно в том смысле, что вы не всегда знаете, с каким исходным внешним видом элемент был создан.

Давайте же посмотрим, каким образом эта проблема управления формой и компоновкой контрола решается в WPF на примере из учебника по WPF.

На рисунке приведен классический пример управления внешним видом элемента выбора цвета.

Мы видим две реализации объекта одного C# класса! Важно обратить внимание, что визуальный объект в красной рамке (сверху на двух картинках) и визуальный объект в черной рамке с желтым фоном (снизу) — это совершенно идентичные объекты одного C# класса (типа).

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

Пример явно демонстрирует разные аспекты компоновки, формы, внутреннего содержания элемента которыми можно управлять:

  • Форма сегмента отображения цвета изменилась с прямоугольной на круглую;

  • Расположение ползунков изменилось с горизонтального на вертикальное;

  • Были добавлены подписи для ползунков;

  • Цвет и фона, ширина и цвет рамки.

Каким же образом происходит такое изменение внешнего вида визуального элемента или как подменить описание внешнего вида элемента?

Дело в том, что в WPF реализована концепция разделения на два типа кода:

один из которых — C#‑код — реализует логику работы визуального элемента и/или его функциональность,

а второй — XAML‑код — реализует описание внешнего вида‑способ рисования визуального компонента,

причем это разделение достигается самым, наверно, радикальным способом — описание внешнего вида пишется на другом языке, на декларативном языке XAML. Для создания элементов, лишенных внешнего вида, такое описание внешнего вида включает в себя все визуальные составляющие, такие как:

  • цвета (фона, под‑элемента, …),

  • линии,

  • геометрические фигуры,

  • низкоуровневые визуальные элементы входящие в состав, такие как текст, рамки,

  • геометрия взаимного расположения элементов.

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

Создание особенной кастомной кнопки

Пусть нам нужно некоторое особенное оформление для кнопки как на рисунке:

        Кастомная кнопка в обычном состоянии                       Кастомная кнопка с наведенным курсором мыши
Кастомная кнопка в обычном состоянии Кастомная кнопка с наведенным курсором мыши

Тут надо отметить, что слово «создание» звучит, конечно, разочаровывающе, для такой, в общем то, рутинной работы, как создание кнопки, хоть она и трижды кастомная. Но дело в том, что для WPF это не совсем корректное слово в этом контексте. Мы же не собираемся менять логику работы кнопки — она также должна отрабатывать нажатие мышью и передавать соответствующее событие в код для обработки. В данном случае нам нужно только изменить внешний вид кнопки, и как мы уже отметили выше, для WPF это отдельная задача, которая никак не затрагивает исполняющий класс этого визуального объекта.

Чтобы обычная кнопка стала выглядеть по‑особенному, как мы видим на рисунке, надо следующим образом переопределить ее стандартный темплейт, просто перечислив вот таким образом те рисованные объекты, из которых она теперь будет состоять:

<ControlTemplate TargetType="Button">
    <Grid Width="{TemplateBinding Width}" Height="{TemplateBinding Height}" ClipToBounds="True">
        <!-- Outer Rectangle with rounded corners. -->
        <Rectangle x:Name="outerRectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="{TemplateBinding Background}" RadiusX="20" RadiusY="20" StrokeThickness="5" Fill="Transparent" />
        <!-- Inner Rectangle with rounded corners. -->
        <Rectangle x:Name="innerRectangle" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Stroke="Transparent" StrokeThickness="20" Fill="{TemplateBinding Background}" RadiusX="20" RadiusY="20" />
        <!-- Present Content (text) of the button. -->
        <DockPanel Name="myContentPresenterDockPanel">
            <ContentPresenter x:Name="myContentPresenter" Margin="20" Content="{TemplateBinding  Content }" TextBlock.Foreground="Black" />
        </DockPanel>
    </Grid>
    <ControlTemplate.Triggers>
    <Trigger Property="IsMouseOver" Value="True">
        <Setter Property="Background" Value="Red"/>
    </Trigger>
    </ControlTemplate.Triggers>
</ControlTemplate>

Здесь мы видим, например, что приходится применять тригер, чтобы кнопка реагировала на попадание курсора мыши (событие IsMouseOver) и перерисовывалась с красным цветом фона (Background).

Где найти кнопку с пиктограммой и с текстом

На Хабре есть целая статья посвященная этой «проблеме» создания кастомизированной кнопки:

WPF: 4 варианта кнопки с иконкой и текстом

Суть в том, что WPF позволяет определить чуть ли не десятки способов добавления иконки‑пиктограммы на кнопку. Например, автор указанной статьи описывает шесть таких способов, достаточно сильно ограничивая себя в том, что считать способом добавления иконки на кнопку.

Вот так вот могут выглядеть кнопки «ОК» и «Cancel», созданные на WPF:

Что называется, найдите хоть одно отличие от кнопок из WinForms.

Конечно, визуально невозможно заметить, что, для эксперимента, я еще и изменил темплейт кнопки «Cancel», и она теперь сделана на основе библиотечного объекта StackPanel, тогда как исходный темплейт, использованный для кнопки «ОК», собран на основе тоже библиотечного объекта Grid.

Исходный темплейт для кнопки «ОК»:

<DataTemplate>
    <Grid>
    <Image
        Source="{Binding Path=(local:EyeCandy.Image), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"
        HorizontalAlignment="Left"
        Margin="8,0,0,0"
        Height="16"
        Width="16" />
    <TextBlock
        Text="{TemplateBinding Content}"
        HorizontalAlignment="Center" />
    </Grid>
</DataTemplate>
Альтернативный темплейт для кнопки «Cancel»:
<DataTemplate>
    <StackPanel Orientation="Horizontal" HorizontalAlignment="Left" Width="101">
        <Image Source="{Binding Path=(local:EyeCandy.Image), RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Button}}}"
Height="16"
Width="16" HorizontalAlignment="Left" />
        <TextBlock Text="Cancel" HorizontalAlignment="Center" Margin="22,0,0,0"/>
    </StackPanel>
</DataTemplate>

Вы можете задать вопрос: зачем же нужно такое разнообразие, когда мне (нам) достаточно простой кнопки с иконкой? Ответ не заставит себя ждать: WPF спроектирован для того, чтобы снять ограничения на реализацию любого уникального пользовательского интерфейса, да(!) эти дополнительные возможности по кастомизации стандартных контролов и разработке новых делают разработку UI более сложной хотя бы потому, что теперь надо хоть немножко понимать эту новую концепцию с новым языком XAML. Да! Система WPF более сложная для понимания, но эта сложность позволяет получить практически неограниченные возможности для построения уникальных визуальных представлений. При этом код для более сложных решений будет значительно компактнее, чем код для такого же визуального представления, написанный в старой парадигме. Вот эти новые возможности и оптимизация более сложных, зачастую даже условно невозможных решений, в рамках старой парадигмы с лихвой окупают повышение порога вхождения в использование технологии WPF.

Теги:
Хабы:
Всего голосов 7: ↑7 и ↓0+7
Комментарии18

Публикации