Freigeben über


Objektlebensdauerereignisse (WPF .NET)

Während ihrer Lebensdauer durchlaufen alle Objekte in verwaltetem Microsoft .NET-Code die Phasen der Erstellung, der Verwendungund der Zerstörung. Windows Presentation Foundation (WPF) stellt Benachrichtigungen über diese Phasen bereit, wenn sie an einem Objekt auftreten, indem Lebenszyklusereignisse ausgelöst werden. Für WPF-Framework-Ebenenelemente (visuelle Objekte) implementiert WPF die Lebensdauerereignisse Initialized, Loadedund Unloaded. Entwickler können diese Lebenszyklusereignisse als Hooks in Code-Behind-Vorgängen verwenden, die Elemente einbeziehen. In diesem Artikel werden die Lebensdauerereignisse für visuelle Objekte beschrieben, und anschließend werden andere Lebenszeitereignisse vorgestellt, die speziell auf Fensterelemente, Navigationshosts oder Anwendungsobjekte angewendet werden.

Voraussetzungen

In diesem Artikel wird davon ausgegangen, dass Sie Grundkenntnisse darüber haben, wie das WPF-Elementlayout als Baum dargestellt werden kann, und dass Sie Übersicht über Routed Eventsgelesen haben. Um den Beispielen in diesem Artikel zu folgen, hilft es Ihnen, wenn Sie mit Extensible Application Markup Language (XAML) vertraut sind und wissen, wie WPF-Anwendungen geschrieben werden.

Lebensdauerereignisse für visuelle Objekte

WPF-Frameworkebenenelemente werden von FrameworkElement oder FrameworkContentElementabgeleitet. Die lebensdauerbezogenen Ereignisse Initialized, Loadedund Unloaded sind für alle Elemente auf Framework-Ebene in WPF üblich. Das folgende Beispiel zeigt eine Elementstruktur, die in erster Linie in XAML implementiert ist. Der XAML-Code definiert ein übergeordnetes Canvas-Element, das geschachtelte Elemente enthält, die jeweils die XAML-Attributsyntax verwenden, um Initialized, Loadedund Unloaded Lebensdauer-Ereignishandler anzufügen.

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

Eines der XAML-Elemente ist ein benutzerdefiniertes Steuerelement, das von einer Basisklasse abgeleitet wird, die Lebensdauerereignishandler in CodeBehind zuweist.

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.

Die Programmausgabe zeigt die Reihenfolge des Aufrufs von Initialized, Loadedund Unloaded-Lebensdauerereignissen für jedes Baumobjekt an. Diese Ereignisse werden in den folgenden Abschnitten in der Reihenfolge beschrieben, in der sie bei jedem Baumobjekt ausgelöst werden.

Initialisiertes Lebenszyklusereignis

Das WPF-Ereignissystem löst das Initialized-Ereignis an einem Element aus:

  • Wenn die Eigenschaften des Elements festgelegt werden.
  • Ungefähr zur gleichen Zeit, zu der das Objekt durch einen Aufruf seines Konstruktors initialisiert wird.

Einige Eigenschaften von Elementen, z. B. Panel.Children, können untergeordnete Elemente enthalten. Übergeordnete Elemente können die Initialisierung erst melden, wenn ihre untergeordneten Elemente initialisiert werden. Eigenschaftenwerte werden also beginnend mit den tief geschachtelten Elementen in einer Elementstruktur festgelegt, gefolgt von aufeinander folgenden übergeordneten Elementen bis zum Anwendungsstamm. Da das Initialized-Ereignis auftritt, wenn die Eigenschaften eines Elements festgelegt werden, wird dieses Ereignis zunächst für die im Markup definierten, tief geschachtelten Elemente aufgerufen, gefolgt von nacheinander folgenden übergeordneten Elementen bis zum Anwendungsstamm. Wenn Objekte dynamisch in CodeBehind erstellt werden, ist die Initialisierung möglicherweise nicht mehr sequenziert.

Das WPF-Ereignissystem wartet nicht darauf, dass alle Elemente in einem Elementbaum initialisiert werden, bevor das Initialized-Ereignis für ein Element ausgelöst wird. Wenn Sie also einen Initialized-Ereignishandler für ein beliebiges Element schreiben, beachten Sie, dass umgebende Elemente in der logischen oder visuellen Struktur, insbesondere übergeordnete Elemente, möglicherweise noch nicht erstellt worden sein könnten. Oder ihre Membervariablen und Datenbindungen könnten nicht initialisiert sein.

Anmerkung

Wenn das Initialized-Ereignis für ein Element ausgelöst wird, werden die Ausdrucksverwendungen des Elements, z. B. dynamische Ressourcen oder Bindung, nicht ausgewertet.

Geladenes Lebensdauer-Ereignis

Das WPF-Ereignissystem löst das Loaded Ereignis für ein Element aus:

  • Wenn der logische Baum, der das Element enthält, vollständig ist und mit einer Präsentationsquelle verbunden ist. Die Präsentationsquelle stellt das Fensterhandle (HWND) und die Renderingoberfläche bereit.
  • Wenn die Datenbindung an lokale Quellen, z. B. andere Eigenschaften oder direkt definierte Datenquellen, abgeschlossen ist.
  • Nachdem das Layoutsystem alle erforderlichen Werte für das Rendern berechnet hat.
  • Vor dem endgültigen Rendering.

Das Loaded-Ereignis wird für ein Element in einer Elementstruktur erst ausgelöst, wenn alle Elemente innerhalb der logischen Struktur geladen werden. Das WPF-Ereignissystem löst zuerst das Loaded-Ereignis auf dem Wurzelelement einer Elementstruktur aus und dann in jedem nachfolgenden untergeordneten Element bis hin zu den tiefst verschachtelten Elementen. Dieses Ereignis kann zwar einem Tunneling Routingereignis ähneln, aber das Loaded Ereignis enthält keine Ereignisdaten von einem Element in ein anderes, sodass das Markieren des Ereignisses als behandelt keine Auswirkung hat.

Anmerkung

Das WPF-Ereignissystem kann nicht garantieren, dass asynchrone Datenbindungen vor dem Loaded-Ereignis abgeschlossen sind. Asynchrone Datenbindungen binden an externe oder dynamische Quellen.

Entladenes Lebenszeitereignis

Das WPF-Ereignissystem löst das Ereignis Unloaded für ein Element aus:

  • Beim Entfernen der Präsentationsquelle oder
  • Beim Entfernen des visuellen übergeordneten Elements.

Das WPF-Ereignissystem löst zuerst das Unloaded-Ereignis für das Stammelement einer Elementstruktur aus und dann bei jedem aufeinanderfolgenden untergeordneten Element bis hin zu den am tiefsten verschachtelten Elementen. Obwohl dieses Ereignis einem Tunneling Routingereignis ähnelt, gibt das Unloaded Ereignis keine Ereignisdaten von Element an Element weiter, sodass das Markieren des Ereignisses als behandelt keine Auswirkung hat.

Wenn das Unloaded-Ereignis für ein Element ausgelöst wird, ist es übergeordnete Element oder ein element höher in der logischen oder visuellen Struktur bereits nicht festgelegt. Unset bedeutet, dass die Datenbindungen, Ressourcenverweise und Formatvorlagen eines Elements nicht mehr auf ihren normalen oder zuletzt bekannten Ausführungszeitwert festgelegt sind.

Andere Lebenszeitereignisse

Aus Sicht der Lebenszyklusereignisse gibt es vier Haupttypen von WPF-Objekten: Elemente im Allgemeinen, Fensterelemente, Navigationshosts und Anwendungsobjekte. Die Lebensdauerereignisse Initialized, Loadedund Unloaded gelten für alle Elemente auf Frameworkebene. Andere Lebenszeitereignisse gelten speziell für Fensterelemente, Navigationshosts oder Anwendungsobjekte. Informationen zu diesen anderen Lebenszeitereignissen finden Sie unter:

Siehe auch