How do I open a window on a new thread?

2019-03-19 19:11发布

问题:

I have a options window and a window that displays color based on these options and Kinect data. So far everything's on one thread (as far as I know; I haven't done any threading).

Now, I'm adding an option to open a viewer window that will need to be updated with lowest possible latency. All this entails is creating a window and showing it:

viewer = new SkeletalViewer.MainWindow();
viewer.Show();

When this event fires, the color window stops displaying colors (i.e. the event that fires 30 times a second on the main thread stops firing), but the viewer is displayed perfectly. I want the viewer and the color window to both be updated.

From reading other questions, it sounds like the solution is to create the viewer on a new thread. I'm encountering a lot of problems with this, though.

This fires when I click the button to open the viewer:

private void launchViewerThread_Click(object sender, RoutedEventArgs e)
    {
        Thread viewerThread = new Thread(delegate()
        {
            viewer = new SkeletalViewer.MainWindow();
            viewer.Dispatcher.Invoke(new Action(delegate() 
                {
                    viewer.Show();
                }));
        });

        viewerThread.SetApartmentState(ApartmentState.STA); // needs to be STA or throws exception
        viewerThread.Start();

    }

Regardless of if I just call viewer.Show() or Invoke() it as above, the line throws an exception: Cannot use a DependencyObject that belongs to a different thread than its parent Freezable. Here's how I understand Invoke(): it accesses viewer's dispatcher, which knows what thread the object is running on, and can then call methods from that thread.

Should I be trying to put this viewer on a new thread? Is the problem even a question of threads? The user will not be interacting with the viewer.

Anyone know why this doesn't work? Thanks for the help.

回答1:

You need to call Show() on the same thread that the window is created on - that's why you are getting the error. Then you also need to start a new Dispatcher instance to get the runtime to manage the window.

private void launchViewerThread_Click(object sender, RoutedEventArgs e)
{
    Thread viewerThread = new Thread(delegate()
    {
        viewer = new SkeletalViewer.MainWindow();
        viewer.Show();
        System.Windows.Threading.Dispatcher.Run();
    });

    viewerThread.SetApartmentState(ApartmentState.STA); // needs to be STA or throws exception
    viewerThread.Start();
}

See the Multiple Windows/Multiple Threads example at: http://msdn.microsoft.com/en-us/library/ms741870.aspx



回答2:

So I was running into a similar issue where a new window failed to open on a new thread. The exception was "cannot use a dependencyobject that belongs to a different thread".

The issue ended up being that the window was using a global resource (Background brush). Once I froze the brush resource, the window loaded just fine.



回答3:

I am not sure if this will solve your problem but can you try creating a thread proc (to open a viewer window) which is executed on a different thread and then have a dispatcher.beginInvoke to update the main window ,

Here is some code-

    in the constructor register this
    public MainWindow()
    { 
       UpdateColorDelegate += UpdateColorMethod;
    } 

    // delegate and event to update color on mainwindow 
    public delegate void UpdateColorDelegate(string colorname);
    public event UpdateColorDelegate updateMainWindow;

    // launches a thread to show viewer
    private void launchViewerThread_Click(object sender, RoutedEventArgs e)
    {
        Thread t = new Thread(this.ThreadProc);
        t.Start();
    }

    // thread proc
    public void ThreadProc()
    {
       // code for viewer window
       ...
       // if you want to access any main window elements then just call DispatchToMainThread method
       DispatchToUiThread(color);   
    } 

    // 
    private void DispatchToUiThread(string color)
    {
      if (updateMainWindow != null)
      {
        object[] param = new object[1] { color};
        Dispatcher.BeginInvoke(updateMainWindow, param);
      }
    }

    // update the mainwindow control's from this method
    private void UpdateColorMethod(string colorName)
    {
       // change control or do whatever with main window controls
    } 

With this you can update the main window controls without freezing it, Let me know if you have any questions