Erez
Erez

Reputation: 6446

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>

Upvotes: 8

Views: 9099

Answers (3)

Contango
Contango

Reputation: 80372

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

Upvotes: 2

user4531448
user4531448

Reputation: 34

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).

Upvotes: 0

Clemens
Clemens

Reputation: 128084

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

Upvotes: 2

Related Questions