次の方法で共有


The Loaded event and the Initialized event

In WPF, controls have both a Loaded even and an Initialized event. Initializing and loading a control tend to happen at about the same time, and consequently these events fire at roughly the same time. But they have slightly – though important – different meanings, and the differences can be a source of confusion. So here’s some background on how we designed these events. (And this doesn’t just apply to the Control class in particular, but to the FrameworkElement and FrameworkContentElement classes in general.)

 

Here’s the short story:

· The Initialized event says just that an element has been created and its properties have all been set, and as a consequence this usually fires on children before their parent. So when Initialized is raised on an element, its whole sub-tree is likely initialized, but its parent is not. The Initialized event is typically fired when the Xaml for a sub-tree is loaded. This event corresponds to the IsInitialized property.

· The Loaded event says that the tree is not only built and initialized, but layout has run on it, data has been bound, it's connected to a rendering surface (window), and you're on the verge of being rendered. When we reach that point, we canvas the tree by broadcasting the Loaded event, starting at the root. This event corresponds to the IsLoaded property.

 

If you’re not sure which event to use, and you don’t want to read any more, use the Loaded event; it’s more often the right choice.

 

And here’s the whole story …

Initialized Event

The Initialized event typically fires when the properties of an element have all been set. Specifically, FrameworkElement/FrameworkContentElement implement ISupportInitialize, and when the EndInit method of that interface is called, the IsInitialized property is set to true, and the Initialized event is fired.

 

ISupportInitialize has existed since before WPF, and exists so that when you’re about to set several properties on a control, you can tell it ahead of time that you’re about to set a batch (BeginInit), and tell it afterwards that you’re done (EndInit). An object that implements ISupportInitialize is allowed to defer property change handling until after the EndInit is called. So in WPF, elements use this to trigger the Initialized event and IsInitialized property. Other objects in WPF implement ISupportInitialize too, such as DataSourceProvider.

 

The interesting thing is seeing when EndInit gets called. The primary place it’s called is by the Xaml loader (if you’re familiar with Baml, it’s called by the Baml loader too.) The Xaml loader calls BeginInit right after it creates an object (i.e. after the start tag), and calls EndInit right after finishing with the Xml tree rooted at that tag (i.e. after the end tag). For example, in this Xaml:

 

<Button Width="100">

  Hello

</Button>

 

… the Button will be created, BeginInit will be called, the Width property will be set, the Content property will be set (to “Hello”), and then EndInit will be called.

 

If you’re creating elements from code, you can call BeginInit/EndInit too, just like the Xaml loader does. That begs a question, though – if you don’t create elements using Xaml, and you don’t call ISupportInitialize, does the Initialized event ever fire? We knew that the answer to that had to be yes, so we added support to raise it and set IsInitialized on a couple of other cues:

·         When an un-Initialized element is added to a Visual tree, the Initialized event is raised.
That works well for all elements except the root element. But the root element is ultimately connected to a PresentationSource to get rendered. So …

·         When an un-Initialized element is connected to a PresentationSource (a window), the Initialized event is similarly raised.

 

By the nature of the definition of Initialized, it typically fires “bottom-up”. That is, a parent shouldn’t be considered Initialized until the child is Initialized, therefore Initialized typically fires on all children elements before their parent. There’s no guarantee of this, though, because anyone can call ISupportInitialize. But when you’re loading from Xaml, it’s pretty much assured.

 

There's one other interesting thing about what elements do with the Initialized event; elements don’t get their implicit styles until it is raised. For example, the Button1 element below gets a blue Background property from the style, but before the button fires Initialized, its Background is null:

 

<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" >

  <Page.Resources>

    <Style TargetType="Button">

      <Setter Property="Background" Value="Blue" />

    </Style>

  </Page.Resources>

  <Button Name="Button1">Clack</Button>

</Page>

Loaded Event

