Eventi di durata degli oggetti (WPF .NET)

Durante la loro durata, tutti gli oggetti nel codice gestito di Microsoft .NET passano attraverso fasi di creazione, uso e distruzione . Windows Presentation Foundation (WPF) fornisce una notifica di queste fasi, man mano che si verificano in un oggetto, generando eventi di durata. Per gli elementi a livello di framework WPF (oggetti visivi), WPF implementa gli Initializedeventi di durata , Loadede Unloaded . Gli sviluppatori possono usare questi eventi di durata come hook per le operazioni code-behind che coinvolgono elementi. Questo articolo descrive gli eventi di durata per gli oggetti visivi e quindi introduce altri eventi di durata che si applicano in modo specifico agli elementi della finestra, agli host di spostamento o agli oggetti applicazione.

Importante

La documentazione di Desktop Guide per .NET 7 e .NET 6 è in fase di costruzione.

Prerequisiti

Questo articolo presuppone una conoscenza di base del modo in cui il layout degli elementi WPF può essere concettualizzato come albero e che è stata letta panoramica degli eventi indirizzati. Per seguire gli esempi in questo articolo, è utile se si ha familiarità con Extensible Application Markup Language (XAML) e si sa come scrivere applicazioni WPF.

Eventi di durata per oggetti visivi

Gli elementi a livello di framework WPF derivano da FrameworkElement o FrameworkContentElement. Gli Initializedeventi di durata , Loadede Unloaded sono comuni a tutti gli elementi a livello di framework WPF. L'esempio seguente mostra un albero degli elementi implementato principalmente in XAML. XAML definisce un elemento padre Canvas che contiene elementi annidati, che usano ogni sintassi dell'attributo XAML per allegare Initializedgestori eventi di durata , Loadede 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>

Uno degli elementi XAML è un controllo personalizzato, che deriva da una classe di base che assegna gestori eventi di durata nel code-behind.

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.

L'output del programma mostra l'ordine di chiamata degli eventi di Initializeddurata , Loadede Unloaded in ogni oggetto albero. Tali eventi sono descritti nelle sezioni seguenti, nell'ordine in cui vengono generati in ogni oggetto albero.

Evento di durata inizializzato

Il sistema di eventi WPF genera l'evento Initialized in un elemento :

  • Quando vengono impostate le proprietà dell'elemento.
  • Nello stesso momento in cui l'oggetto viene inizializzato tramite una chiamata al relativo costruttore.

Alcune proprietà degli elementi, ad esempio Panel.Children, possono contenere elementi figlio. Gli elementi padre non possono segnalare l'inizializzazione finché i relativi elementi figlio non vengono inizializzati. I valori delle proprietà vengono quindi impostati a partire dagli elementi annidati più in profondità in un albero degli elementi, seguiti da elementi padre successivi fino alla radice dell'applicazione. Poiché l'evento Initialized si verifica quando vengono impostate le proprietà di un elemento, tale evento viene innanzitutto richiamato sugli elementi più annidati come definito nel markup, seguito da elementi padre successivi fino alla radice dell'applicazione. Quando gli oggetti vengono creati dinamicamente nel code-behind, l'inizializzazione potrebbe non essere in sequenza.

Il sistema di eventi WPF non attende che tutti gli elementi di un albero degli elementi vengano inizializzati prima di generare l'evento Initialized in un elemento. Pertanto, quando si scrive un Initialized gestore eventi per qualsiasi elemento, tenere presente che gli elementi circostanti nella struttura ad albero logica o visiva, in particolare gli elementi padre, potrebbero non essere stati creati. In alternativa, le variabili membro e i data binding potrebbero non essere inizializzati.

Nota

Quando l'evento Initialized viene generato su un elemento, gli utilizzi delle espressioni dell'elemento, ad esempio risorse dinamiche o binding, non verranno valutati.

Evento di durata caricato

Il sistema di eventi WPF genera l'evento Loaded in un elemento :

  • Quando l'albero logico che contiene l'elemento è completo e connesso a un'origine di presentazione. L'origine della presentazione fornisce l'handle della finestra (HWND) e la superficie di rendering.
  • Quando il data binding a origini locali, ad esempio altre proprietà o origini dati definite direttamente, è completo.
  • Dopo che il sistema di layout ha calcolato tutti i valori necessari per il rendering.
  • Prima del rendering finale.

L'evento Loaded non viene generato su alcun elemento in un albero degli elementi finché non vengono caricati tutti gli elementi all'interno dell'albero logico. Il sistema di eventi WPF genera prima l'evento Loaded sull'elemento radice di un albero degli elementi, quindi su ogni elemento figlio successivo fino agli elementi più annidati. Anche se questo evento potrebbe essere simile a un evento indirizzato di tunneling , l'evento Loaded non trasporta i dati dell'evento da un elemento a un altro, quindi contrassegnare l'evento come gestito non ha alcun effetto.

Nota

Il sistema di eventi WPF non può garantire che i data binding asincroni siano stati completati prima dell'evento Loaded . Associazioni di dati asincrone a origini esterne o dinamiche.

Evento di durata scaricata

Il sistema di eventi WPF genera l'evento Unloaded in un elemento :

  • In caso di rimozione dell'origine della presentazione o
  • Al termine della rimozione dell'elemento padre visivo.

Il sistema di eventi WPF genera prima l'evento Unloaded sull'elemento radice di un albero degli elementi, quindi su ogni elemento figlio successivo fino agli elementi più annidati. Anche se questo evento potrebbe essere simile a un evento indirizzato di tunneling , l'evento Unloaded non propaga i dati dell'evento dall'elemento all'elemento, quindi contrassegnare l'evento come gestito non ha alcun effetto.

Quando l'evento Unloaded viene generato su un elemento, è un elemento padre o qualsiasi elemento superiore nell'albero logico o visivo potrebbe essere già stato annullato. Unset indica che i data binding, i riferimenti alle risorse e gli stili di un elemento non sono più impostati sul valore di runtime normale o dell'ultimo valore di runtime noto.

Altri eventi di durata

Dal punto di vista degli eventi di durata, esistono quattro tipi principali di oggetti WPF: elementi in generale, elementi della finestra, host di spostamento e oggetti applicazione. Gli Initializedeventi di durata , Loadede Unloaded si applicano a tutti gli elementi a livello di framework. Altri eventi di durata si applicano in modo specifico agli elementi della finestra, agli host di spostamento o agli oggetti applicazione. Per informazioni sugli altri eventi di durata, vedere:

Vedi anche