2012-01-22 37 views
8

我有一个选项窗口和一个基于这些选项和Kinect数据显示颜色的窗口。到目前为止,一切都在一个线程上(据我所知,我没有做过任何线程)。如何在新线程上打开窗口?

现在,我添加了一个选项来打开一个查看器窗口,该窗口需要以尽可能低的延迟进行更新。所有这一切都需要是创建一个窗口,并示出它:

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

当这个事件触发时,颜色窗口停止显示的颜色(即,触发该主线程上的30倍的第二事件停止击发),但观看者显示完美。我希望查看器和颜色窗口都被更新。

从阅读其他问题,这听起来像解决方案是在一个新的线程创建查看器。不过,我遇到了很多问题。

该火灾时,我点击按钮,打开浏览器:

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(); 

    } 

不管,如果我只是叫viewer.Show()或调用(的),它上面的,行会抛出异常:无法使用属于与其父级Freezable不同的线程的DependencyObject。下面是我理解Invoke()的方法:它访问查看器的调度程序,该调度程序知道对象在哪个线程上运行,然后可以从该线程调用方法。

我应该试图把这个查看器放在一个新的线程上吗?问题甚至是线程问题吗?用户不会与观众交互。

任何人都知道为什么这不起作用?谢谢您的帮助。

回答

15

你需要调用Show()相同的线程上窗户被创建 - 这就是为什么你会收到错误。然后,您还需要启动一个新的Dispatcher实例,以使运行时管理该窗口。

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(); 
} 

见的多窗口/多线程例子:http://msdn.microsoft.com/en-us/library/ms741870.aspx

+0

我看到Invoke作为保证Show()在浏览器的线程上被调用。即使在viewer.Show()周围没有Invoke(),我仍然会得到Freezable异常。 –

+0

@ michael.greenwald然后在'SkeletalViewer.MainWindow()'中有一些东西导致异常。我构建了一个空的WPF项目,它完全符合我上面显示的内容,并且它毫无例外地运行。也许你的问题类似于这个问题的问题:http://stackoverflow.com/questions/3636761/how-to-debug-this-error-when-none-of-my-code-shows-up-in-堆栈 – shf301

+0

以及如何从'launchViewerThread'关闭那个窗口? –

0

我不确定这是否可以解决您的问题,但是您可以尝试创建一个在另一个线程上执行的线程处理程序(打开查看器窗口),然后使用dispatcher.beginInvoke更新主窗口,

下面是一些代码 -

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 
    } 

有了这个,你可以更新主窗口的控制,不会冻结它,让我知道如果您有任何疑问

+0

谢谢。为什么要将颜色派发到UI线程?是不是在UI线程(默认线程)上调用任何调用的方法? –

+0

,因为从主UI线程分离出的后台线程无法更新在UI线程上创建的任何控件的内容。为了后台线程访问主窗口上的控制,后台线程必须将工作委托给与UI线程关联的分派器。这是通过使用Invoke或BeginInvoke完成的。 Invoke是同步的,Be​​ginInvoke是异步的。该操作将在指定的DispatcherPriority中添加到Dispatcher的事件队列中。 – DotNetUser

+0

看到这个链接 - http://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.begininvoke.aspx – DotNetUser

1

所以我也陷入了类似的问题,即一个新的窗口没有一个新的线程打开。例外是“不能使用属于不同线程的依赖对象”。

问题最终导致窗口正在使用全局资源(背景画笔)。一旦我冻结画笔资源,窗口加载就好了。