The Loaded event fires when an element is not only initialized, but it is about to be rendered. The motivation for the Loaded event is the typical scenario where you want to do some initialization in your application at load-time.

 

The Initialized event is nice for this purpose too, because it means that an element has been created and its properties have been set. But it misses some things. For example, you may want to know the ActualWidth of an element, but when Initialized is fired, the ActualWidth value hasn’t been calculated yet. Or you may want to look at the value of a data-bound property, but that hasn’t been calculated yet either.

 

To deal with this, we initially implemented the Loaded event so that it would fire just after the window was rendered, but before any input was processed. We figured that if it was ready enough for input, it was ready enough for load-time initialization. But then we started to trigger animations off of the Loaded event, and saw the problem; for a split second you’d see the content render without the animation, then you’d see the animation start. You might not always notice it, but it was especially noticeable when you run the app remotely.

 

So we moved Loaded so that it now fires after layout and data binding have had a chance to run, but just before the first render. (And note that if you do anything in your Loaded event handler that invalidates layout, it might be necessary to re-run it before rendering.)

 

Since a whole element tree goes Loaded at the same time, the event is essentially broadcast throughout the tree. This broadcast starts at the root, and as a consequence the Loaded event fires on a parent before its children. So whereas the Initialized event usually goes “bottom-up”, firing on children before the parent, the Loaded event goes “top-down”, firing on a parent before its children.

The Property is the Chicken, the Event is the Egg

One last interesting question for both of these events is – do you set the property and raise the event, or raise the event and set the property? Conceptually they both happen at the same time, but in reality it has to be one before the other.

 

There might be some exceptions, but in general across WPF, when there's a property and a corresponding property-changed event for it, the property is set before the event is fired. For example, on a ListBox, the SelectedItem property has been updated by the time the SelectionChanged event is raised. So Initialized/IsInitialized and Loaded/IsLoaded follow this same pattern.

 

The unique thing about the Loaded event and the IsLoaded property, is that the IsLoaded property is updated throughout the element tree before the Loaded event is fired on any of the elements. That is, all the elements in a tree get their IsLoaded property set to true, and then all the elements in the tree get their Loaded event raised.

 

So putting these together, looking at the above example of a Button inside a Page, if you load it from Xaml the sequence you should see is:

  • Button.IsInitialized goes true
  • Button.Initialized event is raised
  • Page.IsInitialized goes true
  • Page.Initialized event is raised
  • Page IsLoaded goes to true
  • Button IsLoaded goes to true
  • Page.Loaded is raised
  • Button.Loaded is raised

Comments

  • Anonymous
    October 21, 2007
    Nice article... If I may add the Loaded Event is also raised when ever a UI Element is loaded and unloaded. Such a case would be if you have a button named 'x' in Tab 'A', when the user switches to Tab 'B' an UnLoaded event is raised for x. When the user switches back to Tab A an Loaded event is raised for x, but not an Initialized event.... This is another difference between them

  • Anonymous
    April 19, 2008
    In the .Net Framework 3.0 and later, when objects are created by loading XAML, we check to see if they

  • Anonymous
    July 10, 2008
    Hi Mike! I have found your article while looking for a solution on the following problem. The thing is I need to track the performance of a WPF control, in other words measure its rendering time. In WinForms there is a Paint to which I can subscribed and to read time required on rendering. Unfortunately, I could not find an equivalent in WPF. Probably, you might know something about it? It would be nice if you could answer this question, cause I have met a lot of similar problems on Microsoft WPF forums. Thanks! P.S. I will periodically check your blog, thus you may answer here. For any case I have an account karik007 at hotmail dot com server

  • Anonymous
    September 22, 2011
    Important note, if you perform any task in the Initilise or Loaded that allows continued event processing you can lose the Loaded event (it will not reach the event handler you set) and any state changes to the window state property applied after such will not be honoured. Therefore, it is not advisable to have the initilise launch (for example) a modal sub-window.

  • Anonymous
    March 29, 2015
    useful article, but links are broken