mirror of
https://github.com/AtomUI/AtomUI.git
synced 2026-10-27 12:17:42 +08:00
5.9 KiB
5.9 KiB
Scoped Resource Host 开发规范
本规范定义 owner-managed、非 Visual AvaloniaObject 承载 Avalonia binding 和动态资源时的开发规则。
生命周期样板代码由 ScopedResourceHostGenerator 生成;Control 与容器仍负责确定 owner、保存 attachment token
并在所有退出路径释放。
生成器输入、输出和 diagnostic 见 Scoped Resource Host Generator。
适用场景
一个类型同时满足以下条件时,应使用 [GenerateScopedResourceHost]:
- 类型继承
AvaloniaObject,但不是Control、StyledElement或Visual。 - 类型暴露 Avalonia 属性,并可能承载 XAML binding、
DynamicResource或 token-resource binding。 - 类型由 Control、collection、container 或其他 owner 管理生命周期。
- 对象离开 owner 后必须停止资源和主题通知。
典型对象包括 DescriptionItem、NavMenuNode、DataGrid column/group item 和其他描述型对象。
以下对象不使用该机制:
- 普通 POCO 数据模型。
- 已位于 Visual / StyledElement 树中的真实控件。
- 只包含 CLR 属性且不作为 Avalonia binding target 的内部结构。
- 应用级单例资源绑定;这类对象必须提供显式 create/release 生命周期。
类型声明
业务主文件只保留业务 API、Avalonia 属性和 CLR wrapper:
[GenerateScopedResourceHost]
public partial class DescriptionItem : AvaloniaObject
{
public static readonly DirectProperty<DescriptionItem, string> LabelProperty =
AvaloniaProperty.RegisterDirect<DescriptionItem, string>(
nameof(Label),
item => item.Label,
(item, value) => item.Label = value);
private string _label = string.Empty;
public string Label
{
get => _label;
set => SetAndRaise(LabelProperty, ref _label, value);
}
}
业务主文件不得复制以下样板状态:
_resourceHost、attachment count 或 generation。ResourcesChanged/ActualThemeVariantChanged转发。TryGetResourceowner fallback。- owner event subscribe/unsubscribe。
AttachResourceHost/ detach token 实现。
Owner 生命周期
标准 owner 流程:
item enters owner collection or container
-> item.AttachResourceHost(owner)
-> store attachment token with the item/container lifetime
-> subscribe business property changes
item leaves owner collection or container
-> dispose attachment token
-> unsubscribe business property changes
-> clear generated visual mapping
Owner 必须:
- 保存每次
AttachResourceHost(...)返回的 disposable。 - 在 remove、reset、Items replacement、container clear、template reapply、owner change 和 detach 时释放。
- 在重新 attach 前先完成旧 owner 的业务订阅和 visual mapping 清理。
- 只在当前生成周期内保存 item 到 visual 的映射,不能把 generated visual 挂回 item。
- 自身不能提供
IResourceHost时,选择明确的局部 resource host;不能把Application.Current当作主要 owner。
同一对象允许在一个 owner 内重复 attach,但每个 token 都必须由获得它的生命周期负责释放。不得缓存一个全局 token 跨 container 或 owner 复用。
资源查找与通知
- 当前 owner resources 优先于
Application.Current.Resources。 - Owner 实现
IThemeVariantHost时,ActualThemeVariant 跟随 owner。 - Owner 资源或主题变化只影响当前 attachment generation。
- Detach 后旧 owner 的事件不得继续触达 item。
- 动态资源失效通过正常资源通知完成,不使用清空 DataContext、强制路由或 Dispatcher 延时补丁。
生命周期验证
每个使用 [GenerateScopedResourceHost] 的对象至少覆盖:
- DynamicResource 不 root 已移除对象:WeakReference + GC,并模拟真实 DynamicResource anchor。
- Owner resource 优先于 Application resource。
- Owner 或 Application resource 更新后属性重新发布。
- remove/reset/container clear/template reapply/owner change/detach 后释放旧 owner。
- 同一 owner repeated attach 的计数释放。
A -> B -> A后,第一轮 A token 不影响当前 attachment。
涉及具体 Control 时,还应运行相邻 Control 测试、Gallery build,并根据影响面执行 NativeAOT 验证。
Review 清单
- 普通 POCO 是否已经足够,是否确实需要 Avalonia 属性系统。
- 类型是否可能承载 DynamicResource、token-resource binding 或 XAML binding target。
- 业务属性是否仍在主文件中清晰可见。
- Generated resource host 是否保持 internal 实现边界。
- Owner 是否覆盖所有 attach/release 对称路径。
- Item、container、visual mapping 和事件订阅是否一起释放。
- 是否具有 WeakReference、资源优先级、更新和 host reentry 测试。
- 是否没有新增反射、字符串 binding、全局 token binding 或延迟清理。
迁移手写实现
迁移既有手写 IResourceHost / IThemeVariantHost 类型时:
- 先建立 WeakReference、owner resource、resource update 和 host reentry 测试。
- 将目标类型声明为 partial 并添加
[GenerateScopedResourceHost]。 - 删除手写资源宿主字段、事件和 attach/detach 实现。
- 保留业务 API、Avalonia 属性和 owner 侧 token 管理。
- 对比生成后的资源查找顺序、通知和 release 时机。
- 运行目标 Control 测试、Gallery build 和
git diff --check。
迁移不得顺手改变 Control API、默认值、binding priority、视觉树、主题资源 key 或用户可观察行为。