Cannot use a DependencyObject that belongs to a different thread than its parent Freezable

泪湿孤枕 提交于 2019-12-29 08:53:06

问题


WPF - I'm using BackgroundWorker to create a Model3D object, but when I want to add it to a Model3DGroup that is defined in the XAML, I get exception:

Cannot use a DependencyObject that belongs to a different thread than its parent Freezable.

This is the whole code behind:

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

and this is the whole 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>

回答1:


In your RunWorkerCompleted handler you're adding a GeometryModel3D instance to a Model3DGroup, which was obviously created in a thread other than the UI thread, since the BackgroundWorker.DoWork handler is executed in a separate thread.

In short, WPF does not allow this, as you might have noticed from the exception message. All UI elements, or to be more precise, all DispatcherObject-derived objects in your application must be created in the same thread.

Get an overview of the WPF Threading Model and also see the Remarks section in the BackgroundWorker documentation.

EDIT: you could however create new GeometryModel3D instances by synchronously invoking the Dispatcher of your MainWindow class (without having tested that):

private void bw_DoWork(object sender, DoWorkEventArgs e)   
{   
    e.Result = Dispatcher.Invoke(
       (Func<GeometryModel3D>)(() => new GeometryModel3D()));
}   



回答2:


In my situation, I was creating a new WPF window on a new dispatcher thread.

Everything worked perfectly with a minimal WPF project, but if I ported this same code into our large production WPF codebase, it failed.

The problem was that there was some inherited object that was binding to something on an incorrect thread.

I was able to avoid this error using this in the affected UserControl:

public MyUserControl()
{
    // Discard all inherited styles.
    this.InheritanceBehavior = InheritanceBehavior.SkipAllNow;

    this.InitializeComponent();
}

Now that I have it working, I can find out which style or attached property has a hidden binding to another thread, which is causing this issue.

Update

The issue is that some resources were not frozen. The offending resource is referenced here:

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



回答3:


You can create Model3D geometry in a separate thread. But Freeze it after it is created. The RunWorkerCompleted method can then simply Clone the frozen geometry (although I have failed to get this to work if a Model3D contains a texture).



来源:https://stackoverflow.com/questions/11383174/cannot-use-a-dependencyobject-that-belongs-to-a-different-thread-than-its-parent

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!