在我写的WPF应用程序中,我有一个TransformedBitmap属性,它绑定到UI上的Image对象。每当我更改此属性时,图像都会更新(从而更新显示在屏幕上的图像)。为了防止UI在我检索下一个图像时冻结或无响应,我正在尝试使用BackgroundWorker进行快照检索:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
e.Result = this.snapshotHelper.GetSnapshot(ImageFormat.Bmp);
}
然后,在我的RunWorkerCompleted方法中,我有以下内容:
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
this.CurrentImage = (TransformedBitmap)e.Result;
....
}
这似乎工作正常,直到我更新CurrentImage属性时用于告诉Image对象更新的NotifyPropertyChanged方法;我遇到了一个跨线程的错误。
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));
}
}
我真的不知道如何改变现状或做些不同的事情来解决这个错误。在过去的几个小时里,我一直在阅读有关BackgroundWorkers的内容,在我看来,我应该能够在RunWorkerCompleted方法中设置CurrentImage。至少从我能说的来看。任何有关这方面的帮助将不胜感激!谢谢!
答案 0 :(得分:2)
控件(Image)只能在创建它的Thread上更改。基本上发生的事情是你的后台线程改变你对象的属性,然后它会触发PropertyChanged事件,然后由WPF使用,然后WPF尝试修改Image控件(记住我们仍然在整个链中的BackgroundThread上)事件)。
幸运的是,修复非常简单:
private void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
myWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new Action(delegate()
{
this.CurrentImage = (TransformedBitmap)e.Result;
....
});
}
您需要对Window或Control的引用才能生效,但这实际上将委托排队,以便在UI线程而不是后台线程上运行。
答案 1 :(得分:2)
Dispatcher.Invoke((Action<TransformedBitmap>) (obj => this.CurrentImage = obj), e.Result as TransformedBitmap);
这应该有用......
<强>更新强>
在您的情况下,您使用的是freezable对象,并且您在创建的位图中的问题需要在UI线程中发送之前冻结。所以DoWork将如下:
void worker_DoWork(object sender, DoWorkEventArgs e)
{
var bmp = snapshotHelper.GetSnapshot(ImageFormat.Bmp);
bmp.Freeze();
e.Result = bmp;
}
然后在RunWorkerCompleted中更新我上面写的属性。
答案 2 :(得分:0)
只允许调度程序与UI进行交互。看看这里:
http://www.jeff.wilcox.name/2010/04/propertychangedbase-crossthread/