Launching a WPF Window in a Separate Thread, Part 1

Typically, I strongly recommend keeping the user interface within an application’s main thread, and using multiple threads to move the actual “work” into background threads.  However, there are rare times when creating a separate, dedicated thread for a Window can be beneficial.  This is even acknowledged in the MSDN samples, such as the Multiple Windows, Multiple Threads* sample.  However, doing this correctly is difficult.  Even the referenced MSDN sample has major flaws, and will fail horribly in certain scenarios.  To ease this, I wrote a small class that alleviates some of the difficulties involved.

The MSDN Multiple Windows, Multiple Threads Sample* showed how to launch a new thread with a WPF Window, and will work in most cases.  The sample code (commented and slightly modified) works out to the following:

// Create a thread
Thread newWindowThread = new Thread(new ThreadStart( () =>
{
    // Create and show the Window
    Window1 tempWindow = new Window1();
    tempWindow.Show();
    // Start the Dispatcher Processing
    System.Windows.Threading.Dispatcher.Run();
}));
// Set the apartment state
newWindowThread.SetApartmentState(ApartmentState.STA);
// Make the thread a background thread
newWindowThread.IsBackground = true;
// Start the thread
newWindowThread.Start();

This sample creates a thread, marks it as single threaded apartment state, and starts the Dispatcher on that thread. That is the minimum requirements to get a Window displaying and handling messages correctly, but, unfortunately, has some serious flaws.

*Note: The referenced sample is no longer available as of 2013. A new Multithreaded Web Browser Sample shows a similar technique and code

The first issue – the created thread will run continuously until the application shuts down, given the code in the sample.  The problem is that the ThreadStart delegate used ends with running the Dispatcher.  However, nothing ever stops the Dispatcher processing.  The thread was created as a Background thread, which prevents it from keeping the application alive, but the Dispatcher will continue to pump dispatcher frames until the application shuts down.

In order to fix this, we need to call Dispatcher.InvokeShutdown after the Window is closed.  This would require modifying the above sample to subscribe to the Window’s Closed event, and, at that point, shutdown the Dispatcher:

// Create a thread
Thread newWindowThread = new Thread(new ThreadStart( () =>
{
    Window1 tempWindow = new Window1();
    // When the window closes, shut down the dispatcher
    tempWindow.Closed += (s,e) => 
       Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);

    tempWindow.Show();
    // Start the Dispatcher Processing
    System.Windows.Threading.Dispatcher.Run();
}));
// Setup and start thread as before

This eliminates the first issue.  Now, when the Window is closed, the new thread’s Dispatcher will shut itself down, which in turn will cause the thread to complete.

The above code will work correctly for most situations.  However, there is still a potential problem which could arise depending on the content of the Window1 class.  This is particularly nasty, as the code could easily work for most windows, but fail on others.

The problem is, at the point where the Window is constructed, there is no active SynchronizationContext.  This is unlikely to be a problem in most cases, but is an absolute requirement if there is code within the constructor of Window1 which relies on a context being in place.

While this sounds like an edge case, it’s fairly common.  For example, if a BackgroundWorker is started within the constructor, or a TaskScheduler is built using TaskScheduler.FromCurrentSynchronizationContext() with the expectation of synchronizing work to the UI thread, an exception will be raised at some point.  Both of these classes rely on the existence of a proper context being installed to SynchronizationContext.Current, which happens automatically, but not until Dispatcher.Run is called.  In the above case, SynchronizationContext.Current will return null during the Window’s construction, which can cause exceptions to occur or unexpected behavior.

Luckily, this is fairly easy to correct.  We need to do three things, in order, prior to creating our Window:

  • Create and initialize the Dispatcher for the new thread manually
  • Create a synchronization context for the thread which uses the Dispatcher
  • Install the synchronization context

Creating the Dispatcher is quite simple – The Dispatcher.CurrentDispatcher property gets the current thread’s Dispatcher and “creates a new Dispatcher if one is not already associated with the thread.”  Once we have the correct Dispatcher, we can create a SynchronizationContext which uses the dispatcher by creating a DispatcherSynchronizationContext.  Finally, this synchronization context can be installed as the current thread’s context via SynchronizationContext.SetSynchronizationContext.  These three steps can easily be added to the above via a single line of code:

// Create a thread
Thread newWindowThread = new Thread(new ThreadStart( () =>
{
    // Create our context, and install it:
    SynchronizationContext.SetSynchronizationContext(
        new DispatcherSynchronizationContext(
            Dispatcher.CurrentDispatcher));

    Window1 tempWindow = new Window1();
    // When the window closes, shut down the dispatcher
    tempWindow.Closed += (s,e) => 
       Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);

    tempWindow.Show();
    // Start the Dispatcher Processing
    System.Windows.Threading.Dispatcher.Run();
}));
// Setup and start thread as before

This now forces the synchronization context to be in place before the Window is created and correctly shuts down the Dispatcher when the window closes.

However, there are quite a few steps.  In my next post, I’ll show how to make this operation more reusable by creating a class with a far simpler API…

About Reed
Reed Copsey, Jr. - http://www.reedcopsey.com - http://twitter.com/ReedCopsey

Comments

