从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;
        }
    }

从BackgroundWorker返回一个对象

BitmapSource上调用Freeze()函数,此时您还在后台线程上。

这使得对象不可变,因此可以在主线程中使用。

在这里了解更多关于可冻结wpf对象的信息。该页面讨论了Freezables的许多其他方面,但它也明确指出:"冻结的Freezable也可以跨线程共享,……"。如果你问我的话,我认为WPF内置的在后台线程上构建GUI元素(比如位图)的能力是最被低估的WPF特性。

BitmapSource.Clone()可能有一些用途,如果你想复制对象。

Dispatcher.CheckAccess()应该在设置image1.Source之前调用。

相关文章: