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

Durante sua vida útil, 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 tempo de vida. 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.

Importante

A documentação do Guia da Área de Trabalho para .NET 7 e .NET 6 está em construção.

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 de nível de 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 que é implementada principalmente em XAML. O XAML define um elemento pai Canvas que contém elementos aninhados, que usam a sintaxe de atributo XAML para anexar Initialized, Loadede Unloaded manipuladores de eventos de tempo de vida.

<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 vida útil em 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 , Loadede 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 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 ao 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. Assim, os valores de propriedade são definidos começando com o(s) elemento(s) mais profundamente aninhado(s) em uma árvore de elementos, seguido por elementos pai sucessivos até a raiz do aplicativo. Como o evento ocorre quando as Initialized propriedades de um elemento são definidas, esse evento é primeiro chamado no(s) elemento(s) mais profundamente aninhado(s), conforme definido na marcação, seguido por elementos pai sucessivos até a raiz do aplicativo. Quando os objetos são criados dinamicamente em code-behind, sua inicialização pode estar fora de sequência.

O sistema de eventos 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, quando você escreve um Initialized manipulador de eventos para qualquer elemento, lembre-se de que os elementos circundantes na árvore lógica ou visual, particularmente 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 WPF gera o Loaded evento em um elemento:

  • Quando a árvore lógica que contém o elemento estiver concluída e conectada a uma fonte de apresentação. A fonte de apresentação fornece o identificador da janela (HWND) e a superfície de renderização.
  • Quando a vinculação de dados a fontes locais, como outras propriedades ou fontes de dados definidas diretamente, estiver concluída.
  • Após o sistema de layout ter 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 WPF primeiro gera o evento no elemento raiz de uma árvore de elementos e, em seguida, em cada elemento filho sucessivo, até o Loaded (s) elemento(s) mais profundamente aninhado(s). Embora esse evento possa se parecer com um evento roteado de encapsulamento, o evento não carrega dados de evento de um elemento para outro, portanto, marcar o Loaded evento como manipulado não tem efeito.

Observação

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

Evento de vida útil descarregado

O sistema de eventos 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 WPF primeiro gera o evento no elemento raiz de uma árvore de elementos e, em seguida, em cada elemento filho sucessivo, até o Unloaded (s) elemento(s) mais profundamente aninhado(s). Embora esse evento possa se parecer com um evento roteado de encapsulamento, o evento não propaga dados de evento de elemento para elemento, portanto, marcar o Unloaded evento como manipulado não tem efeito.

Quando o evento é gerado em um elemento, ele é o Unloaded elemento pai ou qualquer elemento superior 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 para seu valor de tempo de execução normal ou último conhecido.

Outros eventos vitalícios

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 de nível de 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