WPF: Running code when Window rendering is completed

Posted by Ilya Verbitskiy on Geeks with Blogs See other posts from Geeks with Blogs or by Ilya Verbitskiy
Published on Tue, 16 Oct 2012 19:34:53 GMT Indexed on 2012/10/22 11:02 UTC
Read the original article Hit count: 338

Filed under:

WPF is full of surprises. It makes complicated tasks easier, but at the same time overcomplicates easy  task as well. A good example of such overcomplicated things is how to run code when you’re sure that window rendering is completed. Window Loaded event does not always work, because controls might be still rendered. I had this issue working with Infragistics XamDockManager. It continued rendering widgets even when the Window Loaded event had been raised. Unfortunately there is not any “official” solution for this problem.

But there is a trick. You can execute your code asynchronously using Dispatcher class.

 

Dispatcher.BeginInvoke(new Action(() => Trace.WriteLine("DONE!", "Rendering")), DispatcherPriority.ContextIdle, null);

 

This code should be added to your Window Loaded event handler. It is executed when all controls inside your window are rendered.

I created a small application to prove this idea. The application has one window with a few buttons. Each button logs when it has changed its actual size. It also logs when Window Loaded event is raised, and, finally, when rendering is completed.

Window’s layout is straightforward.

 

   1:  <Window x:Class="OnRendered.MainWindow"
   2:          xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:          xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:          Title="Run the code when window rendering is completed." Height="350" Width="525"
   5:          Loaded="OnWindowLoaded">
   6:      <Window.Resources>
   7:          <Style TargetType="{x:Type Button}">
   8:              <Setter Property="Padding" Value="7" />
   9:              <Setter Property="Margin" Value="5" />
  10:              <Setter Property="HorizontalAlignment" Value="Center" />
  11:              <Setter Property="VerticalAlignment" Value="Center" />
  12:          </Style>
  13:      </Window.Resources>
  14:      <StackPanel>
  15:          <Button x:Name="Button1" Content="Button 1" SizeChanged="OnSizeChanged" />
  16:          <Button x:Name="Button2" Content="Button 2" SizeChanged="OnSizeChanged" />
  17:          <Button x:Name="Button3" Content="Button 3" SizeChanged="OnSizeChanged" />
  18:          <Button x:Name="Button4" Content="Button 4" SizeChanged="OnSizeChanged" />
  19:          <Button x:Name="Button5" Content="Button 5" SizeChanged="OnSizeChanged" />
  20:      </StackPanel>
  21:  </Window>

 

SizeChanged event handler simply traces that the event has happened.

 

   1:  private void OnSizeChanged(object sender, SizeChangedEventArgs e)
   2:  {
   3:      Button button = (Button)sender;
   4:      Trace.WriteLine("Size has been changed", button.Name);
   5:  }

 

Window Loaded event handler is slightly more interesting. First it scheduler the code to be executed using Dispatcher class, and then logs the event.

 

   1:  private void OnWindowLoaded(object sender, RoutedEventArgs e)
   2:  {
   3:      Dispatcher.BeginInvoke(new Action(() => Trace.WriteLine("DONE!", "Rendering")), DispatcherPriority.ContextIdle, null);
   4:      Trace.WriteLine("Loaded", "Window");
   5:  }

 

As the result I had seen these trace messages.

 

   1:  Button5: Size has been changed
   2:  Button4: Size has been changed
   3:  Button3: Size has been changed
   4:  Button2: Size has been changed
   5:  Button1: Size has been changed
   6:  Window: Loaded
   7:  Rendering: DONE!

 

You can find the solution in GitHub.

© Geeks with Blogs or respective owner