开发者

Updating an Image UI property from a BackgroundWorker thread

In a WPF application I'm writing, I have a TransformedBitmap property which is bound to an Image object on the UI. Whenever I change this property, the Image is updated (and thus the image being displayed to the screen is updated). In order to prevent the UI from freezing or becoming unresponsive whilst I retrieve the next image, I'm attempting to the snapshot retrieval with a BackgroundWorker like this:

private void bw_DoWork(object sender, DoWorkEventArgs e)
{
  e.Result = this.snapshotHelper.GetSnapshot(ImageFormat.Bmp);
}

then, in my RunWorkerCompleted method, 开发者_如何学GoI have the following:

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  this.CurrentImage = (TransformedBitmap)e.Result;
  ....
}

This seems to work okay until on the NotifyPropertyChanged method used to tell the Image object to update when I update the CurrentImage property; I get a cross-thread error.

public event PropertyChangedEventHandler PropertyChanged;
private void NotifyPropertyChanged(String info)
{
  if (PropertyChanged != null)
  {
  //The following causes a "the calling thread cannot access this object because a different thread owns it" error!
    PropertyChanged(this, new PropertyChangedEventArgs(info));      
  }
}

I really don't know how to change things around or what to do differently to get around this error. I've been reading about BackgroundWorkers for the past couple of hours and it seems to me that I should be able to set CurrentImage fine in the RunWorkerCompleted method; at least from what I can tell. Any help on this would be greatly appreciated! Thanks!


Dispatcher.Invoke((Action<TransformedBitmap>) (obj => this.CurrentImage = obj), e.Result as TransformedBitmap);

This should work...

Update

In your case you are using freezable object, and the problem is in the bitmap that you have created need to be freezed before sending it in UI thread. So you DoWork will be as follows:

void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            var bmp = snapshotHelper.GetSnapshot(ImageFormat.Bmp);
            bmp.Freeze();
            e.Result = bmp;            
        }

Then in RunWorkerCompleted you update the property as I wrote above.


A control (Image) can only be changed on the Thread which created it. So essentially what happens is your background thread changes the property on your object, which in turn fires the PropertyChanged event, which is then consumed by WPF, which then attempts to modify the Image control (remember we're still on the BackgroundThread throughout this chain of events).

Luckily, the fix is pretty simple:

private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
  myWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate() 
     {
        this.CurrentImage = (TransformedBitmap)e.Result;
        ....
     });
}

You will need a reference to your Window or Control for this to work, but this essentially queues up the delegate to run on the UI thread instead of the background thread.


Only dispatcher is allowed to interact with the UI. Have a look here:

http://www.jeff.wilcox.name/2010/04/propertychangedbase-crossthread/

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