Состоялся релиз версии 0.5 кросслплатформенного XAML UI фреймворка AvaloniaUI (раннее назывался Perspex). Фреймворк сделан по тем же принципам, что и WPF/UWP, т. е. используется XAML, биндинги и шаблонизированные элементы управления. На текущий момент это единственный способ сделать UI на настоящем XAML, который будет работать на Windows, OS X и Linux (так же имеется экспериментальная поддержка iOS и Android).
> Каталог встроенных контролов (gif 3MB)
Начать работать с фреймворком можно скачав дополнение для Visual Studio 2017 и создав проект из шаблона. Так же стоит ознакомиться с документацией на wiki.
В этом релизе: Поддержка .NET Core, переход на GTK3 для *nix-систем, поддержка вывода через Linux fbdev, система расширений, исправлено множество ошибок.
.NET Core
.NET Core поддерживается в качестве цели для сборки (для поддержки превьювера в Visual Studio всё ещё необходимо добавлять net461 в TargetFrameworks) и работает на всех трёх десктопных платформах. Так же произведён переход на netstandard1.1
для неплатформоспецифичных библиотек и на netstandard1.3
для бэкэндов Win32, GTK3 и Skia. Шаблоны для dotnet new брать здесь.
GTK3 на *nix-платформах
Мы больше не используем биндинги из состава GTK#, которые требуют сборки нативных бинарников для каждой платформы и привязаны к Mono. Вместо этого взаимодействие с GTK происходит напрямую через P/Invoke, что дало возможность сделать бэкэнд совместимым с netstandard1.3
. В качестве бонуса на *nix-системах заработала плавная прокрутка. Если требуется поддержка дистрибутивов без GTK3, всё ещё можно переключиться на старый GTK2-бэкэнд.
На OS X требуется установить GTK3 через brew install gtk+3
. В дальнейшем эта зависимость на OSX будет устранена, т. к. удалось завести MonoMac поверх netstandard2.0
Улучшения в поддержке мобильных платформ
Мы больше не пытаемся эмулировать наличие десктопных окон там, где их на самом деле нет. Поэтому на мобильных платформах больше не доступно использование класса Window. Вместо этого поставляются нативные для каждой платформы классы AvaloniaView
со свойством Content
, в котором и следует размещать XAML-контролы. Для удобства так же предоставляются AvaloniaActivity
для Android и AvaloniaWindow
(UIWindow
с встроенным контроллером) для iOS. Таким образом инициализация теперь выглядит примерно так:
public override bool FinishedLaunching(UIApplication uiapp, NSDictionary options)
{
AppBuilder.Configure<App>()
.UseiOS()
.UseSkia().SetupWithoutStarting();
Window = new AvaloniaWindow() {Content = new YOUR_CONTROL_HERE(){DataContext = ...}};
Window.MakeKeyAndVisible();
return true;
}
public class MainActivity : AvaloniaActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
if (Avalonia.Application.Current == null)
{
AppBuilder.Configure(new App())
.UseAndroid()
.SetupWithoutStarting();
}
Content = YOUR_CONTROL_HERE(){DataContext = ...};
base.OnCreate(savedInstanceState);
}
}
Linux fbdev
Добавлена начальная поддержка для вывода через fbdev и ввода через libevdev2. Это позволит в дальнейшем использовать фреймворк на встраиваемых Linux-устройствах без X-сервера.
Встройка в WPF/Winforms/GTK2
Теперь поставляются родные для этих платформ контролы, которые могут отображать контролы AvaloniaUI. Устроены по аналогии с мобильными платформами.
Система расширяемости
Улучшенный авто-детект платформ с системой приоритетов. Пока работает только на полном .NET. Так же в комплекте средства для авторегистрации разного рода зависимостей для сторонних библиотек. См. пул-реквест с описанием.
Оператор ^ в биндингах
Ранее система пыталась автоматически подписываться на все IObservable
и Task
, которые видела в цепочке свойств. Это приводило к проблемам при попытке биндинга к свойствам классов, реализующих IObservable. Чтобы избежать неоднозначности был введён оператор ^. Пользоваться им можно так:
<!-- Bind to the values produced by Content.Observable -->
<TextBlock Text="{Binding Content.Observable^}"/>
<!-- Bind to the Name property on the values produced by Content.Observable -->
<TextBlock Text="{Binding Content.Observable^.Name}"/>
Оператор поддерживает расширяемость, нужно реализовать IStreamPlugin
3rd-party библиотеки
Со времени предыдущего релиза были портированы и в какой-то степени работают:
Библиотеки пока не обновлены для поддержки последней версии, ибо релиз опубликован на nuget только позавчера.
Исправлено множество ошибок
Закрыто 133 issue/PR на гитхабе. Не все из них были ошибками. Полный список можно посмотреть тут.
Мы всегда рады контрьбьюторам. Если вы считаете, что можете помочь или у вас просто есть вопрос, стучитесь в наш чат в gitter.
Комментарии (38)
SonicGD
15.05.2017 13:23+2Проект выглядит довольно интересно. А какие у вас мысли насчёт XAML Standard?
mayorovp
15.05.2017 14:10+1Насколько сложно прикрутить туда альтернативный механизм биндингов? Моя попытка покопаться в дебрях биндингов WPF закончилась кучей внутренних методов которые непонятно что делают...
В первую очередь интересует механизм подписки на изменения когда операция подписки выполняется не вызывающим кодом, а вызываемым.
kekekeks
15.05.2017 14:17+1Наша встроенная система работает на базе ReactiveExtensions. Альтернативный механизм биндингов прикручивается примерно так же как и к WPF-написанием этого самого механизма и реализацией соответствующего MarkupExtension.
Что именно имеется ввиду под "подпиской на изменения вызываемым кодом"?
mayorovp
15.05.2017 14:30Механизм, при котором вызывающий код устанавливает в статическом свойстве обработчик, который будет запомнен теми объектами, к которым он обращается и вызван при изменении любой зависимости. Еще называется "динамическое определение зависимостей".
Для биндинга он, возможно, несколько избыточен — но реализовывать вью-модель с ним довольно удобно.
Krypt
16.05.2017 12:31В стандартной разметке это вполне можно сделать через Dependity Property, на который забинден объект. Другой вопрос, что этот код не отличается вменяемостью (место, в котором я ДЕЙСТВИТЕЛЬНО жалею об отсутствии препроцессора в C#)
mayorovp
16.05.2017 12:40Нельзя ли по-подробнее? Я не вполне понимаю что такое "Dependity Property, на который забинден объект".
Krypt
16.05.2017 13:17— Где-нибудь как-нибудь устанавливаете DataContext элемента управления на экземпляр класса (пусть будет A)
— Добавляете DependencyProperty «Prop» типа B в класс A вот так:
public static readonly DependencyProperty PropProperty = DependencyProperty.Register("Prop", typeof(<B>), typeof(ActionPanel), new FrameworkPropertyMetadata(null, (DependencyObject d, DependencyPropertyChangedEventArgs e) => { // Вот тут ваш callback })); public B Prop { set { SetValue(PropProperty, value); } get { return (B)GetValue(PropProperty); } }
— Биндите свойство в Xaml как вам нужно (если свойство меняется из интерфейса — скорее всего понадобится TwoWay binding) ( <local:A Prop="{Binding}" />)
P.S.: Да, я знаю, что код ужасен. Именно поэтому я жалею об отсутствии препроцессора: он помог бы обернуть бойлерплэйт во что-нибудь вменяемое.mayorovp
16.05.2017 13:31И какое это имеет отношение к тому, что я писал?
Как это решение обобщить на случай произвольного числа зависимостей? Например, как вы предлагаете "забиндиться" на сумму элементов коллекции?
Krypt
16.05.2017 13:45Всё, понял. Что неправильно понял. Можете подробнее описать, что бы хотите сделать? Вы имеете ввиду Dependency Injection?
Код, что я привёл, по сути кастомный сеттер свойства. А-ля public B Prop { set; get; }, но с поддержкой XAML, на случай, если вы хотите мгновенно реагировать на изменения в интерфейсе (например, обновлять по мере ввода текста результаты поиска)
mayorovp
16.05.2017 13:45Кажется, я понял что объяснил слишком непонятно. Попробую подробнее. //cc: Krypt
Вот у меня есть библиотека, которая умеет делать вот так (писал ее под впечатлением от MobX):
private IEnumerable<Foo> _foos; public IEnumerable<Foo> Foos { get { return GetProperty(_foos); } set { SetProperty(ref _foos, value); } } public double Avg => Computed(() => { return Foos.Average(s => s.X); });
Свойство здесь Avg будет пересчитываться при изменении коллекции Foo или изменении свойства X у любого объекта, который входит в эту коллекцию — при условии что коллекция и объект написаны аналогичным способом.
Интересует возможность прикрутить ее куда-нибудь напрямую без реализации INPC (реализовать INPC нетрудно — но при этом теряется ленивость). Для того, чтобы это сделать, нужна возможность оборачивать код биндинга в вызов функции из моей библиотеки.
Krypt
16.05.2017 15:03Для использования в качестве элементов списка вы можете использовать такой код:
List.ItemsSource = Foos
Если вы хотите обновлять элементы из кода, то обновляемые поля должны быть INotifyPropertyChanged или ObservableCollection. Либо дёргать обновление вручную из кода, что наверняка возможно, но я не знаю как. Ибо никогда не использовал.
Но, впрочем, стоит уточнить, что C#/WPF не являются моей основной платформой уже лет 5 как. Так что я не последняя инстанция.
mayorovp
16.05.2017 15:25Эти INotifyPropertyChanged или ObservableCollection — это, по сути, и есть дерганье обновлений из кода вручную. Которое надоело.
Don_Eric
15.05.2017 17:04+1интересный проект, но боюсь что будущее за Xamarin.Forms
уже сейчас поддерживаются Mac, UWP, Android и iOSkekekeks
15.05.2017 17:41+1Авалония и формы — вещи ортогональные. В том плане, что у нас подход "рисуем всё сами и одинаково" (минус — гуй выглядит не "нативно", частично решается мимикрией через темы), а forms оборачивает нативные контролы (минусы — удачи с вёрсткой макета от дизайнера и весельем с реализацией хоть чего-то сложного или нестандартного для каждой из платформ в отдельности).
Каждый из подходов хорош для своих применений. Так что тут скорее будет интеграция одного с другим, а не наоборот. Причём на Android/iOS уже сейчас есть принципиальная возможность встраивать куски интерфейса на авалонии в forms, нужно только рендереры соответствующие написать. На досуге надо будет заняться
Don_Eric
16.05.2017 13:12да, эта разница принципиальная.
а насколько производительность рисования самим отличается от WebView/Browser и электрона?kekekeks
16.05.2017 13:54С браузером не сравнивали, но уже сейчас быстрее чем WPF на Windows на определённых сценариях (авалония поверх Direct2D начинается с 1:30).
И такой немаловажный момент. У нас (как во всех нормальных UI-тулкитах) есть виртуализация списков. В браузеры её не завезли и приходится изобретать криво работающие костыли. Соответственно на задачах "показать список на несколько тысяч элементов" работать будет шустрее, а памяти кушать на порядки меньше.
Skykharkov
15.05.2017 18:35+1Смотрится неплохо. Попытаюсь поюзать для одного некоммерческого проекта.
Первый глюк. Что-то не так с canvas для button.
Skykharkov
15.05.2017 18:41+1<Window xmlns="https://github.com/avaloniaui" Title="Test app" WindowState="Maximized" MinWidth="500" MinHeight="300"> <Grid Name="MainGrid"> <Button Content="Button text" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="40,40,0,0" Width="201" Height="65"/> </Grid> </Window>
Без Alignment работает как ожидается. Судя по всему канва отрисовки текста не понимает Aligment и считает что их нет.
kekekeks
15.05.2017 19:06+1Что-то с Layout-ом перемудрили. Если сделать вот так, то работает.
<Window xmlns="https://github.com/avaloniaui" Title="Test app" WindowState="Maximized" MinWidth="500" MinHeight="300"> <Grid Name="MainGrid"> <Button Content="Button text" VerticalAlignment="Top" HorizontalAlignment="Left" Margin="40,65,0,0" > <Border Width="201" Height="65"> <TextBlock HorizontalAlignment="Center" VerticalAlignment="Center">Button Text</TextBlock> </Border> </Button> </Grid> </Window>
Пойму, что там именно происходит и заведу issue.
Skykharkov
15.05.2017 19:13+1Да, работает. Спасибо. Чуть больше кода, но работает. В UWP моя разметка работает корректно.
kekekeks
15.05.2017 19:16+1Да, это явный баг с layout-ом у нас.
kekekeks
15.05.2017 19:54#984. Когда определимся с как правильно и смержим — будет доступно в nuget-фиде с ночными сборками.
Skykharkov
15.05.2017 20:03Спасибо. Еще вопрос — CEFGlue компилит Chromium с поддержкой MP3\4? Или все равно приходится компилить Chromium с proprientary_codecs=true?
Varim
15.05.2017 20:11между прочим, мне или приснилось или MP3 больше не требует лицензию с 23 апреля 2017 в вики так написано
Skykharkov
15.05.2017 20:16Это надо гуглу сказать. :) Хотя возможно вы и правы. Около трех недель назад, когда я собирал Chromium "крайний раз"
(Заголовок спойлерада я в курсе, про "крайний", но избавиться не могу
Skykharkov
15.05.2017 18:56Насколько плохо (в какой-то степени :) работает CEFGlue? В текущем проекте в WinForms версии использую CefSharp3. Понемногу переношу проект в .NET core и браузер — критичный элемент.
kekekeks
15.05.2017 19:09Я оригинальный CEFGlue использовал в WinForms и в целом был доволен. Порт для авалонии, насколько я знаю, делался по-возможности построчным копированием с WPF. Имеет смысл попробовать его погонять и попинать автора порта, если есть какие-то проблемы. Ну и следует понимать, что сейчас это не коробочное решение, а в какой-то мере конструктор "собери сам". Порт стал возможен только недавно после появления инфраструктуры WritableBitmap.
Skykharkov
15.05.2017 19:15Будем пробовать. Проект выглядит интересно. Спасибо большое. Теперь буду в issue писать. :) Ваш подход выглядит изящно и жизнеспособно (на первый взгляд ;).
QtRoS
15.05.2017 22:341. А с темами как обстоят дела? Внешний вид кастомизируется? Встроенные системные темы не подхватываются?
2. Какие сценарии использования видите лично Вы? Т.е. если бы это был бы платный продукт, какая бы у вас была «бизнес модель» и «целевой потребитель» (кому бы продавали)?kekekeks
15.05.2017 22:45+11) Есть штатная тема, которую можно заменить на свою и в которой можно заменить цвета. Мимикрию под конкретные ОС пока не делали и наврятли будем делать до версии 1.0, если, конечно, не найдётся желающих этим делом заняться.
2) В качестве платного продукта оно бы неплохо зашло на всякого рода встраиваемых устройствах (Avalonia+Linux кушают порядка 100 мегабайт (x86, fbdev, наш каталог контролов в качестве объекта замера) против полугигабайтного аппетита Windows for IoT), а так же в качестве UI для игр. Там наблюдается некоторый голод в плане хорошего UI, так что можно было бы продавать за хорошую цену.
MonkAlex
Грац.
Пробовал простые формы накидывать, получается довольно удобно.
Есть какие то вещи, которые ещё явно нужны и будут в ближайшем релизе?
kekekeks
Смотря для чего нужны. На десктопе сейчас разве что API для работы со шрифтами не хватает.
У нас пока в планах на 0.6 доделать deferred-рендеринг (который в отдельном потоке), переехать на Portable.Xaml (OmniXAML тормозной очень и хуже поддерживается), сделать нормальный превьювер для *nix-платформ и воткнуть его хотя бы в MonoDevelop, сделать уже наконец генерацию линуксовых пакетов и бандлов для OSX в один клик, сделать бакэнд на базе MonoMac.