物件存留期事件 (WPF .NET)

在其存留期間,Microsoft .NET Managed 程式碼中的所有物件都會經歷 建立 使用 解構 階段。 Windows Presentation Foundation (WPF) 會藉由引發存留期事件,在物件上發生時,提供這些階段的通知。 對於 WPF 架構層級專案(視覺物件),WPF 會實作 InitializedLoadedUnloaded 存留期事件。 開發人員可以使用這些存留期事件作為涉及元素的程式碼後置作業的勾點。 本文說明視覺物件的存留期事件,然後介紹其他特別套用至視窗專案、導覽主機或應用程式物件的存留期事件。

重要

.NET 7 和 .NET 6 的桌面指南檔正在建置中。

必要條件

本文假設 WPF 元素配置如何概念化為樹狀結構,以及您已閱讀 路由事件概觀 的基本知識。 若要遵循本文中的範例,如果您熟悉可延伸的應用程式標記語言(XAML),並知道如何撰寫 WPF 應用程式,它很有説明。

視覺物件的存留期事件

WPF 架構層級專案衍生自 FrameworkElementFrameworkContentElementInitializedLoadedUnloaded 存留期事件適用于所有 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.

程式輸出會顯示每個樹狀結構物件上 、 Loaded 、 和 Unloaded 存留期事件的叫用 Initialized 順序。 這些事件會在下列各節中說明,其順序是在每個樹狀結構物件上引發。

初始化的存留期事件

WPF 事件系統會在 Initialized 專案上引發 事件:

  • 設定專案的屬性時。
  • 大約在同一時間,物件是透過對其建構函式的呼叫初始化。

某些專案屬性,例如 Panel.Children ,可以包含子專案。 父元素在初始化其子項目之前,無法報告初始化。 因此,屬性值會從元素樹狀結構中最深的巢狀元素開始設定,後面接著應用程式根目錄的後續父元素。 Initialized由於事件會在設定元素的屬性時發生,因此該事件會先在標記中所定義的最深層巢狀元素上叫用,後面接著應用程式根目錄的後續父元素。 當物件在程式碼後置中動態建立時,其初始化可能順序不一。

WPF 事件系統不會等候專案樹狀結構中的所有專案在引發 Initialized 專案上的事件之前初始化。 因此,當您撰寫 Initialized 任何專案的事件處理常式時,請記住邏輯或視覺化樹狀結構中的周圍專案,尤其是父元素,可能尚未建立。 或者,其成員變數和資料系結可能未初始化。

注意

Initialized在專案上引發事件時,元素的運算式使用方式,例如動態資源或系結,將會不受評估。

載入的存留期事件

WPF 事件系統會在 Loaded 專案上引發 事件:

  • 當包含 專案的邏輯樹狀結構完成並連接到簡報來源時。 簡報來源會提供視窗控點 (HWND) 和轉譯介面。
  • 當資料系結至本機來源時,例如其他屬性或直接定義的資料來源,都已完成。
  • 配置系統計算轉譯所需的所有值之後。
  • 在最終轉譯之前。

Loaded 載入邏輯樹狀結構中的所有專案 之前 ,不會在專案樹 狀結構中的任何專案上引發事件。 WPF 事件系統會先在專案樹狀結構的根項目上引發 事件,然後在每個後續子項目上,向下引發 Loaded 至最深層巢狀元素的 事件。 雖然此事件可能類似于 通道 路由事件, Loaded 但事件不會將事件資料從某個元素傳送到另一個元素,因此將事件標示為已處理沒有任何作用。

注意

WPF 事件系統無法保證非同步資料系結在事件之前 Loaded 已完成。 非同步資料系結會系結至外部或動態來源。

卸載的存留期事件

WPF 事件系統會在 Unloaded 專案上引發 事件:

  • 移除其簡報來源,或
  • 移除其視覺父系時。

WPF 事件系統會先在專案樹狀結構的根項目上引發 事件,然後在每個後續子項目上,向下引發 Unloaded 至最深層巢狀元素的 事件。 雖然此事件可能類似于 通道 路由事件, Unloaded 但事件不會將事件資料從元素傳播到元素,因此將事件標示為已處理沒有任何作用。

Unloaded在專案上引發事件時,可能是 邏輯或視覺化樹狀結構中較高元素的父 元素,可能已經 未設定 。 Unset 表示元素的資料系結、資源參考和樣式不再設定為其一般或上次已知的運行時間值。

其他存留期事件

從存留期事件的觀點來看,WPF 物件有四種主要類型:一般專案、視窗元素、導覽主機和應用程式物件。 InitializedLoadedUnloaded 存留期事件會套用至所有架構層級專案。 其他存留期事件特別適用于視窗專案、導覽主機或應用程式物件。 如需其他存留期事件的相關資訊,請參閱:

另請參閱