Avalonia — кроссплатформенный .NET UI-тулкит с открытым исходным кодом, вдохновлённый технологиями WPF и UWP. Он полностью поддерживает Windows, macOS и Linux, .NET Core 2.0-3.1, XAML, дата-биндинги, lookless-контролы и многое другое.
(на ВДПВ показана работа без XOrg)
Версия 0.9 стала большим обновлением с набором давно ожидаемых фич: компилируемый XAML, поддержка глобальных меню, возможность плавной прокрутки виртуализированных списков с элементами произвольного размера, поддержкой сенсорного ввода и ещё кое-чем.
За подробностями прошу под кат.
Компилируемый XAML
В прошлых версиях парсинг XAML происходил во время выполнения приложения. Этот подход имел ряд недостатков:
- медленно
- программа компилируется без ошибок, а потом не работает (QML, я смотрю на тебя)
- используемый парсер выдавал невразумительные ошибки типа "The value of 'Content' property is null" при опечатке в названии контрола.
Наш новый компилятор всё парсит во время компиляции и перегоняет прямо в MSIL. Можно даже посмотреть на хелловорлд декомпилятором и увидеть что-то типа этого:
Декомпилятор, кстати, тоже работает на авалонии.
За счёт этого получилось выкинуть уйму обращений к рефлексии, за счёт чего запуск приложения стал турбореактивным. Только наш каталог контролов после перехода на компилятор стал запускаться на 40% быстрее.
Когда проверки во время компиляции не помогли (например, что-то развалилось в конвертере), вы можете смотреть на нормальные стектрейсы ошибок с указанием строки в XAML-файле. Ну и вообще походить отладчиком по XAML-у, если интересно:
Глобальные меню в macOS и Linux
На маках приложения меню внутри окна обычно не рисуют, поскольку наверху экрана есть глобальная строка меню для всех. На ряде десктопных окружений Linux (Unity, KDE с поставленной галкой в настройках, vala-panel-appmenu) так же доступна схожая возможность.
В Avalonia появилась поддержка таких меню, в коде выглядят они примерно так:
<Window>
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="File">
<NativeMenuItem.Menu>
<NativeMenu>
<NativeMenuItem Header="Open" Command="{Binding OpenCommand}"/>
<NativeMenuItemSeperator/>
<NativeMenuItem Header="Recent">
<NativeMenuItem.Menu>
<NativeMenu/>
</NativeMenuItem.Menu>
</NativeMenuItem>
<NativeMenuItemSeperator/>
<NativeMenuItem Header="Quit Avalonia" Command="{Binding CloseCommand}" Gesture="CMD+Q"/>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem>
<NativeMenuItem Header="Edit">
<NativeMenuItem.Menu>
<NativeMenu>
<NativeMenuItem Header="Copy"/>
<NativeMenuItem Header="Paste"/>
</NativeMenu>
</NativeMenuItem.Menu>
</NativeMenuItem>
</NativeMenu>
</NativeMenu.Menu>
</Window>
Не пугайтесь CMD+Q
, оно на не-маке смаппится на `Control+Q. Это теперь такой синоним для платформозависимого модификатора "Command".
Чтобы людям не заниматься копипастой такой же структуры в "обычное" меню для "традиционных" платформ мы так же завезли контрол <NativeMenuBar/>
, который можно положить туда, где это самое меню должно быть. Ну а контрол уже сам заберёт все этим NativeMenu и сам разберётся, надо ли его рисовать или система справится.
меню окна в глобальном баре на макоси
Так же на macOS есть особенность — у приложения есть пункт меню с именем приложения. Там обычно находятся элементы, глобальные для всего приложения типа настроек. Мы, чтобы пользователи не удивлялись, это меню генерируем по-умолчанию:
дефолтное содержание меню приложения
Ну а кастомизировать его надо в вашем App.xaml:
<Application>
<NativeMenu.Menu>
<NativeMenu>
<NativeMenuItem Header="About MyApp" Command="{Binding AboutCommand}" />
</NativeMenu>
</NativeMenu.Menu>
</Application>
Соответственно, у Application
теперь есть свой собственный DataContext
, в который можно положить вьюмодель. Ну чтобы было куда биндиться.
На Linux, ввиду его общей, кхм, разнородности, данная опция по-умолчанию выключена и может быть включена посредством:
.With(new X11PlatformOptions
{
UseDBusMenu = true
})
Я лично тестировал на Unity и в KDE на Ubuntu 18.04, но кто ж его знает, как оно в других дистрах.
Managed-реализации файловых диалогов
Если по каким-то причинам не хочется использовать нативные диалоги (например, не хочется тащить в embedded-систему цельнотянутый GTK), теперь можно воспользоваться реализованными на самой авалонии:
Весь API тот же самый, просто надо включить в AppBuilder-е:
AppBuilder.Configure<App>()
.UsePlatformDetect()
.UseManagedSystemDialogs();
Если хочется положить диалоги в кастомное окно (рамочку там свою нарисовать, например, цвета застилизовать), то делается это так:
.UseManagedSystemDialogs<AppBuilder, MyCustomWindowType>();
ItemsRepeater
Из UWP был портирован ItemsRepeater
. Данный контрол позволяет делать высокопроизводительные виртуализированные списки с элементами разных размеров и плавной прокруткой. Layout при этом контролируется отдельно от контрола посредством системы Attached Layouts.
В дальнейшем мы планируем перевести на него вообще все списочные контролы, ну а пока можно брать и пользоваться в своих приложениях там, где оно вам нужно.
Импорт Grid and GridSplitter из WPF
Продолжаем портировать контролы из теперь-уже-почти-скоро-может-быть-целиком-совсем-совсем-опенсорсного WPF. Из уже опенсорсных его частей.
В этот раз перетащили такую фундаментальную штуку как Grid, вместе со всеми его SharedSizeGroup-ами и GridSplitter-ом. Так что теперь тот код, что работал в WPF будет работать в авалонии так же, а не "вообще так же, но ...".
Оптимизации производительности
Над ними произведена большая работа. В основном по выкорчёвыванию LINQ отовсюду и ненужных аллокаций из самых неожиданных мест. Так что на Raspberry Pi у нас теперь анимации выдают FPS, а не SPF.
KMS/DRM/GBM/libinput на линуксе
То, что показано на видео перед катом. Мы теперь умеем использовать ту же самую инфраструктуру, на базе которой работают новые более лучшие дисплейные менеджеры, которые понемногу приходят на замену Xorg. Что позволяет нам работать без дисплейного менеджера вообще, прямо поверх линуксового ядра да ещё и с аппаратным ускорением через OpenGL.
Эта функция даёт вам возможность делать системы-"киоски", в которых запущен только ядро Linux и ваше приложение.
Поддержка сенсорного ввода
Пока что выключена по-умолчанию, но при наличии сенсорного экрана может быть включена через добавление следующего кода в ваш AppBuilder:
.With(new X11PlatformOptions
{
EnableMultiTouch = true
})
.With(new Win32PlatformOptions
{
EnableMultitouch = true
})
Если раньше сенсорный ввод обрабатывался как эмулируемая операционной системой мышь, то теперь мы перешли на UWPшную модель pointer events с отдельным "указателем" на каждый контакт с сенсорной поверхностью. Так же завезли базовую поддержку распознавателей жестов, на которой сделали поддержку скроллинга пальцем.
Весенние видео с первыми демонстрациями:
Production Ready
У нас постоянно спрашивают "а пригодно ли для продакшна?", "а когда 1.0?". Да, пригодно. 1.0 будет, согласно модели SemVer, когда перестанем каждую версию по чуть-чуть менять API (ну или можем перейти на браузерную модель версионирования и через пару лет будет "состоялся релиз AvaloniaUI 71").
В настоящий момент есть несколько приложений на AvaloniaUI с большими пользовательскими базами. Мы знаем о ряде коммерческих приложений, портируемых на AvaloniaUI. Командам всех этих проектов мы хотим сказать спасибо за неоценимую помощь в тестировании этого релиза.
Ввиду подобного активного использования, мы переходим на новую модель поддержки релизов. Ветка 0.9 будет поддерживаться багфиксами и бекпортированием критических изменений вплоть до выхода следующей версии.
Так же со следующего года будет доступна техническая поддержка на коммерческой основе. Это не потому, что мы жадные, это потому, что на энтузиазме и "в свободное после работы время" далеко не уедешь и нужны разработчики на full-time. Условия пока вырабатываются, что если вам интересно или есть что-то срочное, то пишите на team@avaloniaui.net
Как начать пользоваться
Есть достаточно подробный туториал на английском. Для знакомых с WPF/UWP разработчиков всё должно быть интуитивно и просто, есть перечень наиболее важных отличий.
Благодарности
Этот релиз стал возможен благодаря работе многих людей, в частности grokys, jkoritzinsky, kekekeks, danwalmsley, jmacato, marchingcube, wieslawsoltes, gillibald и многих других.
Лучший способ поддержать Avalonia — принять участие в разработке: реализовать фичу, починить баг или помочь в тестировании. См. страницу Contributing
Автор: Никита Цуканов