不能使用属于与其父Freezable - prism不同的线程的DependencyObject

时间:2012-10-15 23:33:40

标签: c# wpf multithreading prism sta

我在开发WPF应用程序时遇到了一个问题。 该应用程序基于Prism。 应用程序使用prism bootstraper启动 在加载任何Window之前,应用程序在不同的线程(STA)上打开模式对话框, 然后加载一堆东西(服务等) 该对话框在此期间打开,并允许通知用户应用程序启动过程的进度(使用事件聚合器传递更新)。 加载完成后,bootstraper将关闭对话框并打开主应用程序窗口。 到目前为止一直很好...... 然后在关闭应用程序时,同样的事情正在发生。 关闭主窗口,打开一个对话框(再次在新的STA线程上),以允许通知。 但现在,当点击ShowDialog调用(发生在新的STA线程内)时, 引发异常: “不能使用属于与其父Freezable不同的线程的DependencyObject”。 经过长时间的长时间调试后,我发现异常的原因是窗口的背景,它是从应用程序级别的合并字典中获取的画笔/图像(在wpf UI线程上实例化)。 如果在没有ResouceDictionary的情况下加载图像 - 一切顺利。


摘要: 只有在使用resourceDictionary并且仅在第二次调用新的STA线程时才会观察到异常,而新的STA线程又会加载DialogBox并在调用ShowDialog时引发异常 如果只有一个对话框(例如,在引导时没有对话框,并且只在关闭过程中有对话框),则不会发生异常。


我的问题是: 是什么原因造成的?在这种情况下,这个例外究竟意味着什么 (我知道一般来说,有一些UI线程更新形成其他线程,但后来我不明白为什么这只发生在dialgo +线程的第二个实例上)。

谢谢:)

2 个答案:

答案 0 :(得分:2)

正如您所正确提到的,您的背景对象是在主UI线程上创建的。你的背景实际上是一个Brush对象,而Brush是一个DependencyObject。

创建DependencyObject时,它“依赖”它创建的STA线程。因此,与其他依赖项对象一样,它只能在自己的线程上使用。这意味着STA和旧COM对象模型的兼容性。

因此,当您尝试在其他STA线程上使用它时,您会得到一个适当的异常。

P.S我对定义为资源的图像存在同样的问题。

答案 1 :(得分:1)

我遇到了类似的问题。我不确定你是如何实现背景的。我可以试着解释一下我的情况,也许你可以从中得到一些东西。我创建了自己的基础窗口,我们称之为MyWindow,它继承自Window。即

public class MyWindow : Window
{
}

我的目的是应用来自应用程序资源字典的动态资源的背景。

我第一次回答这个问题

public class MyWindow : Window
    {
        public MyWindow()
        {
            this.SetResourceReference(BackgroundProperty, "MyResourceKey");
        }
    }

现在,当资源是一种固定颜色时,这对我有效。

<SolidColorBrush x:Key="MyResourceKey" Color="White"/>

我发现当我将资源引用设置为系统颜色时,我会得到你遇到的问题。

<SolidColorBrush x:Key="MyResourceKey"  Color="{DynamicResource {x:Static SystemColors.WindowColorKey}}"/>    

它会第一次工作但第二次我得到父可冻结错误。所以我最初的想法是,哦,这是一个线程问题,我只需要调用调度程序。现在这是我陷入困境的地方因为我认为我需要在窗口上调用它。不对。您需要在该资源的依赖项对象上调用它 问题。您没有使用SetResourceReference的对象,因为它查找资源并创建对它的引用。所以我们需要的是实际的依赖对象。要从资源中获取对象,您可以执行此操作。

object temp = this.TryFindResource("MyResourceKey");

现在你有了对象,但这需要是一个依赖对象。我没试过这个,但你可以做到这一点

DependencyObject temp = (DependencyObjet)this.TryFindResource("MyResourceKey"); 

现在你有了依赖对象!这就是导致我们使用freezable父级的线程问题的原因。现在我们在这个对象上调用调度程序。我最终得到了类似的东西。这对我有用,但我可能会尝试清理一下。

public class MyWindow: Window
    {
        public MyWindow()
        {
            SetResources();                
        }

        private void SetResources()
        {
            DependencyObject dependencyObject;
            object temp;

                temp = this.TryFindResource("MyResourceKey");

                if (temp != null)
                {
                    if (temp is DependencyObject)
                    {
                        dependencyObject = (DependencyObject)temp;
                        if (!dependencyObject.CheckAccess())
                        {
                            dependencyObject.Dispatcher.BeginInvoke(new System.Action(() => { this.SetResources(); }));
                        }
                        else
                        {
                            this.SetValue(BackgroundProperty, temp);                            
                        }
                    }                   
                }
          }                  
    }

现在这只是设置背景属性。我相信这对于一种风格应该是一样的。所以你可以做到

this.SetValue(StyleProperty, temp)

稍微弄清楚了。但是一旦我开始工作,我就兴奋不已。看起来我们的资源正在使用的依赖对象遇到了线程问题因此它第一次加载而不是第二次加载。第一次它在正确的线程上,但在某个地方另一个线程被启动。然而要想出这个。如果有人有更好的解决方案,我很乐意看到它。