воскресенье, 7 марта 2010 г.

События жизненного цикла приложения WPF

Недавно у меня возникла необходимость узнать, в каком порядке вызываются методы и события элементов управления WPF. Так сказать, узнать жизненный цикл приложения. Эта информация может пригодиться тем, кто разрабатывает собственные элементы управления. Для этого я создал небольшую программу, содержащую окно и два вложенных друг в друга элемента управления. XAML-код главного окна прост:

<Window x:Class="WpfEvents.Window"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:my="clr-namespace:WpfEvents"

Title="{Binding WindowTitle}" Height="300" Width="300"

Loaded="OnLoadedHandler" Initialized="OnInitializedHandler"

Activated="OnActivatedHandler" LayoutUpdated="OnLayoutUpdatedHandler"

SizeChanged="OnSizeChangedHandler" SourceUpdated="OnSourceUpdatedHandler"

TargetUpdated="OnTargetUpdatedHandler" Deactivated="OnDeactivatedHandler"

GotFocus="OnGotFocusHandler" LostFocus="OnLostFocusHandler" Unloaded="OnUnloadedHandler">

<StackPanel Orientation="Vertical">

<my:MyUserControl/>

<TextBlock Text="{Binding ControlText}"/>

StackPanel>

Window>

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

Все обработчики событий просто сообщают о себе:

private void OnLayoutUpdatedHandler(object sender, EventArgs e)

{

Trace.WriteLine("Window.LayoutUpdated event handler.");

}

Так же ряд виртуальных методов был переопределен следующим образом:

protected override void OnTemplateChanged(ControlTemplate oldTemplate, ControlTemplate newTemplate)

{

Trace.WriteLine("Window.OnTemplateChanged before base.");

base.OnTemplateChanged(oldTemplate, newTemplate);

Trace.WriteLine("Window.OnTemplateChanged after base.");

}

Перейдем к полученным результатам.

Методы класса Application оказывается вызываются еще до того, как вызывается конструктор главного окна:

Application.OnStartup begin.

Application.Startup event handler.

Application.OnStartup end.

Window.Constructor before InitializeComponent.

Поэтому здесь вы можете изменить свойство StartupUri. Последовательность событий окна при его конструировании следующая:

Window.Constructor before InitializeComponent.

Getting Window.WindowTitle property for binding.

Getting Window.ControlText property for binding.

Window.OnInitialized before base.

Window.OnTemplateChanged before base.

Window.OnTemplateChanged after base.

Window.Initialized event handler.

Window.OnInitialized after base.

Window.Constructor after InitializeComponent.

Window.OnApplyTemplate before base.

Window.OnApplyTemplate after base.

Window.MeasureOverride before base.

Window.MeasureOverride after base.

Window.ArrangeOverride before base.

Window.ArrangeOverride after base.

Window.OnRender before base.

Window.OnRender after base.

Window.SizeChanged event handler.

Window.LayoutUpdated event handler.

Window.OnSourceInitialized before base.

Window.OnSourceInitialized after base.

Window.OnActivated before base.

Window.Activated event handler.

Window.OnActivated after base.

Window.MeasureOverride before base.

Window.MeasureOverride after base.

Window.ArrangeOverride before base.

Window.ArrangeOverride after base.

Window.LayoutUpdated event handler.

Window.Loaded event handler.

Конструктор вызывает метод InitializeComponent. Этот метод осуществляет привязку (binding) и вызывает OnInitialized, который изменяет шаблон окна, приводя к вызову метода OnTemplateChanged.

После того, как конструктор отработал вызывается метод OnApplyTemplate что говорит о том, что шаблон был применен к окну.

Далее идет измерение и упорядычивание элементов управления MeasureOverride и ArrangeOverride. После чего вызывается метод OnRender, поскольку имеются изменения, которые нужно отрисовать. После этого вызывается события SizeChanged и LayoutUpdated.

Метод OnSourceInitialized отвечает за создание класса, обеспечивающего взаимодействие окна Win32 с WPF.

Далее окно активируется, о чем говорит вызов метода OnActivated и события Activated.

После этого идет еще один цикл вычисления размеров: MeasureOverride, ArrangeOverride и LayoutUpdated.

И в самом конце вызывается событие Loaded.

События, возникающие у окна при активации (переходе в активное состояние) просты:

Window.OnActivated before base.

Window.Activated event handler.

Window.OnActivated after base.

Аналогично при деактивации окна происходят следующие события:

Window.OnDeactivated before base.

Window.Deactivated event handler.

Window.OnDeactivated after base.

Осталось только узнать, как события окна связаны с событиями его дочерних элементов. Вот полный листинг событий при создании окна:

Window.Constructor before InitializeComponent.

Getting Window.WindowTitle property for binding.

UserControl.Constructor before InitializeComponent.

Getting UserControl property for binding.

UserControl2.Constructor before InitializeComponent.

Getting UserControl2 property for binding.

UserControl2.OnInitialized before base.

UserControl2.OnTemplateChanged before base.

UserControl2.OnTemplateChanged after base.

UserControl2.Initialized event handler.

UserControl2.OnInitialized after base.

UserControl2.Constructor after InitializeComponent.

UserControl.OnInitialized before base.

UserControl.OnTemplateChanged before base.

UserControl.OnTemplateChanged after base.

