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

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


Avalonia — кроссплатформенный .NET UI-тулкит, вдохновлённый технологиями WPF, с полной поддержкой .NET Core 2.0, XAML, дата-биндиногов, lookless-контролов и многого другого, поставляемый под лицензией MIT. 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, на котором в дальнейшем планируется размещать статьи и документацию. Поиск и просмотр xml-документации доступен уже сейчас.


Основные фичи в релизе


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


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


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


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


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


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


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


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



  


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



  


Помимо $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


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


StaticResource и DynamicResource


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


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


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


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


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



Календарик


Утащен в #1244 из Silverlight. Теперь можно брать и выбирать даты.


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


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


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


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


Предыдущая версия превьювера для студии была, по сути, написана за три дня со срезанием всех возможных и невозможных углов. В результате вышла она крайне непортабельной, с привязкой к Win32 API, полному дотнету и 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). Выглядит это примерно так:


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

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


Если этого не сделать, то превьювер работать не будет. Такие дела.


DataContextChanging и DataContextChanged


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


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


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


StyleResource


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


Mouse device


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


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


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


Скачать дополнение к студии и создать проект из шаблона. Ввиду отсутствия нормальной документации будет необходим опыт работы с WPF/UWP.

© Habrahabr.ru