物件存留期事件 (WPF .NET)
在其存留期間,Microsoft .NET 受控程式碼中的所有物件都會經歷建立、使用和消滅階段。 Windows Presentation Foundation (WPF) 會在物件發生這些階段時,藉由引發存留期事件來提供這些階段的通知。 對於 WPF 架構層級元素 (視覺物件),WPF 會實作 Initialized、Loaded 和 Unloaded 存留期事件。 開發人員可以使用這些存留期事件作為涉及元素的程式碼後置作業的勾點。 本文說明視覺物件的存留期事件,然後介紹特別適用於視窗元素、導覽主機或應用程式物件的其他存留期事件。
必要條件
本文假設您已基本了解 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.
程式輸出會顯示每個樹狀結構物件上,Initialized
、Loaded
和 Unloaded
存留期事件的叫用順序。 這些事件會在下列各節中按照在每個樹狀結構物件上引發的順序進行說明。
初始化存留期事件
WPF 事件系統會在下列時間點在元素上引發 Initialized 事件:
- 設定元素的屬性時。
- 呼叫物件的建構函式以初始化該物件的同時。
某些元素屬性,例如 Panel.Children,可以包含子元素。 父元素在其子元素初始化之前,無法報告初始化。 因此,屬性值會從元素樹狀結構中最深的巢狀元素開始設定,然後是連續的父元素,直到應用程式根。 由於 Initialized
事件會在設定元素的屬性時發生,因此該事件會先在標記中所定義的最深層巢狀元素上叫用,然後是連續的父元素,直到應用程式根。 當物件在程式碼後置中動態建立時,其初始化可能順序不一。
WPF 事件系統不會等待元素樹狀結構中的所有元素都初始化之後,才在元素上引發 Initialized
事件。 因此,當您為任何元素撰寫 Initialized
事件處理常式時,請記住邏輯或視覺化樹狀結構中的周圍元素,尤其是父元素,可能尚未建立。 或者,其成員變數和資料繫結可能未初始化。
注意
當元素上引發 Initialized
事件時,不會評估元素的運算式使用方式,例如動態資源或繫結。
載入存留期事件
WPF 事件系統會在下列時間點在元素上引發 Initialized 事件:
- 當包含元素的邏輯樹狀結構完成並連接到簡報來源時。 簡報來源會提供視窗控制代碼 (HWND) 和轉譯介面。
- 當繫結至本機來源的資料都已完成時,例如其他屬性或直接定義的資料來源。
- 配置系統計算轉譯所需的所有必要值之後。
- 最終轉譯之前。
除非載入邏輯樹狀結構中的所有元素,否則不會在元素樹狀結構中的任何元素上引發 Loaded
事件。 WPF 事件系統會先在元素樹狀結構的根項目上引發 Loaded
事件,然後在每個後續子元素上,再向下至最深層巢狀元素上引發。 雖然此事件可能類似於通道路由事件,但 Loaded
事件不會在元素之間傳送事件資料,因此將事件標示為已處理並沒有任何作用。
注意
WPF 事件系統無法保證非同步資料繫結在 Loaded
事件之前已完成。 非同步資料繫結會繫結至外部或動態來源。
卸除存留期事件
WPF 事件系統會在下列時間點在元素上引發 Initialized 事件:
- 移除其簡報來源時,或
- 移除其視覺父元素時。
WPF 事件系統會先在元素樹狀結構的根項目上引發 Loaded
事件,然後在每個後續子元素上,再向下至最深層巢狀元素上引發。 雖然此事件可能類似於通道路由事件,但 Unloaded
事件不會在元素之間散佈事件資料,因此將事件標示為已處理並沒有任何作用。
當元素上引發 Unloaded
事件時,可能已經取消設定其父元素,或者邏輯或視覺化樹狀結構中任何較高的元素。 取消設定表示元素的資料繫結、資源參考和樣式不再設定為其一般或上次已知的執行階段值。
其他存留期事件
從存留期事件的角度來看,有四種主要的 WPF 物件類型:一般元素、視窗元素、瀏覽主機和應用程式物件。 Initialized、Loaded 和 Unloaded 存留期事件適用於所有架構層級元素。 其他存留期事件特別適用於視窗元素、導覽主機或應用程式物件。 如需其他存留期事件的相關資訊,請參閱:
- 適用於 Application 物件的應用程式管理概觀。
- 適用於 Window 元素的 WPF 視窗概觀。
- 適用於 Page、NavigationWindow 和 Frame 元素的瀏覽概觀。