In WPF, is there a "render complete" event?

Viewed 7834

In WPF, When I load a wrap panel with a lot of elements, the window stalls for a while before showing the content. I would like to add a wait hint but I could not find a way to detect when the wrap panel completes rendering.

I have tried "loaded", "sizechanged", "initialized" without success. Has anyone got any idea on this issue ?

Many thanks !

3 Answers

I know at this point this question is a little old, but I just ran into this same issue and found a great work around and figured this could help someone else in the future. At the start of rendering call Dispatcher.BeginInvoke with the Dispatcher Priority of ContextIdle. My start of rendering just happened to be a treeview selected item change event, but this could be any event that you need to wait for the UI to finish updating.

    private void TreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
    {
        Dispatcher.BeginInvoke(new Action(() => DoSomething()), DispatcherPriority.ContextIdle, null);
    }

    private void DoSomething()
    {
       //This will get called after the UI is complete rendering
    }

Yes, use the Window.ContentRendered event.

You can override the OnRender method to detect when rendering is complete.

To push all events call Dispatcher.DoEvents() where DoEvents is implemented as an extension method:

public static class DispatcherExtensions
{
    public static void DoEvents(this Dispatcher dispatcher, DispatcherPriority priority = DispatcherPriority.Background)
    {
        DispatcherFrame frame = new DispatcherFrame();
        DispatcherOperation dispatcherOperation = dispatcher.BeginInvoke(priority, (Action<DispatcherFrame>)ExitFrame, frame);
        Dispatcher.PushFrame(frame);

       if (dispatcherOperation.Status != DispatcherOperationStatus.Completed)
           dispatcherOperation.Abort();
   }

    private static void ExitFrame(DispatcherFrame frame)
    {
        frame.Continue = false;
    }

    public static void Flush(this Dispatcher dispatcher, DispatcherPriority priority)
    {
        dispatcher.Invoke(()=> { }, priority);
    }

}

In retrospect I think it's a terrible idea to use this because it can cause hard to solve bugs.

// this shows how bad it is to call Flush or DoEvents
int clicker = 0;
private void OnClick(object sender, RoutedEventArgs e)
{
    if (clicker != 0)
    {
        // This is reachable... // Could be skipped for DispatcherPriority.Render but then again for render it seems to freeze sometimes.
    }
    clicker++;
    Thread.Sleep(100);
    this.Dispatcher.Flush(DispatcherPriority.Input);

    //this.Dispatcher.DoEvents(DispatcherPriority.Render);
    //this.Dispatcher.DoEvents(DispatcherPriority.Loaded);
    //this.Dispatcher.DoEvents(DispatcherPriority.Input);
    //this.Dispatcher.DoEvents(DispatcherPriority.Background);
    clicker--;
}
Related