从BackgroundWorker返回一个对象

发布于 2024-11-29 05:55:49 字数 4074 浏览 0 评论 0原文

我正在 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;
        }
    }

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

独守阴晴ぅ圆缺 2024-12-06 05:55:49

当您仍在后台线程时,调用 BitmapSource 上的 Freeze() 函数。

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

在此处了解有关可冻结 wpf 对象的更多信息。 该页面讨论了许多其他内容Freezable 的各个方面,但它也明确指出:“冻结的 Freezable 也可以跨线程共享,......”。如果您问我,WPF 中内置的在后台线程上构建 GUI 元素(例如位图)的功能是排名第一的未充分宣传的 WPF 功能。

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.

夜吻♂芭芘 2024-12-06 05:55:49

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

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

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

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文