Pull to refresh

Релиз первой беты кросс-платформенного XAML UI-тулкита Avalonia

Reading time 6 min
Views 22K

Состоялся релиз первой бета-версии Авалонии.


Avalonia — кроссплатформенный .NET UI-тулкит, вдохновлённый технологиями WPF и распространяемый под лицензией MIT. Он полностью поддерживает .NET Core 2.0, XAML, дата-биндинги, lookless-контролы и многое другое. Avalonia позволяет писать на C# приложения под Windows, Linux и Mac OS X. Возможность запуска на iOS и Android находится в экспериментальном состоянии.



Данная версия стабильна и имеет полное право называться бетой: тулкит не разваливается в руках и имеет приличный базовый набор контролов (см. видео). На нём создано два достаточно больших приложения с открытым исходным кодом: AvalonStudio — кроссплатформенная IDE для разработки на C# и C++ и Core2D — редактор 2D-схем и диаграмм.


Самый простой способ взять и начать пользоваться — установить наше расширение для Visual Studio или же воспользоваться шаблонами для dotnet new. Для ознакомления доступны примеры в основном репозитории.


В процессе подготовки релиза произошёл ряд важных изменений:


  • Переход на платформу .NET Standard 2.0
  • Старый медленный XAML-парсер заменён на Portable.Xaml, что позволило ускорить запуск AvalonStudio с 25 до 7 секунд (на SSD или на HDD с "горячим" файловым кэшем).
  • Удалены старые и несовместимые с .NET Core бэкенды Cairo и gtk-sharp. Вместо них теперь используется Skia (библиотека отрисовки 2D из состава Google Chrome) и собственные биндинги к GTK3.
  • Вместо форка ReactiveUI 8 перешли на официальную версию, поскольку она стала поддерживать .NET Standard.
  • Запущен сайт avaloniaui.net, на котором доступна документация. В будущем там будут добавлены статьи.

Основные возможности в релизе


Retained-режим отрисовки в отдельном потоке


Раньше окно полностью перерисовывалось при каждом изменении. Этот подход прост и надежен, но крайне неэффективен. Вместо него был реализован DeferredRenderer, который преобразует вызовы нашего API отрисовки в граф сцены окна. Отдельный поток ищет в графе измененные участки и перерисовывает только их. Помимо снятия нагрузки с UI-потока это позволяет добавлять оптимизации, выделяя части окна в кешируемые слои.


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


Если у вас возникают проблемы при использовании новой инфраструктуры, старая реализация ImmediateRenderer оставлена для совместимости.


MonoMac-бакэнд для OS X


Поскольку GTK2/GTK3 — тяжелые библиотеки (~60MB), а использовались только ради обертки над нативным Carbon API, мы реализовали Cocoa-бэкенд с помощью специально собранного под .NET Standard MonoMac. Это упростило сборку бандлов и позволило использовать родные для OS X файловые диалоги вместо "универсальных", предоставляемых GTK.


Синтаксический сахар для RelativeSource


Ранее, как и в WPF, привязки к другим контролам осуществлялись через RelativeSource. Этот синтаксис очень многословен: например, привязка текста на свойство Tag родительского элемента выглядит примерно так:


<Border Tag="Hello World">
  <TextBlock Text="{Binding Tag, RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type Border} AncestorLevel=1}}"/>
</Border>

Новый синтаксис позволяет переписать это вот так:


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

Помимо ключевого слова $parent добавлены следующие возможности:


Сокращение Полная форма
$self Mode = Self
$parent Mode = FindAncestor; AncestorLevel = 1
$parent[Level] Mode = FindAncestor; AncestorLevel = Level +1
$parent[ns:Type] Mode = FindAncestor; AncestorType = ns:Type
$parent[ns:Type; Level] Mode = FindAncestor; AncestorType = ns:Type; AncestorLevel = Level + 1

*ns:Type — неймспейс: Тип, например Borderили local:MyControl


Drawing


Drawing — удобное представление для векторной графики типа иконок, используемое в WPF. Библиотека изображений Visual Studio предоставляет сотни иконок в данном формате. Их использование улучшает производительность, поскольку все части изображения являются одним элементом visual tree.


Теперь в Avalonia появилась поддержка данного формата. Наш стандартный контрол Image пока не умеет с ними работать, но вы уже можете использовать DrawingPresenter.


StaticResource и DynamicResource


В новой версии добавлены привычные по WPF/UWP Control.Resources, StaticResource и DynamicResource.


Реализация в точности соответствует аналогичному функционалу в WPF/UWP. Раннее все ресурсы были привязаны к стилям и для доступа к ним предлагалось использовать {StyleResource}. Теперь у каждого элемента управления есть свой словарь ресурсов, которые наследуются по дереву элементов. Расширение разметки {StyleResource} было удалено: теперь вместо него следует использовать {DynamicResource}.


