Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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>
like image 675
Erez Avatar asked Jul 08 '12 12:07

Erez


2 Answers

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()));
}   
like image 118
Clemens Avatar answered Oct 01 '22 05:10

Clemens


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.                
}
like image 42
Contango Avatar answered Oct 01 '22 05:10

Contango