Ilya Verbitskiy

Thoughts and links about computer programming
posts - 16 , comments - 242 , trackbacks - 0

Tuesday, October 16, 2012

WPF: Running code when Window rendering is completed

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.

Posted On Tuesday, October 16, 2012 9:34 PM | Comments (2) | Filed Under [ WPF ]

Powered by: