开发者

Return an Object from BackgroundWorker

I am dithering an image in a BackgroundWorker thread and need to update the UI after the image is done processing. The image I need to update exists in the same class as the DitherWorker. How do I pass the BitmapSource so that the noted error does not occur?

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

Problem solved. The BitmapSource must be frozen in the background thread before being passed. Here is the final comented code:

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


Call the Freeze() function on the BitmapSource when you are still on the background thread.

This makes the object immutable, and can therefore be used on the main thread.

Learn here more about freezable wpf objects. The page discusses a lot of other aspects of Freezables, but it also explicitly states: "A frozen Freezable can also be shared across threads, ...". This ability built in WPF to build GUI elements (such as bitmaps) on background threads is the #1 underadvertized WPF feature if you ask me.


BitmapSource.Clone() may be of some use if you want to copy the object.

Dispatcher.CheckAccess() should be called prior to setting image1.Source.

0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