Pages

Sunday, September 5, 2010

Threading model in WPF and application resources

A couple of weeks ago my colleague asked me for help with multi-threading in a WPF application. He had an issue with a window created in a new thread. The situation looked like this:
  • A window with few controls was dedicated to show a progress on file downloading from some web storage.
  • The window was created in a new thread.
  • Every time when it was necessary to show the window a new thread was created.
  • First time the window was shown properly.
  • When the window was shown the second time the following exception was fired:
    Exception: System.Windows.Markup.XamlParserException, "Cannot convert the value in attribute 'Background' to object of type 'System.Windows.Media.Brush'. The calling thread cannot access this object because a different thread owns it.  Error at object 'System.Windows.Controls.Border' in markup file 'PresentationFramework.Aero, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, ProcessorArchitecture=MSIL; component/themes/aero.normalcolor.xaml'."
    InnerException: System.InvalidOperationException, "The calling thread cannot access this object because a different thread owns it."
Having these symptoms I supposed that we had a problem with static, global GUI object. I decided so due to the fact that during the first call the object had not been created yet and object creation and initialization went right, where as during the second call, the object had already been created and initialized but in another thread and an attempt to access the object from the different thread failed (more about threading model in WPF applications you can find here). By that time there was the only question to answer "What a static object it was?"

Trying to figure out what was going on in the application and spot that very static object I looked through the code. First I thought that some property and binding to that property might be a reason for the issue but I didn't find anything suspicious. Trying to lessen a complexity of the real application  and reproduce the issue I created a simple application.

App.xaml

<Application x:Class="WpfApplication1.App"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  StartupUri="MainWindow.xaml">
  <Application.Resources>
    <ResourceDictionary>
      <ResourceDictionary.MergedDictionaries>
        <ResourceDictionary Source="/PresentationFramework.Aero,
         Version=3.0.0.0,
         Culture=neutral,
         PublicKeyToken=31bf3856ad364e35;
         component/themes/aero.normalcolor.xaml" />
      </ResourceDictionary.MergedDictionaries>
    </ResourceDictionary>
  </Application.Resources>
</Application>
}

MainWindow.xaml

<Window x:Class="WpfApplication1.MainWindow"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="MainWindow" Height="350" Width="525">
  <Grid>
    <Button Name="button1"
      Content="New Window"
      Height="23"
      HorizontalAlignment="Left"
      Margin="33,43,0,0"
      VerticalAlignment="Top"
      Width="115"
      Click="button1_Click" />
  </Grid>
</Window>

MainWindow.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace WpfApplication1
{
  /// 
  /// Interaction logic for MainWindow.xaml
  /// 
  public partial class MainWindow : Window
  {
    public MainWindow( )
    {
      InitializeComponent( );
    }

    private void button1_Click( object sender, RoutedEventArgs e )
    {
      System.Threading.Thread thread = new System.Threading.Thread(
        new System.Threading.ParameterizedThreadStart( ThreadWork ) );
      thread.SetApartmentState( System.Threading.ApartmentState.STA );
      thread.Start( );
    }

    private void ThreadWork( object state )
    {
      Window1 window = new Window1( );
      window.ShowDialog( );
    }
  }
}

Window1.xaml

<Window x:Class="WpfApplication1.Window1"
  xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
  xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
  Title="Window1" Height="300" Width="300">
  <Grid>
    <ProgressBar Height="10"
      HorizontalAlignment="Left"
      Margin="44,124,0,0"
      Name="progressBar1"
      VerticalAlignment="Top"
      Width="175" />
    <Button Content="Button"
      Height="23"
      HorizontalAlignment="Left"
      Margin="119,166,0,0"
      Name="button1"
      VerticalAlignment="Top"
      Width="75" />
    <CheckBox Content="CheckBox"
      Height="16"
      HorizontalAlignment="Left"
      Margin="22,22,0,0"
      Name="checkBox1"
      VerticalAlignment="Top" />
    <RadioButton Content="RadioButton"
      Height="16"
      HorizontalAlignment="Left"
      Margin="112,22,0,0"
      Name="radioButton1"
      VerticalAlignment="Top" />
    <RadioButton Content="RadioButton"
      Height="16"
      HorizontalAlignment="Left"
      Margin="112,44,0,0"
      Name="radioButton2"
      VerticalAlignment="Top" />
    <TextBlock Height="23"
      HorizontalAlignment="Left"
      Margin="22,44,0,0"
      Name="textBlock1"
      Text="TextBlock"
      VerticalAlignment="Top" />
    <TextBox Height="23"
      HorizontalAlignment="Left"
      Margin="74,66,0,0"
      Name="textBox1"
      VerticalAlignment="Top"
      Width="120" />
  </Grid>
</Window>

Window1.xaml.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;

namespace WpfApplication1
{
  /// 
  /// Interaction logic for Window1.xaml
  /// 
  public partial class Window1 : Window
  {
    public Window1( )
    {
      InitializeComponent( );
    }
  }
}

This application is pretty simple and you can find a lot of similar pieces of code if you search the web.
This code loads Aero styles for controls (see lines from 8 to 12 in App.xaml) and has a button on the main window. On the button click a new thread is started and a new window is created in just started thread. When the window is closed the thread finishes too.
This simple example reproduces the issue.
Experimenting with this code I found out that removing Aero dictionary from App.xaml or removing ProgressBar from Window1.xaml solves the problem.
So it was obvious that styles of controls that were merged with the application dictionary caused the problem (the styles of ProgressBar in particular).

Solution

To solve the problem the resources from PresentationFramework.Aero.dll must be merged with resource dictionary of window but not application.
As application resources can be treated as global variables and all drawbacks of global variables can be attributed to the application resources as well, and the situation becomes even worse due to the fact that UI elements are tightly coupled with the thread which they were created by.

I carried out a little research to compare different objects in application resources and window resources and found out interesting facts.
  1. Brushes defined in application resources are not associated with the thread which they were created by even though they are inherited from DispatcherObject and have Dispatcher property but this property is set to null.
  2. When the same brushes are declared in the window resources, they are associated with the thread which they were created by and their Dispatcher property is set to the same value as the Dispatcher property of the window which they belong to.
  3. Object of types like Border, Button, Rectangle and so on are associated with the creating thread despite the type of resource. Their Dispatcher property is always corresponds to the dispatcher attributed to the thread which they were created by.

Conclusion

To sum up everything mentioned above, application resources are just one more type of global variables and can cause serious problems in the application which has many UI threads - use window resources instead of application resources.
Any static variables in custom controls, styles or templates can lead to bad consequences as well - try to avoid them when it is possible.
Don't forget about sigletons, they also have all cons of global variables.
 
Powered by Blogger