架构原理
Crystal.Avalonia 的内部工作方式。用法请见 快速开始 与 教程。
启动流程
CrystalApplication.OnFrameworkInitializationCompleted() 按以下顺序执行:
ConfigureOptions(Options) ← 应用配置 CrystalOptions
↓
services.AddSingleton(Options, Mvvm)
RegisterServices(services) ← 应用级 DI(AddMvvm 写入该实例的映射)
↓
创建并注册 ModuleManager
RegisterModules(moduleRegistrar) ← 应用注册 IModule
↓
moduleManager.InitService(services) ← 各 module.RegisterServices()
↓
services.BuildServiceProvider()
↓
Mvvm.ServiceProvider = sp ← 在 InitModules 之前(ViewModelLocator 可解析)
↓
将 ViewLocator 加入 DataTemplates ← 若 Options.EnableViewLocator
↓
moduleManager.InitModules(sp) ← 各 module.InitializeModule()
↓
CreateShell(sp) ← 创建 MainWindow / MainView
要点:
- 先 Options 后 DI —
ConfigureOptions最先执行;CrystalOptions作为单例进入容器 - 先 App 后模块 —
App.RegisterServices在InitService之前执行 - 单一
ServiceProvider— 只构建一次;模块在容器就绪后初始化 CrystalApplication.Mvvm.ServiceProvider— 在InitModules与CreateShell前赋值,供ViewModelLocator使用- 每个应用一个
MvvmManager— 映射在应用实例上,不是进程级静态字典
模块系统
ModuleManager 实现 IModuleRegistrar:
| 阶段 | 方法 | 时机 |
|---|---|---|
| 注册 | RegisterModule<T>() |
构建容器前;Activator.CreateInstance<T>() |
| 服务 | IModule.RegisterServices() |
InitService 期间,BuildServiceProvider 之前 |
| 初始化 | IModule.InitializeModule() |
容器构建后,InitModules 期间 |
模块是普通类 —— 无程序集扫描。每个模块在 RegisterModules 中显式注册。库不提供模块依赖图、延迟加载、导航或事件聚合器。
MVVM 绑定
类型映射(MvvmManager)
AddMvvmTransient / AddMvvmSingleton 做两件事:
- 仅将 ViewModel 注册进 DI(
AddTransient/AddSingleton) - 在
IServiceCollection中的MvvmManager实例上记录 View ↔ ViewModel 映射(与CrystalApplication.Mvvm为同一实例)
TView 不会加入 IServiceCollection。
View-First(ViewModelLocator)
附加属性 AutoWireViewModel="True" 在变更时触发。不依赖 Options.EnableViewLocator。
XAML 加载 View
↓
ViewModelLocator 读取 view.GetType()
↓
CrystalApplication.Mvvm.GetVmType(viewType)
↓
ServiceProvider.GetRequiredService(vmType)
↓
view.DataContext = viewModel
↓
ViewLifecycleBinder.AttachIfNeeded()
设计时模式(Design.IsDesignMode)会跳过。
ViewModel-First(ViewLocator)
当 CrystalApplication.Options.EnableViewLocator 为 true(默认)时,注册为 Application.DataTemplates 上的 IDataTemplate:
ContentControl.Content = viewModel
↓
ViewLocator.Match(vm) — 是否有映射?
↓
Mvvm.GetViewType(vmType)
↓
Activator.CreateInstance(viewType) ← 不从 DI
↓
view.DataContext = viewModel
↓
ViewLifecycleBinder.AttachIfNeeded()
仅匹配已注册映射的类型。
生命周期(ViewLifecycleBinder)
当 DataContext 实现 ILifecycleAware:
- 订阅 View
Loaded/Unloaded并保持订阅 - 每次
Loaded调用OnLoadedAsync(isFirstLoad)—isFirstLoad按 ViewModel 实例(该对象第一次加载为true) - 每次
Unloaded调用OnUnloaded() - 异常捕获并写入
Trace
ViewModelLocator 与 ViewLocator 都会使用。
Shell 创建
Shell 视图(MainWindow、MainView)不进 DI — 用 new 创建:
public override void CreateShell(IServiceProvider sp)
{
CreateShell<MainWindow, MainView>();
}
CreateShell<TWindow, TView>() 按 ApplicationLifetime 赋值:
| Lifetime | 行为 |
|---|---|
IClassicDesktopStyleApplicationLifetime |
desktop.MainWindow = new TWindow() |
ISingleViewApplicationLifetime |
single.MainView = new TView() |
IActivityApplicationLifetime |
factory.MainViewFactory = () => new TView() |
子内容 ViewModel 在 View 加载时注入(AutoWireViewModel="True")。
若 Shell 确实需要构造注入,可重写 CreateShell 手动解析 —— 这是例外,不是默认。
设计决策
为什么 View 不进 DI
| View | ViewModel | |
|---|---|---|
| 创建方式 | XAML 解析 / Activator.CreateInstance |
DI 容器 |
| 生命周期 | 绑定视觉树 | Transient 或 Singleton |
| 构造依赖 | 通常无(code-behind 精简) | 服务、仓储等 |
View 是 UI 产物;ViewModel 承载业务逻辑与依赖。View 不进 DI 可避免容器管理 UI 生命周期,并简化 AOT 裁剪。
Transient vs Singleton
仅两种模式 —— 无 Hybrid。按 ViewModel 选择:
- Transient — 每次导航新实例(页面常见);每次都是
OnLoadedAsync(true) - Singleton — 共享状态(设置、会话);再次进入时
isFirstLoad: false
版本脉络
| 从 | 到 | 说明 |
|---|---|---|
| v1.2 | 2.0 | 仅 ViewModel 进 DI;无 AddMvvmHybrid |
| 2.0.0 | 2.0.1 | Shell 用 CreateShell / new;移除 CreateShellFromDi |
| 2.0.1 | 3.0.0 | 实例 MvvmManager;EnableViewLocator;OnLoadedAsync(bool) |
| 3.0.0 | 3.1.0 | 实例 CrystalOptions(ConfigureOptions / DI 单例;原为静态类) |
见 升级指南。
AOT 与裁剪
Crystal.Avalonia 避免运行时程序集扫描。类型发现均为编译期泛型:
AddMvvmTransient<TView, TViewModel>— 通过[DynamicallyAccessedMembers(PublicConstructors)]保留构造函数ModuleManager.RegisterModule<TModule>()— 同上ViewLocator.CreateView— 映射字典中的 View 类型已注解
库设置 IsAotCompatible=true。发布命令见 AOT 兼容性。
组件关系
CrystalApplication
├── Options (CrystalOptions) ─ EnableViewLocator(默认 true);DI 单例
├── Mvvm (MvvmManager) ───── 每应用一份映射 + ServiceProvider
├── ModuleManager ────────── IModule.RegisterServices / InitializeModule
├── ViewModelLocator ─────── 附加属性 → DI 解析 ViewModel
├── ViewLocator ──────────── IDataTemplate → Activator.CreateInstance View
├── EventToCommand ───────── 事件 → ICommand(默认 Avalonia xmlns)
└── ViewLifecycleBinder ──── ILifecycleAware 钩子