Вернуть объект из BackgroundWorker - PullRequest
2 голосов
/ 13 августа 2011

Я сморщил изображение в потоке BackgroundWorker, и мне нужно обновить пользовательский интерфейс после того, как изображение будет обработано. Изображение, которое мне нужно обновить, существует в том же классе, что и 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;
        }
    }

Ответы [ 2 ]

3 голосов
/ 13 августа 2011

Вызовите функцию Freeze() на BitmapSource, когда вы все еще находитесь в фоновом потоке.

Это делает объект неизменным и, следовательно, может использоваться в главном потоке.

Узнайте здесь больше о замораживаемых объектах wpf. На странице обсуждается множество других аспектов Freezables, но в ней также прямо говорится: «Замороженный Freezable также может быть разделен между потоками, ...».Эта возможность, встроенная в WPF для создания элементов графического интерфейса (например, растровых изображений) в фоновых потоках, является нераздернизированной функцией WPF № 1, если вы спросите меня.

0 голосов
/ 13 августа 2011

BitmapSource.Clone() может пригодиться, если вы хотите скопировать объект.

Dispatcher.CheckAccess() следует вызвать до установки image1.Source.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...