UserControl.Initialized event handler.

UserControl.OnInitialized after base.

UserControl.Constructor after InitializeComponent.

Getting Window.ControlText property for binding.

Window.OnInitialized before base.

Window.OnTemplateChanged before base.

Window.OnTemplateChanged after base.

Window.Initialized event handler.

Window.OnInitialized after base.

Window.Constructor after InitializeComponent.

Window.OnApplyTemplate before base.

Window.OnApplyTemplate after base.

Window.MeasureOverride before base.

UserControl.OnApplyTemplate before base.

UserControl.OnApplyTemplate after base.

UserControl.MeasureOverride before base.

UserControl2.OnApplyTemplate before base.

UserControl2.OnApplyTemplate after base.

UserControl2.MeasureOverride before base.

UserControl2.MeasureOverride after base.

UserControl.MeasureOverride after base.

Window.MeasureOverride after base.

Window.ArrangeOverride before base.

UserControl.ArrangeOverride before base.

UserControl2.ArrangeOverride before base.

UserControl2.ArrangeOverride after base.

UserControl2.OnRender before base.

UserControl2.OnRender after base.

UserControl.ArrangeOverride after base.

UserControl.OnRender before base.

UserControl.OnRender after base.

Window.ArrangeOverride after base.

Window.OnRender before base.

Window.OnRender after base.

Window.SizeChanged event handler.

UserControl.SizeChanged event handler.

UserControl2.SizeChanged event handler.

UserControl2.LayoutUpdated event handler.

UserControl.LayoutUpdated event handler.

Window.LayoutUpdated event handler.

Window.OnSourceInitialized before base.

Window.OnSourceInitialized after base.

Window.OnActivated before base.

Window.Activated event handler.

Window.OnActivated after base.

Window.MeasureOverride before base.

Window.MeasureOverride after base.

Window.ArrangeOverride before base.

Window.ArrangeOverride after base.

UserControl2.LayoutUpdated event handler.

UserControl.LayoutUpdated event handler.

Window.LayoutUpdated event handler.

Window.Loaded event handler.

UserControl.Loaded event handler.

UserControl2.Loaded event handler.

Какие выводы можно отсуда сделать?

  • Конструкторы родителей вызывают конструкторы вложенных элементов. К моменту появления события Initialized все дочерние элементы уже проинициализированы, привязка данных произошла. Отметим, что привязка данных происходит при создании элементов в том порядке, в каком они размещены в дереве элементов.
  • При изменении размеров метод MeasureOverride вызывает аналогичные методы вложенных элементов. Т.е. этот метод вложенного элемента заканчивает работу раньше, чем у родительского.
  • Метод ArrangeOverride так же вызывает ArrangeOverride у вложенных элементов, а так же и метод OnRender. Т.е. вложенные элементы отрисовывают себя раньше родительских.
  • Событие SizeChanged происходит до события LayoutUpdated.
  • События SizeChanged распространяются от родителя к вложенным элементам, а LayoutUpdated - наоборот, от вложенного элемента к родителям.
  • Событие Loaded сначала срабатывает у родителя (так же как и SizeChanged).

А вот какие события происходят при изменении размеров окна:

Window.MeasureOverride before base.

UserControl.MeasureOverride before base.

UserControl2.MeasureOverride before base.

UserControl2.MeasureOverride after base.

UserControl.MeasureOverride after base.

Window.MeasureOverride after base.

Window.ArrangeOverride before base.

UserControl.ArrangeOverride before base.

UserControl2.ArrangeOverride before base.

UserControl2.ArrangeOverride after base.

UserControl2.OnRender before base.

UserControl2.OnRender after base.

UserControl.ArrangeOverride after base.

UserControl.OnRender before base.

UserControl.OnRender after base.

Window.ArrangeOverride after base.

Window.OnRender before base.

Window.OnRender after base.

Window.SizeChanged event handler.

UserControl.SizeChanged event handler.

UserControl2.SizeChanged event handler.

UserControl2.LayoutUpdated event handler.

UserControl.LayoutUpdated event handler.

Window.LayoutUpdated event handler.

События Unloaded при закрытии главного окна приложения не возникали. При удалении же элемента из окна события возникают в следующем порядке:

Window.MeasureOverride before base.

Window.MeasureOverride after base.

Window.ArrangeOverride before base.

Window.ArrangeOverride after base.

UserControl2.LayoutUpdated event handler.

UserControl.LayoutUpdated event handler.

Window.LayoutUpdated event handler.

UserControl.Unloaded event handler.

UserControl2.Unloaded event handler.


При добавлении элемента в окно события идут в следующем порядке:

Window.MeasureOverride before base.

Window.MeasureOverride after base.

Window.ArrangeOverride before base.

UserControl.ArrangeOverride before base.

UserControl2.ArrangeOverride before base.

UserControl2.ArrangeOverride after base.

UserControl2.OnRender before base.

UserControl2.OnRender after base.

UserControl.ArrangeOverride after base.

UserControl.OnRender before base.

UserControl.OnRender after base.

Window.ArrangeOverride after base.

UserControl2.LayoutUpdated event handler.

UserControl.LayoutUpdated event handler.

Window.LayoutUpdated event handler.

UserControl.Loaded event handler.

UserControl2.Loaded event handler.