[Avalonia] 练习一:搭建Avalonia+Prism+Material项目
这是个练手项目,初次接触Avalonia,决定搭建一个架构由Prism实现,UI基于Material,演示基本控件、自定义控件、遮罩、弹窗、标签页等UI基础的桌面项目,实现这些UI基础,基于WPF转Avalonia应该就没太大的问题了。
一、新建项目。
(推荐编辑器:JetBrains Rider,智能提示更友好)
- 新建空白解决方案AvaloniaApps。
- 新建项目,选择Avalonia .NET MVVM App (AvaloniaUl),下一步。
- 在创建之前勾选“Remove View Locator”,使用Prism的View Locator就可以了,同样的,MVVM Toolkit也是使用Prism,但由于引入CefGlue.Avalonia隐式引入了ReactiveUI,所以这里选择ReactiveUI。
- 项目建好后,右键项目“管理 Nuget 程序包”,将已安装的“CommunityToolkit.Mvvm”卸载掉。
- 安装 Nuget 包:Prism.DryIoc.Avalonia、Material.Avalonia.DataGrid。
二、集成Prism。
- 基于Prism的BindableBase实现ViewModel-First的ViewModelBase。
public abstract class PrismViewModelBase : BindableBase { /// <summary> /// 创建视图 /// </summary> /// <returns></returns> /// <exception cref="Exception"></exception> public virtual Control CreateView() { var type = this.GetType(); // 按命名规则获取View的类型 string? nameSpace = null; if (type.Namespace is { } ns) { nameSpace = ns.Replace("ViewModel", "View"); } var name = type.Name.Replace("ViewModel", "View"); var fullName = nameSpace == null ? name : $"{nameSpace}.{name}"; var definedTypes = type.Assembly.DefinedTypes.ToList(); var viewType = definedTypes.FirstOrDefault(q => q.FullName == fullName); if (viewType == null) { fullName = fullName.Replace("ViewModel", "View"); viewType = definedTypes.FirstOrDefault(q => q.FullName == fullName); if (viewType == null) { throw new Exception(type.FullName + "对应的View未找到!"); } } if (Activator.CreateInstance(viewType) is not Control { } control) { throw new Exception( $"{Activator.CreateInstance(viewType)?.GetType().FullName}无法转换为类型{typeof(Control).FullName}。"); } control.DataContext = this; Control = control; return control; } /// <summary> /// 视图 /// </summary> public Control? Control { get; set; } }
- CreateView是public方法,调用CreateView方法根据命名规则自动查找View,并将View的DataContext设为当前ViewModel。
- 不调用CreateView方法则可以兼容Prism的View-First方式,比如在axaml中使用prism:ViewModelLocator.AutoWireViewModel="True",或者IRegionManager.RegisterViewWithRegion方法;
-
那么使用Prism的View-First方式创建的ViewModel如何对Control(视图)赋值呢?使用ViewModelLocationProvider.SetDefaultViewModelFactory监听ViewModel实例化,代码如下:
ViewModelLocationProvider.SetDefaultViewModelFactory((view, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] viewModelType) => { var viewModel = Activator.CreateInstance(viewModelType); if (viewModel is PrismViewModelBase myViewModel && view is Control control) { myViewModel.Control = control; } return viewModel; });
- 启动App。
using Avalonia; using Avalonia.Controls; using Avalonia.Controls.ApplicationLifetimes; using Avalonia.Data.Core.Plugins; using Avalonia.Markup.Xaml; using AvaloniaDesktopApp.ViewModels; using GntAvaloniaCore.Mvvm; using GntAvaloniaCore.Utils; using Prism.DryIoc; using Prism.Ioc; using Prism.Mvvm; using System; using System.Diagnostics.CodeAnalysis; namespace AvaloniaDesktopApp { public class App : PrismApplication { public override void Initialize() { AvaloniaXamlLoader.Load(this); // Required when overriding Initialize base.Initialize(); } [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")] public override void OnFrameworkInitializationCompleted() { if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop) { // Line below is needed to remove Avalonia data validation. // Without this line you will get duplicate validations from both Avalonia and CT BindingPlugins.DataValidators.RemoveAt(0); var mainWindowViewModel = Container.Resolve<MainWindowViewModel>(); desktop.MainWindow = mainWindowViewModel.View; } base.OnFrameworkInitializationCompleted(); } protected override AvaloniaObject CreateShell() { // 获取主窗口的 ViewModel 并启动相应的 View // ViewModel-First var mainWindowViewModel = Container.Resolve<MainWindowViewModel>(); return mainWindowViewModel.CreateView(); } protected override void RegisterTypes(IContainerRegistry containerRegistry) { // Register you Services, Views, Dialogs, etc. // 注册 MainWindowViewModel containerRegistry.RegisterSingleton<ILog, NLogWrapper>(); containerRegistry.RegisterSingleton<MainWindowViewModel>(); } protected override void OnInitialized() { base.OnInitialized(); ViewModelLocationProvider.SetDefaultViewModelFactory((view, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] viewModelType) => { var viewModel = Activator.CreateInstance(viewModelType); if (viewModel is PrismViewModelBase myViewModel && view is Control control) { myViewModel.Control = control; // 将 View 注入 ViewModel } return viewModel; }); // Register Views to the Region it will appear in. Don't register them in the ViewModel. //var regionManager = Container.Resolve<IRegionManager>(); // WARNING: Prism v11.0.0-prev4 // - DataTemplates MUST define a DataType or else an XAML error will be thrown // - Error: DataTemplate inside of DataTemplates must have a DataType set //regionManager.RegisterViewWithRegion(nameof(RegionNames.MainWindowContentRegion), typeof(WindowTestView)); } } }
- App集成PrismApplication。
- 注册 MainWindowViewModel 为单例,在CreateShell初始化MainWindowViewModel单例,并通过CreateView返回对应的View,在OnFrameworkInitializationCompleted中再次获取MainWindowViewModel单例,并将View设为MainWindow。
三、引入Material主题。
<Application xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:themes="clr-namespace:Material.Styles.Themes;assembly=Material.Styles" x:Class="AvaloniaDesktopApp.App"> <Application.Styles> <themes:MaterialTheme BaseTheme="Light" PrimaryColor="Blue" SecondaryColor="Yellow" /> <StyleInclude Source="avares://GntAvaloniaUI/Styles/Generic.axaml"/> </Application.Styles> </Application>
- 使用Material主题色。

# cd到项目输出目录AvaloniaDesktop/bin/x64/Debug/net80 # 添加执行权限,主要是浏览器 chmod +x AvaloniaDesktopApp chmod +x ./CefGlueBrowserProcess/Xilium.CefGlue.BrowserProcess # 执行APP ./AvaloniaDesktopApp
由于Avalonia在国产系统UOS上表现不一,Ubuntu上可以完美呈现Demo,但UOS在窗口最大化时,窗口尺寸不变且移至左上角,在渲染阴影上一片漆黑,渲染通知窗口底色是灰色的,而且在两台不同设备同样都是x86_x64的UOS系统上渲染也是不一致,所以放弃该移植方案,该系列不再更新。
分类:
Avalonia
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)