Русский
Русский
English
Статистика
Реклама

От WPF к Авалонии

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

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

Стили

На первый взгляд, стили в Авалонии выглядят точно также, как и в WPF они задаются в блоке Styles с помощью селекторов и сеттеров. Первые выбирают набор блоков, к которым применяются стили, вторые задают непосредственно стили. Давайте сравним два одинаковых стиля в WPF и Авалонии:

<Style TargetType="TextBlock">  <Setter Property="HorizontalAlignment" Value="Center" />  <Setter Property="FontSize" Value="24"/></Style>
<Style Selector="TextBlock"><Setter Property="HorizontalAlignment" Value="Center" /><Setter Property="FontSize" Value="24"/></Style>

Как видите, в данном фрагменте различается только объявление тега Style в WPF для выбора целевого блока используется параметр TargetType, а в Авалонии - Selector. Однако селекторы в Авалонии куда мощнее, чем TargetType в WPF. Больше всего они напоминают селекторы из CSS с классами, псевдоклассами и кастомными обращениями.

Например, вот так мы можем задать размер шрифта для всех текстовых блоков с классом h1

<Styles>  <Style Selector="TextBlock.h1">    <Setter Property="FontSize" Value="24"/>  </Style></Styles><TextBlock Classes="h1">Header</TextBlock>

Псевдоклассы позволяют нам выбирать элементы в определенном состоянии когда они в фокусе, когда на них наведена мышь, когда чекбокс выбран и так далее. В WPF подобное реализовывалось через триггеры (стиль изменялся, когда активировалось некоторое событие), однако подход через псевдоклассы выглядит более удобным и современным.

<Styles>  <Style Selector="Button:pointerover">    <Setter Property="Button.Foreground" Value="Red"/>  </Style></Styles><Button>I will have red text when hovered.</Button>

И, конечно же, селекторы в Авалонии позволяют гибко выбирать целевые контролы для стилей через цепочки дочерних элементов, по совпадению нескольких классов, по шаблонам и по значениям определенных свойств. Опять же, это очень похоже на селекторы в CSS. Например, вот так мы можем выбрать кнопку, являющуюся прямым наследником элемента с классом block, имеющую значение свойства IsDefault = true:

.block > Button[IsDefault=true]

Полный список доступных селекторов и их описания вы можете найти в документации Авалонии.

Обновленный синтаксис XAML

Как и в случае со стилями, синтаксис XAML не слишком сильно отличается от WPF. Объявление контролов, параметры, биндинги все выглядит по-прежнему. Однако некоторые отличия все же есть синтаксис стал более емким и понятным, а где-то добавились новые возможности. Посмотрим на изменения по порядку.

Начнем с упрощений в синтаксисе. Самый простой пример такого упрощения это объявление строк и столбцов в Grid. Классическое, привычное с WPF объявление будет выглядеть следующим образом:

<Grid>  <Grid.RowDefinitions>    <RowDefinition Height="*"></RowDefinition>    <RowDefinition Height="Auto"></RowDefinition>    <RowDefinition Height="32"></RowDefinition>  </Grid.RowDefinitions></Grid>

Этот код будет отлично работать и в Авалонии, однако, помимо полного варианта объявления, добавился и сокращенный.

<Grid RowDefinitions="*,Auto,32,"/>

Упростилось и подключение зависимостей в XAML файлах. Теперь clr-namespace можно заменить на using. Такое изменение позволяет сделать подключение сторонних библиотек короче и читаемее.

Было: xmlns:styles="clr-namespace:Material.Styles;assembly=Material.Styles"

Стало: xmlns:styles="using=Material.Styles"

Другое любопытное изменение это вынесение DataTemplates и Styles в отдельные теги. Раньше они размещались внутри Resources.

<UserControl xmlns:viewmodels="clr-namespace:MyApp.ViewModels;assembly=MyApp">  <UserControl.DataTemplates>    <DataTemplate DataType="viewmodels:FooViewModel">      <Border Background="Red" CornerRadius="8">        <TextBox Text="{Binding Name}"/>      </Border>    </DataTemplate>  </UserControl.DataTemplates>  <UserControl.Styles>    <Style Selector="ContentControl.Red">      <Setter Property="Background" Value="Red"/>    </Style>  </UserControl.Styles><UserControl>

Важные изменения произошли и в биндингах. Авалония позволяет связывать между собой элементы разметки, прибегая только к свойствам XAML. Достаточно обратиться к источнику зависимости, используя # и имя элемента. Например, вот такой код привяжет значение поля other к значению поля source.

<TextBox Name="source"/><!-- Binds to the Text property of the "source" control --><TextBlock Name=other Text="{Binding #source.Text}"/>

Конструкция $parent позволяет обращаться к родительским компонентам.

<Border Tag="Hello World!">  <TextBlock Text="{Binding $parent.Tag}"/></Border>

Кстати, такое обращение поддерживает индексирование. Иначе говоря, конструкция $parent[1] позволит вам обратиться к родителю родителя вашего компонента. А конструкция $parent[0] эквивалентна $parent.

Помимо индексов здесь также можно использовать обращение по типу. $parent[Border] позволит вам обратиться к первому предку с типом Border. А еще такое обращение можно совместить с индексированием.

