WPF - LayoutUpdated event firing repeatedly

Drew Noakes picture Drew Noakes · Oct 16, 2009 · Viewed 11.3k times · Source

I've been adding a bit of animation to my WPF application.

Thanks to Dan Crevier's unique solution to animating the children of a panel combined with the awesome WPF Penner animations it turned out to be fairly straightforward to make one of my controls look great and have its children move about with some nice animation.

Unfortunately this all comes with a performance overhead. I'm happy to have the performance hit when items are added/removed or the control is resized, but it seems that this perf hit occurs consistently throughout the application's lifetime, even when items are completely static.

The PanelLayoutAnimator class uses an attached property to hook the UIElement.LayoutUpdated event. When this event fires, render transforms are animated to cause the children to glide to their new positions.

Unfortunately it seems that the LayoutUpdated event fires every second or so, even when nothing is happening in the application (at least I don't think my code's doing anything -- the app doesn't have focus and the mouse is steady.) As the reason for the event is not immediately apparent to the event handler, all children of the control have to be reevaluated. This event is being called about once a second when idle. The frequency increases when actually using the app.

So my question is, how can I improve the performance here? Any answer that assists would be appreciated, but I'm currently stuck on these sub-questions:

  1. What causes the LayoutUpdated event to fire so frequently? Is this supposed to happen, and if not, how can I find out why it's firing and curtail it?

  2. Is there a more convenient way within the handler to know whether something has happened that might have moved children? If so, I could bail out early and avoid the overhead of looping each child.

For now I will work around this issue by disabling animation when there are more than N children in the panel.

Answer

Chris picture Chris · Jul 16, 2012

If you're updating the UI from the EventHandler you attached to the LayoutUpdated event, this will also trigger that event!

For example:

void my_LayoutUpdatedEvent(object sender, EventArgs e)
{
    textBlock1.Text = "changed";
    Console.Out.WriteLine("text changed!");
}
    

will go into an infinite loop of updates.

Perhaps you need to do something like this:

void my_BetterLayoutUpdatedEvent(object sender, EventArgs e)
{
    if (!hasChanged)
        textBlock1.Text = "changed";
    hasChanged = true;
    Console.Out.WriteLine("text changed!");
}