What is a parent freezable? What does this error

2019-04-21 07:27发布

问题:

I'm getting this error:

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

What does that even mean? Is it in English? Is the parent frozen, or is it just freezable? Any way to make a parent not freezable, if it makes the error go away?

What's happening:

I have two opengl winforms controls in a WPF app, and so far, everything's been working smoothly (I think). Now, I've added an update so that when one winform control updates the image, the other should as well. That actually used to work, and now I'm getting that error. Stepping through the code has the crash happen in random places, which leads me to believe that it's a garbage collection fault (ie, some update in another thread is creating something that's getting garbage collected, and that collection happens at a random time).

The exception is caught in the main run method, and it's an InvalidOperationException.

I'm grasping at straws here. Where do I start?

EDIT: It looks like the call that's causing the problem is this one:

        if (imagePanel.InvokeRequired)
        {
            imagePanel.Invoke(new System.Windows.Forms.MethodInvoker(delegate{
                imagePanel.ClearImages();
            }));
        }
        else
        {
            imagePanel.ClearImages();
        }

I'm still tracking it down; if that series of lines is commented out, the crash still happens, and the thread status has a 'just ended' thread (hence the garbage collection assumption).

回答1:

OK, I've figured it out. Ordinarily, I'd just delete this question, but it was a pain to find any information about how to fix this.

The problem was a call that looked like this:

ImageBrush theBrush = new ImageBrush(new Bitmap(new Uri(...)));

if (labelStatus.Dispatcher.Thread == System.Threading.Thread.CurrentThread) {
    button.background = theBrush;
}
else {
   labelStatus.Dispatcher.BeginInvoke((System.Threading.ThreadStart)(delegate {
    button.background = theBrush;
   }));
}

But! if you do that, then the dispatcher works, and then it tries to delete the brush, but the brush itself is also apparently getting deleted in another location.

So, the take-home lesson is, if you declare an ImageBrush, then delete it in the same thread, like so:

void MyFunc(){
     ImageBrush theBrush = new ImageBrush(new Bitmap(new Uri(...)));
     button.background = theBrush;
}

if (labelStatus.Dispatcher.Thread == System.Threading.Thread.CurrentThread) {
    MyFunc();
}
else {
   labelStatus.Dispatcher.BeginInvoke((System.Threading.ThreadStart)(delegate {
       MyFunc();
   }));
}