<Border Tag="Hello World!">  <Border>    <Decorator>      <TextBlock Text="{Binding $parent[Border;1].Tag}"/>    </Decorator>  </Border></Border>

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

Небольшие изменения также коснулись конвертеров. Один из самых популярных конвертеров, логическое отрицание, был реализован в виде синтаксиса в XAML коде.

<StackPanel>  <TextBox Name="input" IsEnabled="{Binding AllowInput}"/>  <TextBlock IsVisible="{Binding !AllowInput}">Sorry, no can do!</TextBlock></StackPanel>

Кстати, этот конвертер использует метод Convert.ToBoolean для преобразования значений, что позволяет писать код такого вида:

<Panel>  <ListBox Items="{Binding Items}"/>  <TextBlock IsVisible="{Binding !Items.Count}">No results found</TextBlock></Panel>

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

<Panel>  <ListBox Items="{Binding Items}" IsVisible="{Binding !!Items.Count}"/></Panel>

Я бы не рекомендовал использовать такой трюк, однако множественное применение конвертеров может пригодиться, когда в Авалонии реализуют синтаксическую поддержку для других конвертеров. Теоретически вы можете сделать это сами, но на практике это достаточно сложно и никак не документировано.

Помимо синтаксической поддержки, Авалония дает пользователю несколько дефолтных конвертеров. Для них не реализован никакой специфический XAML синтаксис, так что они используются как обычные конвертеры.

<TextBlock Text="{Binding MyText}" IsVisible="{Binding MyText, Converter={x:Static StringConverters.IsNotNullOrEmpty}}"/>

И последняя возможность, которую хочется упомянуть, относится к проблемам кроссплатформенности. Видели вот это меню в macOS?

Подобные элементы это вечная головная боль кроссплатформенных приложений. На маках такое меню есть, а на винде нет. А в Linux есть, но не везде, только в некоторых оконных менеджерах.

Для работы с такими меню в девятой версии Авалонии реализовали компонент NativeMenu. По умолчанию, даже без добавления NativeMenu в приложение, Авалония будет генерировать меню с базовыми пунктами как раз они и представлены на скриншоте. А для добавления новых пунктов как раз и используется NativeMenu.

<Application>  <NativeMenu.Menu>    <NativeMenu>      <NativeMenuItem Header="About MyApp" Command="{Binding AboutCommand}" />    </NativeMenu>  </NativeMenu.Menu></Application>

Декларативный UI via F#

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

Сообщество Авалонии разработало отличную библиотеку Avalonia.FuncUI, позволяющую вам писать UI на Авалонии в декларативном стиле. Получающийся код напоминает реализацию UI с помощью Elm или Jetpack Compose.

module Counter =    type CounterState = {    count : int  }  let init = {    count = 0  }  type Msg =  | Increment  | Decrement      let update (msg: Msg) (state: CounterState) : CounterState =   match msg with    | Increment -> { state with count =  state.count + 1 }    | Decrement -> { state with count =  state.count - 1 }  let view (state: CounterState) (dispatch): IView =    DockPanel.create [      DockPanel.children [        Button.create [          Button.onClick (fun _ -> dispatch Increment)          Button.content "click to increment"        ]        Button.create [          Button.onClick (fun _ -> dispatch Decrement)          Button.content "click to decrement"         ]        TextBlock.create [          TextBlock.dock Dock.Top          TextBlock.text (sprintf "the count is %i" state.count)        ]      ]    ]

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

Недостатки

Перейдем к самому интересному а что же не так с Авалонией в сравнении с WPF?

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

Другая проблема это экосистема. Так как Авалония куда моложе WPF и имеет значительно меньшую аудиторию, вокруг Авалонии пока не выстроилась богатая экосистема со множеством стилей и кастомных контролов. Да и гигантских платных UI фреймворков вроде DevExpress пока не появилось. Аналогичные сложности и с инструментами разработки они есть, и поддержка становится все лучше, однако пока что отстает по качеству от WPF.

Ну и самая заметная проблема, о которой говорят многие это отсутствие гарантий. Авалония это инструмент, создаваемый исключительно сообществом, что не слишком-то привычно для .NET разработчиков. За ним не стоит Microsoft или какая-то другая крупная компания. В этих условиях многим не хочется рисковать, надеясь на open source продукт кто знает, вдруг завтра мейнтейнер потеряет интерес, и разработка встанет? Однако это же дает вам возможность заметно влиять на развитие Авалонии, исправляя существующие проблемы и предлагая новые фичи.

Заключение

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

Компенсация за это типичный набор проблем молодого опенсорсного инструмента. Кое-какие фичи не реализованы, экосистема бедновата, да и качественная поддержка в будущем не гарантирована. Так или иначе, это несет и преимущества в виде возможности заметно влиять на будущее Авалонии.

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

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

Источник: habr.com
К списку статей
Опубликовано: 16.02.2021 12:20:24
0

Сейчас читают

Комментариев (0)
Имя
Электронная почта

Блог компании контур

Программирование

Net

Разработка под macos

Разработка под windows

Avalonia

Wpf

Desktop

Cross-platform

Avaloniaui

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru