控件的生命周期
UserControl 的生命周期
UserControl 继承自 Control,因此它的生命周期与所有 Control 类型相同。主要的生命周期事件包括:
1. Loaded 事件
Loaded 事件在控件完全构建在可视树中,并且布局和渲染都完成后触发。 Control.cs:174-183
触发时机:
- 控件附加到可视树后(
OnAttachedToVisualTreeCore) Control.cs:382-389 - 控件模板已应用,模板中创建的对象引用可用
- 这是控件创建生命周期中最晚的时机
实现机制:
控件附加到可视树时会调用 ScheduleOnLoadedCore(),将控件添加到加载队列中: Control.cs:294-308
加载队列会在 Dispatcher 的 Loaded 优先级上批量处理,以提高性能: Control.cs:260-288
最终调用 OnLoadedCore() 触发 Loaded 事件: Control.cs:314-328
2. Unloaded 事件
Unloaded 事件在控件从可视树中移除时触发。 Control.cs:190-201
触发时机:
- 控件从可视树分离时
- 与
Loaded对称,用于清理资源
实现机制: Control.cs:334-352
3. Window 的特殊处理
Window 和 WindowBase 作为可视树根节点,不会触发 OnAttachedToVisualTreeCore,因此需要手动调度 Loaded/Unloaded 事件: WindowBase.cs:208-227
在Control被挂载到可视树之前,会有以下几个关键的生命周期阶段:
1. 构造函数 (Constructor)
控件实例化时调用,这是生命周期的起点。
2. BeginInit() / EndInit()
Control 继承自 StyledElement,实现了 ISupportInitialize 接口。link to Repo AvaloniaUI/Avalonia: src/Avalonia.Base/StyledElement.cs:350-368
BeginInit():
- 在 XAML 解析开始时调用
- 增加
_initCount计数器,表示初始化开始
EndInit():
- 在 XAML 解析完成、所有属性设置后调用
- 减少
_initCount计数器 - 当
_initCount为 0 且控件已附加到逻辑树时,会触发样式应用和初始化
测试代码验证了初始化顺序:link to Repo AvaloniaUI/Avalonia: tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs:686-708
控件会在附加到父控件之后、最终 EndInit 之前被添加到逻辑树。
3. AttachedToLogicalTree
控件附加到逻辑树时触发,发生在附加到可视树之前。
测试验证了顺序:link to Repo AvaloniaUI/Avalonia: tests/Avalonia.Markup.Xaml.UnitTests/Xaml/BasicTests.cs:663-684
控件会在属性设置之前被添加到父控件。
4. ApplyStyling()
在 EndInit() 中,如果控件已附加到逻辑树,会调用 ApplyStyling()。link to Repo AvaloniaUI/Avalonia: src/Avalonia.Base/StyledElement.cs:381-414
这个方法会:
- 应用控件主题 (
ApplyControlTheme) - 应用模板化父控件主题 (
ApplyTemplatedParentControlTheme) - 应用样式 (
ApplyStyles)
5. InitializeIfNeeded()
在样式应用后调用,设置 IsInitialized 为 true。link to Repo AvaloniaUI/Avalonia: src/Avalonia.Base/StyledElement.cs:416-420
6. OnInitialized()
当 IsInitialized 设置为 true 后触发的虚方法,子类可以重写。link to Repo AvaloniaUI/Avalonia: src/Avalonia.Base/StyledElement.cs:614-616
7. OnAttachedToVisualTreeCore()
控件附加到可视树时调用。link to Repo AvaloniaUI/Avalonia: src/Avalonia.Controls/Control.cs:382-389 这个方法会:
- 调用
InitializeIfNeeded()确保控件已初始化 - 调用
ScheduleOnLoadedCore()安排Loaded事件
完整的挂载前生命周期顺序
![[Pasted image 20251108222519.png]]