在其生存期内,Microsoft .NET 托管代码中的所有对象都会经历
创建
、
使用
和
销毁
阶段。 Windows Presentation Foundation (WPF) 通过引发生存期事件来提供这些阶段的通知,因为它们发生在对象上。 对于 (视觉对象) 的 WPF 框架级元素,WPF 实现
Initialized
、
Loaded
和
Unloaded
生存期事件。 开发人员可以将这些生存期事件用作涉及元素的代码隐藏操作的挂钩。 本文介绍视觉对象的生存期事件,然后介绍专门应用于窗口元素、导航主机或应用程序对象的其他生存期事件。
面向 .NET 7 和 .NET 6 的桌面指南文档正在撰写中。
本文假定你已基本了解如何将 WPF 元素布局概念化为树,并且你已阅读
路由事件概述
。 若要遵循本文中的示例,如果熟悉 Extensible Application Markup Language (XAML) 并知道如何编写 WPF 应用程序,将会很有帮助。
视觉对象的生存期事件
WPF 框架级元素派生自
FrameworkElement
或
FrameworkContentElement
。
Initialized
、
Loaded
和
Unloaded
生存期事件是所有 WPF 框架级别元素通用的。 以下示例演示主要在 XAML 中实现的元素树。 XAML 定义一个包含嵌套元素的父
Canvas
元素,每个元素都使用 XAML 属性语法来附加
Initialized
、
Loaded
和
Unloaded
生存期事件处理程序。
<Canvas x:Name="canvas">
<StackPanel x:Name="outerStackPanel" Initialized="InitHandler" Loaded="LoadHandler" Unloaded="UnloadHandler">
<custom:ComponentWrapper x:Name="componentWrapper" Initialized="InitHandler" Loaded="LoadHandler" Unloaded="UnloadHandler">
<TextBox Name="textBox1" Initialized="InitHandler" Loaded="LoadHandler" Unloaded="UnloadHandler" />
<TextBox Name="textBox2" Initialized="InitHandler" Loaded="LoadHandler" Unloaded="UnloadHandler" />
</custom:ComponentWrapper>
</StackPanel>
<Button Content="Remove canvas child elements" Click="Button_Click"/>
</Canvas>
其中一个 XAML 元素是自定义控件,该控件派生自在代码隐藏中分配生存期事件处理程序的基类。
public partial class MainWindow : Window
public MainWindow() => InitializeComponent();
// Handler for the Initialized lifetime event (attached in XAML).
private void InitHandler(object sender, System.EventArgs e) =>
Debug.WriteLine($"Initialized event on {((FrameworkElement)sender).Name}.");
// Handler for the Loaded lifetime event (attached in XAML).
private void LoadHandler(object sender, RoutedEventArgs e) =>
Debug.WriteLine($"Loaded event on {((FrameworkElement)sender).Name}.");
// Handler for the Unloaded lifetime event (attached in XAML).
private void UnloadHandler(object sender, RoutedEventArgs e) =>
Debug.WriteLine($"Unloaded event on {((FrameworkElement)sender).Name}.");
// Remove nested controls.
private void Button_Click(object sender, RoutedEventArgs e) =>
canvas.Children.Clear();
// Custom control.
public class ComponentWrapper : ComponentWrapperBase { }
// Custom base control.
public class ComponentWrapperBase : StackPanel
public ComponentWrapperBase()
// Assign handler for the Initialized lifetime event (attached in code-behind).
Initialized += (object sender, System.EventArgs e) =>
Debug.WriteLine($"Initialized event on componentWrapperBase.");
// Assign handler for the Loaded lifetime event (attached in code-behind).
Loaded += (object sender, RoutedEventArgs e) =>
Debug.WriteLine($"Loaded event on componentWrapperBase.");
// Assign handler for the Unloaded lifetime event (attached in code-behind).
Unloaded += (object sender, RoutedEventArgs e) =>
Debug.WriteLine($"Unloaded event on componentWrapperBase.");
/* Output:
Initialized event on textBox1.
Initialized event on textBox2.
Initialized event on componentWrapperBase.
Initialized event on componentWrapper.
Initialized event on outerStackPanel.
Loaded event on outerStackPanel.
Loaded event on componentWrapperBase.
Loaded event on componentWrapper.
Loaded event on textBox1.
Loaded event on textBox2.
Unloaded event on outerStackPanel.
Unloaded event on componentWrapperBase.
Unloaded event on componentWrapper.
Unloaded event on textBox1.
Unloaded event on textBox2.
Partial Public Class MainWindow
Inherits Window
Public Sub New()
InitializeComponent()
End Sub
' Handler for the Initialized lifetime event (attached in XAML).
Private Sub InitHandler(sender As Object, e As EventArgs)
Debug.WriteLine($"Initialized event on {CType(sender, FrameworkElement).Name}.")
End Sub
' Handler for the Loaded lifetime event (attached in XAML).
Private Sub LoadHandler(sender As Object, e As RoutedEventArgs)
Debug.WriteLine($"Loaded event on {CType(sender, FrameworkElement).Name}.")
End Sub
' Handler for the Unloaded lifetime event (attached in XAML).
Private Sub UnloadHandler(sender As Object, e As RoutedEventArgs)
Debug.WriteLine($"Unloaded event on {CType(sender, FrameworkElement).Name}.")
End Sub
Private Sub Button_Click(sender As Object, e As RoutedEventArgs)
' Remove nested controls.
canvas.Children.Clear()
End Sub
End Class
' Custom control.
Public Class ComponentWrapper
Inherits ComponentWrapperBase
End Class
' Custom base control.
Public Class ComponentWrapperBase
Inherits StackPanel
Public Sub New()
' Attach handlers for the lifetime events.
AddHandler Initialized, AddressOf InitHandler
AddHandler Loaded, AddressOf LoadHandler
AddHandler Unloaded, AddressOf UnloadHandler
End Sub
' Handler for the Initialized lifetime event (attached in code-behind).
Private Sub InitHandler(sender As Object, e As EventArgs)
Debug.WriteLine("Initialized event on componentWrapperBase.")
End Sub
' Handler for the Loaded lifetime event (attached in code-behind).
Private Sub LoadHandler(sender As Object, e As RoutedEventArgs)
Debug.WriteLine("Loaded event on componentWrapperBase.")
End Sub
' Handler for the Unloaded lifetime event (attached in code-behind).
Private Sub UnloadHandler(sender As Object, e As RoutedEventArgs)
Debug.WriteLine("Unloaded event on componentWrapperBase.")
End Sub
End Class
'Output:
'Initialized event on textBox1.
'Initialized event on textBox2.
'Initialized event on componentWrapperBase.
'Initialized event on componentWrapper.
'Initialized event on outerStackPanel.
'Loaded event on outerStackPanel.
'Loaded event on componentWrapperBase.
'Loaded event on componentWrapper.
'Loaded event on textBox1.
'Loaded event on textBox2.
'Unloaded event on outerStackPanel.
'Unloaded event on componentWrapperBase.
'Unloaded event on componentWrapper.
'Unloaded event on textBox1.
'Unloaded event on textBox2.
程序输出显示每个树对象上 、
Loaded
和
Unloaded
生存期事件的调用
Initialized
顺序。 这些事件按在每个树对象上引发的顺序在以下各节中介绍。
初始化的生存期事件
在以下情况下,WPF 事件系统会在元素上引发
Initialized
事件:
设置元素的属性时。
大约在对象通过调用其构造函数进行初始化的同一时间。
某些元素属性(如
Panel.Children
)可以包含子元素。 父元素在初始化其子元素之前无法报告初始化。 因此,属性值从元素树中嵌套最深的元素 () 开始设置,后跟应用程序根目录的连续父元素。
Initialized
由于在设置元素的属性时发生该事件,因此首先在标记中定义的最深层嵌套的元素 () 上调用该事件,然后调用到应用程序根目录的连续父元素。 在代码隐藏中动态创建对象时,其初始化可能不按顺序进行。
WPF 事件系统不会等待初始化元素树中的所有元素,然后再对元素引发
Initialized
事件。 因此,在为任何元素编写
Initialized
事件处理程序时,请记住,逻辑树或可视化树中的周围元素(尤其是父元素)可能尚未创建。 或者,其成员变量和数据绑定可能未初始化。
在
Initialized
元素上引发 事件时,将取消计算元素的表达式用法,例如动态资源或绑定。
加载的生存期事件
在以下情况下,WPF 事件系统会在元素上引发
Loaded
事件:
当包含 元素的逻辑树完成并连接到表示源时。 呈现源提供 (HWND) 和呈现图面的窗口句柄。
当数据绑定到本地源(例如其他属性或直接定义的数据源)完成时。
在布局系统计算出呈现所需的所有值之后。
在最终呈现之前。
在
Loaded
加载逻辑树中的所有元素之前,不会对元素
树
中的任何
元素引发 该事件。 WPF 事件系统首先在元素树的根元素上引发
Loaded
事件,然后在每个连续的子元素上向下引发嵌套最深的元素。 尽管此事件可能类似于
隧道
路由事件,但
Loaded
事件不会将事件数据从一个元素传输到另一个元素,因此将事件标记为已处理没有效果。
WPF 事件系统无法保证异步数据绑定在事件之前
Loaded
已完成。 异步数据绑定绑定到外部或动态源。
Unloaded 生存期事件
在以下情况下,WPF 事件系统会在元素上引发
Unloaded
事件:
删除其呈现源时,或
删除其可视父级时。
WPF 事件系统首先在元素树的根元素上引发
Unloaded
事件,然后在每个连续的子元素上向下引发嵌套最深的元素。 尽管此事件可能类似于
隧道
路由事件,
Unloaded
但事件不会将事件数据从元素传播到元素,因此将事件标记为已处理没有效果。
在
Unloaded
元素上引发 事件时,该元素的
父
元素或逻辑树或可视化树中任何更高级别的元素可能已
未设置
。 未设置意味着元素的数据绑定、资源引用和样式不再设置为其正常或最后一个已知运行时值。
其他生存期事件
从生存期事件的角度来看,WPF 对象有四种main类型:常规元素、窗口元素、导航主机和应用程序对象。
Initialized
、
Loaded
和
Unloaded
生存期事件适用于所有框架级元素。 其他专门应用于窗口元素、导航主机或应用程序对象的生存期事件。 有关这些其他生存期事件的信息,请参阅:
对象的
应用程序管理概述
Application
。
元素
的 WPF 窗口
Window
概述。
、
NavigationWindow
和
Frame
元素的
Page
导航概述
。
Initialized
Loaded
Unloaded
处理 Loaded 事件
Loaded 事件和 Initialized 事件
WPF 中的树
路由事件概述