Поделиться через


События жизненного цикла объекта (WPF .NET)

На протяжении их жизненного цикла все объекты в управляемом коде Microsoft .NET проходят этапы создания, использованияи уничтожения. Windows Presentation Foundation (WPF) предоставляет уведомление об этих этапах по мере того, как они происходят для объекта, вызывая события жизненного цикла. Для элементов уровня фреймворка WPF (визуальные объекты) WPF реализует события жизненного цикла Initialized, Loadedи Unloaded. Разработчики могут использовать эти события жизненного цикла в качестве обработчиков для операций заднего кода, которые включают элементы. В этой статье описываются события времени существования визуальных объектов, а затем представлены другие события времени существования, которые специально применяются к элементам окна, узлам навигации или объектам приложений.

Необходимые условия

В этой статье предполагается, что у вас есть основы того, как макет элементов WPF можно концептуально рассматривать как дерево, и что вы прочитали обзор маршрутизированных событий. Чтобы следовать примерам в этой статье, это поможет вам, если вы знакомы с языком разметки расширяемых приложений (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 возникает при установке свойств элемента, сначала оно вызывается в наиболее глубоко вложенных элементах, как это определено в разметке, а затем – в порядке родительских элементов до корневого элемента приложения. Если объекты динамически создаются в code-behind, их инициализация может быть несогласованной.

Система событий WPF не ожидает инициализации всех элементов в дереве элементов, прежде чем вызывать событие Initialized для элемента. Поэтому при написании обработчика событий Initialized для любого элемента следует помнить, что окружающие элементы в логическом или визуальном дереве, особенно родительские элементы, могут не быть созданы. Кроме того, их переменные-члены и привязки данных могут быть неинициализированы.

Заметка

Когда событие Initialized возникает в элементе, использование выражений элемента, например динамических ресурсов или привязки, будет неоценимо.

Загруженное событие времени существования

Система событий WPF вызывает событие Loaded для элемента:

  • Когда логическое дерево, содержащее элемент, будет завершено и подключено к источнику презентации. Источник презентации предоставляет дескриптор окна (HWND) и область отрисовки.
  • Когда привязка данных к локальным источникам, например другим свойствам или непосредственно определенным источникам данных, завершается.
  • После того как система макета вычислила все необходимые значения для отрисовки.
  • Перед окончательной отрисовкой.

Событие Loaded не вызывается ни на одном элементе в дереве элементов, пока не все элементы в логическом дереве загружаются. Система событий WPF сначала вызывает событие Loaded на корневом элементе дерева элементов, а затем на каждом из последующих дочерних элементов вплоть до наиболее глубоко вложенных элементов. Хотя это событие может напоминать туннелирование перенаправленного события , событие Loaded не переносит данные о событии из одного элемента в другой, поэтому обозначение события как обработанного не оказывает никакого эффекта.

Заметка

Система событий WPF не может гарантировать, что асинхронные привязки данных завершены до события Loaded. Асинхронные привязки данных привязываются к внешним или динамическим источникам.

Выгрузленное событие времени существования

Система событий WPF вызывает событие Unloaded для элемента:

  • При удалении источника презентации или
  • При удалении визуального родительского элемента.

Система событий WPF сначала вызывает событие Unloaded корневого элемента дерева элементов, а затем на каждом последующем дочернем элементе вплоть до наиболее глубоко вложенных элементов. Хотя это событие может напоминать туннелирование перенаправленное событие, событие Unloaded не распространяет данные событий от элемента к элементу, поэтому маркировка события как обработанная не влияет.

Когда событие Unloaded возникает в элементе, это родительский элемент или любой элемент выше в логическом или визуальном дереве, возможно, уже не настроены. Unset означает, что привязки данных элемента, ссылки на ресурсы и стили больше не установлены на их обычное значение или на последнее известное значение во время выполнения.

Другие события в течение жизни

С точки зрения жизненного цикла выделяют четыре основных типа объектов WPF: элементы в целом, элементы окна, узлы навигации и объекты приложения. События времени существования Initialized, Loadedи Unloaded применяются ко всем элементам уровня фреймворка. Другие события жизненного цикла применяются специально к элементам окна, узлам навигации или объекту приложения. Дополнительные сведения об этих других событиях жизни см. в следующем разделе:

См. также