Привязка команд к методам


Думаю, у всех есть в проекте реализация ICommand, принимающая в конструкторе делегат. Чтобы не плодить лишние сущности, привязка напрямую к методам вью-модели добавлена в сам фреймворк.


public class ViewModel
{
    public void ButtonClicked()
    {
        Console.WriteLine("Hooray!");
    }
}

<Button Command="{Binding ButtonClicked}"/>

Календарик


Был утащен из Silverlight Toolkit. Теперь можно выбирать даты.


Интеграция с WPF


В новой версии компоненты Авалонии можно полностью бесшовно встраивать в приложения на WPF. Это стало возможным за счет того, что системы layout-а в Авалонии и WPF практически идентичны, а Direct2D с помощью чёрной магии может рендерить прямо в заточенный под Direct3D 9 D3DBitmap. Демку можно посмотреть тут.


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


Новая инфраструктура для превьювера с поддержкой .NET Core


Предыдущая версия предварительного просмотра для студии была написана за три дня со срезанием всех возможных углов. В результате она оказалась привязана к Win32 API, полному .NET Framework и Windows Forms. Теперь вместо неё была придумана более умная система, с общением по TCP/IP и возможностью передачи битмапов вместо прямой итеграции окна в Visual Studio. Это позволило нормально предпросматривать XAML в проектах под .NET Core и, что более важно, открыло дорогу для поддержки в других IDE. В частности, поддержка уже добавлена в AvalonStudio. Ведутся работы над плагином к Visual Studio Code, но это сложно: эта IDE построена на передовых и прогрессивных веб-технологиях и требует отдельный language-server, обвязку вокруг MSBuild и передачу картинок через WebSocket.


Помимо превьювера инфраструктура "удалённых" виджетов может пригодиться ещё где-нибудь, поэтому классы RemoteServer и RemoteWidget доступны для использования.


Иные улучшения


Полный список можно посмотреть тут, а ниже представлены самые интересные пункты:


  • #894 Кнопки выключаются, если привязка свойства Command вернула null
  • #1085 Добавлен FindAncestor
  • #1086 ReactiveUI обновлён до восьмой версии
  • #1128 У кнопки появилось свойство IsPressed
  • #1133 Для всплывающей подсказки реализованы IsOpen, Placement, Offset
  • #1145 Переехали на .NET Standard 2.0
  • #1146 Для окна реализовано свойство ShowTaskbarIcon
  • #1150 Реализовано API доступа к информации об экранах
  • #1174 СожглиУбрали поддержку GTK2 and Cairo support
  • #1175 Реализованны Orientation и IsIndeterminate у индикатора прогресса
  • #1253 Для PageSlide-анимации реализована вертикальная ориентация
  • #1265 Реализована поддержка трёх состояний для ToggleButton, CheckBox и RadioButton (серенькое ни-да-ни-нет)
  • #1273 Облегчили настройку логирования при настройке AppBuilder

Изменения, ломающие совместимость


Хорошая новость: мы их теперь отслеживаем и иногда даже документируем.
Плохая новость: они есть.


BuildAvaloniaApp для превьювера.


Для работы новому превьюеру необходимо получить информацию у самого приложения. Для этого в классе с точкой входа (обычно Program.cs прямо рядом с Main) должен быть реализован метод BuildAvaloniaApp, примерно такой:


static void Main(string[] args)
{
    BuildAvaloniaApp().Start<MainWindow>();
}

public static AppBuilder BuildAvaloniaApp() =>
    AppBuilder.Configure<App>()
              .UsePlatformDetect()
              .LogToDebug();

Без него предварительный просмотр работать не будет. Такие дела.


DataContextChanging и DataContextChanged


Заменены на OnDataContextBeginUpdate and OnDataContextEndUpdate.


Расширения разметки Static и Type


Заменены на x:Static и x:Type, нужно добавить пространство xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" в корневой элемент XAML-файла. Ибо стандартизация.


StyleResource


StyleResource заменен на StaticResource и DynamicResource, как во всех нормальных XAML-фреймворках. StaticResource и DynamicResource ищут в Control.Resources и Style.Resources.


Mouse device


Убрали одну из глобальных переменных — MouseDevice. Теперь мышь доступна на верхнем уровне: вызывайте GetVisualRoot и приводите результат к IInputRoot. Всё потому, что глобальные переменные и service locator — зло.


var pos = (_control.GetVisualRoot() as IInputRoot)?.MouseDevice?.Position ?? default(Point);

Как начать пользоваться?


Скачать дополнение к Студии и создать проект из шаблона. Полноценной документации пока нет, поэтому нужен опыт работы с WPF или UWP.

Tags:
Hubs:
+94
Comments 77
Comments Comments 77

Articles