从BackgroundWorker返回一个对象
本文关键字:一个对象 返回 BackgroundWorker | 更新日期: 2023-09-27 18:05:20
我在BackgroundWorker线程中抖动图像,需要在图像完成处理后更新UI。我需要更新的图像与DitherWorker存在于同一个类中。我如何传递BitmapSource,使注意到的错误不会发生?
public void DitherWorker()
{
double scalebox = Double.Parse(myWindow.scaleBox.Text);
int slider = (int)myWindow.convolutionBiasSlider.Value;
BitmapSource final = null;
ditherobj output = new ditherobj(scalebox, originalImage, slider);//.Get_Halftone();
worker = new BackgroundWorker();
worker.WorkerSupportsCancellation = true;
worker.DoWork += delegate(object s, System.ComponentModel.DoWorkEventArgs args)
{
ditherobj dob = (ditherobj)args.Argument;
Binarize bn = new Binarize(dob.scalebox, dob.localbms, dob.slider);
BitmapSource bms = (BitmapSource)bn.Get_Halftone();
final = bms;
args.Result = new ditherobj(0,null,0,bms);
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
ditherobj dob = (ditherobj)args.Result;
image1.Source = dob.localbms; //ERROR. The calling thread cannot access this object because another thread owns it
myWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
myWindow.activityBar.IsBusy = false;
}));
};
worker.RunWorkerAsync((ditherobj)output);
}
public class ditherobj
{
public double scalebox;
public BitmapSource localbms;
public BitmapImage localbmi;
public int slider;
public ditherobj(double scalebox, BitmapImage localbmi, int slider, BitmapSource bms = null)
{
this.scalebox = scalebox;
this.slider = slider;
if (bms == null)
{
BitmapImage test = localbmi.Clone();
localbms = (BitmapSource)test;
}
else
localbms = bms;
}
}
问题解决了。BitmapSource在被传递之前必须在后台线程中被冻结。下面是最后的注释代码:
BackgroundWorker worker;
public void DitherWorker()
{
double scalebox = Double.Parse(myWindow.scaleBox.Text); //get values from UI for the job
int slider = (int)myWindow.convolutionBiasSlider.Value;
DitherSettings output = new DitherSettings(scalebox, originalImage, slider); //create holder object to be passed to BackgroundWorker
worker = new BackgroundWorker();
worker.DoWork += delegate(object s, System.ComponentModel.DoWorkEventArgs args)
{
DitherSettings ds = (DitherSettings)args.Argument; //cast argument as our holder object
Binarize bn = new Binarize(ds.scalebox, ds.localbms, ds.slider); //create object to do our work
BitmapSource bms = (BitmapSource)bn.Get_Halftone(); //do work
bms.Freeze(); //freeze resulting BitmapSource so it can be utilized elsewhere
args.Result = new DitherSettings(0,null,0,bms); //create new object with resulting BitmapSource
};
worker.RunWorkerCompleted += delegate(object s, RunWorkerCompletedEventArgs args)
{
DitherSettings ds = (DitherSettings)args.Result; //get object with our BitmapSource
if (image1.Dispatcher.CheckAccess())
this.image1.Source = ds.localbms; //update class image
myWindow.Dispatcher.Invoke(DispatcherPriority.Normal, new ThreadStart(() =>
{
myWindow.activityBar.IsBusy = false; //Update UI control
}));
};
worker.RunWorkerAsync((DitherSettings)output);
}
public class DitherSettings
{
public double scalebox;
public BitmapSource localbms;
public BitmapImage localbmi;
public int slider;
public DitherSettings(double scalebox, BitmapImage localbmi, int slider, BitmapSource bms = null)
{
this.scalebox = scalebox;
this.slider = slider;
if (bms == null)
localbms = (BitmapSource)localbmi;
else
localbms = bms;
}
}
在BitmapSource
上调用Freeze()
函数,此时您还在后台线程上。
这使得对象不可变,因此可以在主线程中使用。
在这里了解更多关于可冻结wpf对象的信息。该页面讨论了Freezables的许多其他方面,但它也明确指出:"冻结的Freezable也可以跨线程共享,……"。如果你问我的话,我认为WPF内置的在后台线程上构建GUI元素(比如位图)的能力是最被低估的WPF特性。
BitmapSource.Clone()
可能有一些用途,如果你想复制对象。
Dispatcher.CheckAccess()
应该在设置image1.Source之前调用。