WPF new window creation on new thread error

1.3k views Asked by At
void itemCommand_Click(Office.CommandBarButton Ctrl, ref bool CancelDefault)
{
    var thread = new Thread(() =>
    {
    if (LoginCheck())
    {
        ItemWindow itw = new ItemWindow(); 
        //Dispatcher.CurrentDispatcher.Invoke((System.Action)(() =>
        //{
              itw.Show();
              itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
        //}));

        Dispatcher.Run();
     }
     });

    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();

}

I keep getting error message of "The calling thread cannot access this object because a different thread owns it." on line "itw.show();" when this function called twice. It works fine for the first call, and after the window is closed and trying to open again, it fails. As I commented out "Invoke" method, it doesn't work with Dispatcher either. Please help me to find the solution. Thank you.

----------------- Edit

The reason why I'm creating a new thread is because it is an Excel addin. I can't create windows from main thread which is the excel that collides with windows if i create them from main thread.
The thing I don't understand is that why does the new instance(ItemWindow) from new thread collide with old thread.

3

There are 3 answers

2
Xavier On

I created a simple test method in a new application that is called when I click the (only) button on my main form. The method looks like this:

private void Button_Click(object sender, RoutedEventArgs e)
{
    Thread thread = new Thread(() =>
    {
        Window1 window = new Window1();
        window.Closed += (s, a) => window.Dispatcher.InvokeShutdown();
        window.Show();
        System.Windows.Threading.Dispatcher.Run();
    });

    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
}

Window1 is a window class I made that has nothing but a single TextBlock on it. I can click that button as many times as I want, and it continues to open new windows with no issues (regardless whether or not I close the previous one first).

I suspect the issue is occurring in code that you are not showing us somewhere. You need to be very careful that nothing on your new thread attempts to access anything UI related from your main thread. Windows running on separate threads cannot communicate with each other unless they go through the dispatcher of the other thread. The exception you are seeing is thrown when any method or property of a DispatcherObject is accessed from a thread other than the one that created the object.

Taking a step back, why is it important that the new window be on its own thread? Unless the new window is going to monopolize the thread, it will probably run fine on the main thread. If you are running some long blocking operation, perhaps that operation alone should be moved to a thread rather than the entire window. I don't know what you are doing exactly, but it is something to think about.


EDIT: Realizing that you may not be running in a typical WPF application (looks like you might be in an Office plugin), I updated my test to launch the windows completely standalone on their own threads. However, I am still able to launch two windows in a row with no issues.

Here is my new test. This method and the test class Window1 are the entirety of my application.

[STAThread]
public static int Main(string[] args)
{
    ThreadStart threadFunc = () =>
    {
        Window1 window = new Window1();
        window.Closed += (s, a) => window.Dispatcher.InvokeShutdown();
        window.Show();
        System.Windows.Threading.Dispatcher.Run();
    };

    Thread thread = new Thread(threadFunc);
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    thread.Join();

    thread = new Thread(threadFunc);
    thread.SetApartmentState(ApartmentState.STA);
    thread.Start();
    thread.Join();

    return 0;
}

So, there appears to be nothing inherently wrong with what you are trying to do, nor do I see any obvious issue in your code. I suspect that there is some invalid cross-thread communication happening somewhere in your custom window while it is being shown. (Either that, or you are running into an issue specific to Office plugins.)

0
CShark On

A possible cause are dependency properties. Dependency properties are a bit picky when it comes to threading.

Even if you don't define your own DepProps, your window will still have some and there is no way to get rid of them.

DepProps have a significant downside: they are thread bound and cannot be accessed from another thread. Which thread holds all the rights is defined by the thread that initializes the DepProps, in your case the first call to new ItemWindow(). After that first call your thread is set and you need that thread to access your DepProps.

For the first window that is no problem but the second one clearly had a different thread. I don't know exactly how DepProps do that, but you might try capturing and restoring the synchronization context of the first thread. Another option would be to capture the dispatcher of the first thread (not the main thread one)

0
J. D. On

You are trying to connect the event handler to the ItemWindow after it is already visible.

You need to switch the order from:

ItemWindow itw = new ItemWindow(); 
itw.Show();
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };

to

ItemWindow itw = new ItemWindow(); 
itw.Closed += (sender2, e2) => { itw.Dispatcher.InvokeShutdown(); };
itw.Show();