views:

46

answers:

3

Hi All

I have the following code, which runs a WPF window on it's own dedicated UI thread:

// Create the dedicated UI thread for AddEditPair window
Thread addEditPairThread = new Thread(() =>
{
    // Initialise the add edit pair window
    addEditPair = new AddEditPair(this);
    addEditPair.PairRecordAdded += new EventHandler<PairRecordEventArgs>(addEditPair_PairRecordAdded);
    addEditPair.PairRecordEdited += new EventHandler<PairRecordEventArgs>(addEditPair_PairRecordEdited);

    // Force AddEditPair to run on own UI thread
    System.Windows.Threading.Dispatcher.Run();
});
addEditPairThread.IsBackground = true;
addEditPairThread.Name = "AddEditPair";
addEditPairThread.SetApartmentState(ApartmentState.STA);
addEditPairThread.Start();

This works great except when i try to set the Owner of this window, to a window that runs on the main Ui thread.

The exception i get is:

The calling thread cannot access this object because a different thread owns it.

I understand what the error means and why it happens, so then i implemented the following:

// If invoke is not required - direct call
if (addEditPair.Dispatcher.CheckAccess())
    method();
// Else if invoke is required - invoke
else
    addEditPair.Dispatcher.BeginInvoke(dispatcherPriority, method);

But i still get the same error. Now i'm confused!

Any ideas anyone? Any help would be appreciated.

A: 

Trying to set one window to be the parent of a window on another thread is not possible in WPF without the windows being frozen (which I'm not sure is possible) as each of the windows will not be able to access the other ones data.

Is there a good reason to create the windows on seperate threads? Mostly you should be fine creating windows all on the same ui threads and using background workers for processing long running taks.

mattythomas2000
The AddEditPair window contains a ListView which is updated constantly - 1 to 10 updates per second. This was causing the Main UI thread to freeze. After moving this to a separate dedicated UI thread - everything works beautifully.
c0D3l0g1
+1  A: 

Why are you creating a windows in a separate thread?

I assume you are doing it because the window executes long running code or needs to be accessed by long running code, if this is true than the window will be non-responsive when the long running code is running (this is bad and can even freeze your entire system in some circumstances, but let's ignore this for a moment) - and you are doing the whole threading thing to keep the main window responsive while the second window is frozen.

This is not supported by WPF - but even if it was supported it wouldn't have worked - Windows links the message queues of windows that are "related" to each other (it's the only way to keep the behavior of the system predictable) so the non responsive window will not process messages and that will block the queue and that in turn would prevent the owner window from receiving messages making it also non-responsive.

There is a reason most programs have just one UI thread - the logic is simple:

  • the only reason to run something in a different thread is if you have two or more long running or blocking operations and you don't want them to block each other.

  • A window that executes long running or blocking operation will be non-responsive, that WILL affect other windows in the same app (even if they are on different threads) and might destabilize the entire system - so we don't want that.

  • So me must not execute blocking or long running operations from a window but use a background thread.

  • If a window does not execute long running or blocking operations it will not block the thread and therefor can run on the same thread with other well behaved windows without any problems.

  • And since windows on the same thread will not interfere with each others and multi-threading adds complexity - there is no reason to ever have more then one UI thread.

Note: Only one UI thread that is actually showing UI, it's perfectly fine to have background threads using WPF that will never open a window (example: create a large FixedDocument in the background) I don't call those UI threads, also I didn't say anything about the number of background threads.

Nir
A: 

Some kind of validation seems to be happening on the Owner property of a window, which checks if the windows were created on the same thread.

My solution to overcome this, is to implement my own property of type Main Window and store a reference to this from the constructor, as follows:

private MainWindow _main;

public AddEditPair(MainWindow main)
    : base(false)
{
    InitializeComponent();

    // Initialise local variables
    _main = main;
}

public MainWindow Main
{
    get
    {
        return _main;
    }
}

Now i have access to the main window.

c0D3l0g1