不能使用一个属于不同线程的父级Freezable的DependencyObject

7

WPF - 我正在使用BackgroundWorker创建一个Model3D对象,但是当我想将其添加到在XAML中定义的Model3DGroup中时,我会遇到异常:

无法使用属于不同线程而非其父Freezable的DependencyObject。

这是整个代码背后的部分:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        BackgroundWorker bw = new BackgroundWorker();
        bw.DoWork += bw_DoWork;
        bw.RunWorkerCompleted += bw_RunWorkerCompleted;
        bw.RunWorkerAsync();
    }

    private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
    {
        GeometryModel3D geometryModel3D = (GeometryModel3D)e.Result;
        model3DGroup.Children.Add(geometryModel3D);
    }

    private void bw_DoWork(object sender, DoWorkEventArgs e)
    {
        GeometryModel3D geometryModel3D = new GeometryModel3D();
        e.Result = geometryModel3D;
    }
}

以下是整个XAML代码:

    <Grid>
    <Viewport3D Margin="4,4,4,4" Grid.Row="0" Grid.Column="0">
        <ModelVisual3D>
            <ModelVisual3D.Content>
                <Model3DGroup x:Name="model3DGroup">
                </Model3DGroup>
            </ModelVisual3D.Content>
        </ModelVisual3D>
    </Viewport3D>
</Grid>

你有答案了吗? - Clemens
我已经理解了问题所在,但不知道如何解决它。后来我尝试了简单的线程而不是背景工作器...似乎对我有效,但我希望我能用背景工作器完成同样的事情。 - Erez
好的,你在这里简单地描述了你的程序错误,但并没有提出任何问题。我已经解释了出了什么问题,现在就看你是否接受这个答案并尝试解决你的问题,当你遇到更多麻烦时,请再提出问题。否则,人们可能会失去帮助你的乐趣。 - Clemens
看一下我编辑过的回答,或许有所帮助。 - Clemens
你现在得到答案了吗? - Clemens
3个回答

2
在你的RunWorkerCompleted处理程序中,你正在将一个GeometryModel3D实例添加到一个Model3DGroup中,而这个Model3DGroup显然是在与UI线程不同的线程中创建的,因为BackgroundWorker.DoWork处理程序在单独的线程中执行。
简而言之,WPF不允许这样做,正如你可能从异常消息中注意到的那样。所有UI元素,或者更准确地说,应用程序中所有继承自DispatcherObject的对象都必须在同一线程中创建。
了解WPF 线程模型的概述,并查看BackgroundWorker文档中的备注部分。
编辑:但是你可以通过同步调用你的MainWindow类的Dispatcher来创建新的GeometryModel3D实例(尚未测试):
private void bw_DoWork(object sender, DoWorkEventArgs e)   
{   
    e.Result = Dispatcher.Invoke(
       (Func<GeometryModel3D>)(() => new GeometryModel3D()));
}   

2
我有一个类似的问题(相同的错误信息),这是使用dispatcher.Invoke所引起的... - Scott Solmer

1
在我的情况下,我正在新的调度程序线程上创建一个新的WPF窗口。在一个最小的WPF项目中,一切都运行得很完美,但是如果我将相同的代码移植到我们大型生产WPF代码库中,它就会失败。问题在于有一些继承的对象将某些内容绑定到了错误的线程上。我能够通过在受影响的UserControl中使用以下内容来避免此错误:
public MyUserControl()
{
    // Discard all inherited styles.
    this.InheritanceBehavior = InheritanceBehavior.SkipAllNow;

    this.InitializeComponent();
}

现在我已经让它工作了,我可以找出哪些样式或附加属性与另一个线程隐藏绑定,这是导致此问题的原因。

更新

问题在于一些资源没有被冻结。有问题的资源在这里被引用:

ResourceDictionary resources = Application.Current.Resources;
foreach (var resource in resources)
{
   // The offending resource (a scrollbar) is listed here.                
}

0

您可以在单独的线程中创建Model3D几何体。但是,在创建后请将其冻结。然后,RunWorkerCompleted方法可以简单地克隆冻结的几何体(尽管如果Model3D包含纹理,则我未能使其工作)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接