Eventos de tempo de vida do objeto (WPF .NET)

Durante seu tempo de vida, todos os objetos no código gerenciado do Microsoft .NET passam por estágios de criação, uso e destruição . O Windows Presentation Foundation (WPF) fornece notificação desses estágios, à medida que ocorrem em um objeto, gerando eventos de tempo de vida. Para elementos de nível de estrutura do WPF (objetos visuais), o WPF implementa os Initializedeventos , Loadede Unloaded lifetime. Os desenvolvedores podem usar esses eventos de tempo de vida como ganchos para operações code-behind que envolvem elementos. Este artigo descreve os eventos de tempo de vida para objetos visuais e, em seguida, apresenta outros eventos de tempo de vida que se aplicam especificamente a elementos de janela, hosts de navegação ou objetos de aplicativo.

Pré-requisitos

Este artigo pressupõe um conhecimento básico de como o layout do elemento WPF pode ser conceituado como uma árvore e que você leu Visão geral de eventos roteados. Para seguir os exemplos neste artigo, é útil se você estiver familiarizado com XAML (Extensible Application Markup Language) e souber como escrever aplicativos WPF.

Eventos de tempo de vida para objetos visuais

Os elementos no nível da estrutura do WPF derivam de FrameworkElement ou FrameworkContentElement. Os Initializedeventos , Loadede Unloaded lifetime são comuns a todos os elementos de nível de estrutura do WPF. O exemplo a seguir mostra uma árvore de elementos implementada principalmente em XAML. O XAML define um elemento pai Canvas que contém elementos aninhados, cada um usando sintaxe de atributo XAML para anexar Initializedmanipuladores de eventos , Loadede vitalícios 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>

Um dos elementos XAML é um controle personalizado, que deriva de uma classe base que atribui manipuladores de eventos de tempo de vida no 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.

A saída do programa mostra a ordem de invocação de , Loaded, e Unloaded eventos de tempo de vida em cada objeto de Initializedárvore. Esses eventos são descritos nas seções a seguir, na ordem em que são gerados em cada objeto de árvore.

Evento de tempo de vida inicializado

O sistema de eventos do WPF gera o Initialized evento em um elemento:

  • Quando as propriedades do elemento são definidas.
  • Ao mesmo tempo em que o objeto é inicializado por meio de uma chamada para seu construtor.

Algumas propriedades de elemento, como Panel.Children, podem conter elementos filho. Os elementos pai não podem relatar a inicialização até que seus elementos filho sejam inicializados. Portanto, os valores de propriedade são definidos começando com o(s) elemento(s) mais profundamente aninhado(s) em uma árvore de elementos, seguidos por elementos pai sucessivos até a raiz do aplicativo. Como o Initialized evento ocorre quando as propriedades de um elemento são definidas, esse evento é invocado primeiro no(s) elemento(s) aninhado(s) mais profundamente, conforme definido na marcação, seguido por elementos pai sucessivos até a raiz do aplicativo. Quando os objetos são criados dinamicamente no code-behind, sua inicialização pode estar fora de sequência.

O sistema de eventos do WPF não espera que todos os elementos em uma árvore de elementos sejam inicializados antes de gerar o Initialized evento em um elemento. Portanto, ao escrever um manipulador de Initialized eventos para qualquer elemento, lembre-se de que os elementos circundantes na árvore lógica ou visual, especialmente os elementos pai, podem não ter sido criados. Ou suas variáveis de membro e associações de dados podem não ser inicializadas.

Observação

Quando o Initialized evento é gerado em um elemento, os usos de expressão do elemento, como recursos dinâmicos ou associação, não serão avaliados.

Evento de vida útil carregado

O sistema de eventos do WPF gera o Loaded evento em um elemento:

  • Quando a árvore lógica que contém o elemento estiver completa e conectada a uma origem de apresentação. A origem da apresentação fornece o identificador de janela (HWND) e a superfície de renderização.
  • Quando a associação de dados a fontes locais, como outras propriedades ou fontes de dados definidas diretamente, é concluída.
  • Depois que o sistema de layout tiver calculado todos os valores necessários para renderização.
  • Antes da renderização final.

O Loaded evento não é gerado em nenhum elemento em uma árvore de elementos até que todos os elementos dentro da árvore lógica sejam carregados. O sistema de eventos do WPF primeiro gera o Loaded evento no elemento raiz de uma árvore de elementos e, em seguida, em cada elemento filho sucessivo até o(s) elemento(s) aninhado(s) mais profundamente. Embora esse evento possa se assemelhar a um evento roteado de túnel, o Loaded evento não transporta dados de um elemento para outro, portanto, marcar o evento como manipulado não tem efeito.

Observação

O sistema de eventos do WPF não pode garantir que as associações de dados assíncronas tenham sido concluídas antes do Loaded evento. As associações de dados assíncronas são associadas a fontes externas ou dinâmicas.

Evento de vida útil descarregado

O sistema de eventos do WPF gera o Unloaded evento em um elemento:

  • Na remoção de sua fonte de apresentação, ou
  • Na remoção de seu pai visual.

O sistema de eventos do WPF primeiro gera o Unloaded evento no elemento raiz de uma árvore de elementos e, em seguida, em cada elemento filho sucessivo até o(s) elemento(s) aninhado(s) mais profundamente. Embora esse evento possa se assemelhar a um evento roteado de túnel, o Unloaded evento não propaga dados de evento de elemento para elemento, portanto, marcar o evento como manipulado não tem efeito.

Quando o Unloaded evento é gerado em um elemento, seu elemento pai ou qualquer elemento mais alto na árvore lógica ou visual pode já ter sido desconfigurado. Unset significa que as associações de dados, referências de recursos e estilos de um elemento não estão mais definidos como seu valor de tempo de execução normal ou último conhecido.

Outros eventos ao longo da vida

Da perspectiva de eventos de tempo de vida, há quatro tipos principais de objetos WPF: elementos em geral, elementos de janela, hosts de navegação e objetos de aplicativo. Os Initializedeventos , Loadede Unloaded lifetime se aplicam a todos os elementos no nível da estrutura. Outros eventos de tempo de vida se aplicam especificamente a elementos de janela, hosts de navegação ou objetos de aplicativo. Para obter informações sobre esses outros eventos vitalícios, consulte:

Confira também