对象生存期事件

在其生存期内,Microsoft .NET 托管代码中的所有对象都会经历 创建使用销毁 阶段。 Windows Presentation Foundation (WPF)通过引发生存期事件来提供这些阶段的通知,因为它们在对象上发生。 对于 WPF 框架级元素(可视对象),WPF 实现InitializedLoadedUnloaded生存期事件。 开发人员可以将这些生命周期事件用作涉及元素的代码执行操作的钩子函数。 本文介绍视觉对象的生存期事件,然后引入其他专门应用于窗口元素、导航主机或应用程序对象的生存期事件。

先决条件

本文假定基本了解 WPF 元素布局如何概念化为树,以及已阅读 路由事件概述。 若要遵循本文中的示例,如果熟悉可扩展应用程序标记语言(XAML),并且知道如何编写 WPF 应用程序,则很有帮助。

视觉对象生命周期事件

WPF 框架级元素派生自 FrameworkElementFrameworkContentElement. InitializedLoadedUnloaded 生命周期事件是所有 WPF 框架级元素通用的。 以下示例演示主要在 XAML 中实现的元素树。 XAML 定义一个父Canvas元素,其中包含嵌套元素,每个元素都使用 XAML 属性语法附加InitializedLoadedUnloaded生存期事件处理程序。

<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.

程序输出显示在每个树对象上调用InitializedLoadedUnloaded生存期事件的顺序。 这些事件按每个树对象上引发的顺序在以下各节中进行了介绍。

初始化的生命周期事件

WPF 事件系统在元素上引发 Initialized 事件:

  • 设置元素的属性时。
  • 大约在同一时间,该对象通过对其构造函数的调用进行初始化。

某些元素属性(例如 Panel.Children)可以包含子元素。 父元素只有在子元素初始化后才能报告初始化。 因此,属性值的设置从元素树中嵌套最深的元素开始,然后逐级向上直到应用程序的根元素。 由于事件发生在设置元素属性时,因此该事件首先在按照标记定义的最深嵌套元素上调用,接着逐层到达应用程序根。 在后台代码中动态创建对象时,其初始化可能会不按顺序进行。

WPF 事件系统在引发一个元素的 Initialized 事件时,不会等待元素树中的所有元素都完成初始化。 因此,为任何元素编写 Initialized 事件处理程序时,请记住,逻辑或可视化树中的元素(尤其是父元素)可能尚未创建。 或者,其成员变量和数据绑定可能未初始化。

注释

Initialized事件在一个元素上触发时,该元素的表达式使用(如动态资源或绑定)将停止评估。

加载的生存期事件

WPF 事件系统在元素上引发 Loaded 事件:

  • 当包含元素的逻辑树完成并连接到演示文稿源时。 呈现源提供窗口句柄(HWND)和呈现图面。
  • 当数据绑定到本地源(例如其他属性或直接定义的数据源)时已完成。
  • 在布局系统计算完所有渲染所需的值之后。
  • 在最终呈现之前。

逻辑树中的所有元素加载完成之前,Loaded事件不会在元素树中的任何元素上引发。 WPF 事件系统首先在元素树的根元素上提升 Loaded 事件,然后在每个后续子元素上依次提升到最深层的嵌套元素。 尽管此事件可能与 隧道 路由事件类似,但 Loaded 该事件不会将事件数据从一个元素传送到另一个元素,因此将事件标记为已处理没有效果。

注释

WPF 事件系统无法保证在 Loaded 事件之前异步数据绑定已完成。 异步数据绑定连接到外部或动态数据源。

卸载生命周期事件

WPF 事件系统在元素上引发 Unloaded 事件:

  • 删除其演示源时,或
  • 当其视觉上的父级被移除时。

WPF事件系统首先在元素树的根元素上引发Unloaded事件,然后逐个在每个后续的子元素上引发,直到最深层嵌套的元素。 尽管此事件可能与 隧道 路由事件类似,但 Unloaded 该事件不会将事件数据从元素传播到元素,因此将事件标记为已处理没有效果。

当事件 Unloaded 在元素上引发时,它的 元素或在逻辑或可视化树中更高级的元素可能已被取消。 Unset 表示元素的数据绑定、资源引用和样式不再设置为其正常或上次已知的运行时值。

其他人生重大事件

从生存期事件的角度来看,WPF 对象有四种主要类型:一般元素、窗口元素、导航主机和应用程序对象。 InitializedLoadedUnloaded生存期事件适用于所有框架级元素。 其他生存期事件特别适用于窗口元素、导航主机或应用程序对象。 有关其他生存期事件的信息,请参阅:

另请参阅