22 Responses to “Launching a WPF Window in a Separate Thread, Part 1”
  1. Josh says:

    Great article Reed. Very informative and detailed. I’m anxiously awaiting part 2.

    Thanks,
    Josh

  2. Robert says:

    You made my day, thanks a million for the background information!

    Cheers
    Robert.

  3. elshev says:

    Thanks Reed.

    But what about modal windows? I can’t write
    tempWindow.ShowDialog();
    System.Windows.Threading.Dispatcher.Run();

    But it works good without Dispatcher.Run();

    Should I write something like this:
    tempWindow.Loaded += (s, e) => Dispatcher.Run();
    tempWindow.ShowDialog();
    (which doesn’t work)?
    Or for modal windows it’s not necessary to write
    Dispatcher.Run();
    and
    Dispatcher.CurrentDispatcher.BeginInvokeShutdown(DispatcherPriority.Background);

    • Reed says:

      Typically, you wouldn’t use a Modal dialog in its own thread – if you want modal behavior, you’d use it in the UI thread.

      • DaveD says:

        Hi Reed, great article, thanks for offering up your expertise!

        The reason I’m using ShowDialog() is I put a try/catch around it as a very handy way of catching unhandled exceptions.

        So, I have this same question: Is the Dispatcher.Run() necessary for ShowModal()?

        (FYI – I need to run my UI in a separate thread because it is very large and changes often and is therefore bug-prone. The main window runs machine automation. The end result is that the UI can crash but the main window continues the machine automation.)

        • DaveD says:

          UPDATE –
          When I wrote the above I didn’t realize that processing stops at the Dispatcher.Run() , so I can wrap my try/catch around it.

          IMPORTANTLY I found that although ShowModal() seems to work every bit as well, I found in testing that randomly – sometimes after hours or days of operation – I’d get some weird error like protected memory violation or something like that (I forget exactly). This error disappeared after going back to Dispatcher.Run(). So this is more testimony that the method presented here by Reed is best!

  4. Amokk says:

    InvalidOperationException. Hate you :( Not STAThread.

  5. Gennady Vanin (Novosibirsk) says:

    Hello, Reed,
    your “Multiple Windows, Multiple Threads sample” link does not really point to used in article sample.

    It points to MSDN “Threading Model” (.NET 4.5) which has the section with such title and respective snippets
    This article has the only ref to one-window “Single-Threaded Application with Long-Running Calculation Sample” only.
    So, I downloaded few times that one-window sample

    Only if to switch the version of .NET 3.0 or 3.5 of this article, the section “Multiple Windows, Multiple Threads” has a reference to used by you sample.
    Though, it is called “Multithreading Web Browser Sample” and has quite different link:
    http://msdn.microsoft.com/en-us/library/ms771272%28v=vs.90%29.aspx

    Probably it would have been better to give more correct/direct references and titles…

    • Reed says:

      Gennady,

      Thank you for the updated info. When I wrote this article (in late 2011), the link and title was correct. A recent MSDN update for VS 2012 has broken it.

      I’ve updated the article to link to the web browser sample, and noted that the original sample is no longer available.

      -Reed

  6. Jan S. says:

    Hello,

    how can i close Window prgrammatically? It allways says that Thread is not Thread that owns Window?! How can i do that?

    Thanks

    • Jan S. says:

      Just abort Thread wouldn´t be the correct way isn´t it?

    • Reed says:

      If you’re inside the Window’s thread, that should happen. If you’re trying to close it from “outside”, you’ll need to use theWindow.Dispatcher.BeginInvoke to marshal the call to close back onto the Window’s Dispatcher/thread.

      • Jan S. says:

        Thanks for your answer,

        in relation to your example that should look like tempWindow.Dispatcher.BeginInvoke(new Action(() => tempWindow.Close())); ?

        Cheers

        • Reed says:

          Yes, though you’d also need to declare tempWindow outside of the thread’s lambda (since you’re accessing it from outside of the thread to close it).

  7. winzi says:

    i am showing waiting icon to user in wpf app. For showing wait icon, i create a new thread and then load the icon usercontrol in that thread. If i use Dispatcher.Run() in the thread delegate then app crashes after some clicks. Reason is obvious that Dispatcher processing is not getting shut down but my view is there is no need of Dispatcher.Run in that case. What do you suggest?

    • Reed says:

      If you’re using this technique just to show a waiting icon, it sounds more like you need to just move your work into a background thread, and use the icon on the main UI thread. That’s the standard, preferred pattern.

  8. Chris W says:

    Hi, thanks for the article. I was wondering what if you use styles from a ResourceDictionary defined in App.xaml? It says that “The calling thread cannot access this object because a different thread owns it.” when I’m trying to use Style=”{StaticResource UserControlFontStyle}” in a Window in the new Thread.

    • Reed says:

      This isn’t going to work. The application plumbing is tied to the main application class, which is going to be on the main thread. In general, your styles would need to be explicitly handled within this Window instead.

  9. Ciberguille says:

    Hi, good article if he could see this http://stackoverflow.com/questions/23297133/launching-a-wpf-window-in-a-separate-thread, and how I could after the code executed hide this window

  10. Kevin says:

    I tried a search, and it doesn’t look like there is a part 2. Too bad, this was a great article.

Trackbacks

Check out what others are saying about this post...
  1. […] implemented my solution following this example by – Reed […]



Speak Your Mind

Tell us what you're thinking...
and oh, if you want a pic to show with your comment, go get a gravatar!