Passing ArrayList from BackgroundWorker

Hi,

I have a background process in a WPF application that produces an ArrayList and a textstring. I get an exception when I try to pass these data from the BackgroundWorker to the main thread - but only for the ArrayList - see the screendump. I have tried different approaches but without sucess. I would appreciate help on this.

I have supplied the XAML code as well as the C# code.

Best regards
RTSol
XAML:

<Window x:Class="BackGround.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Window1" Height="700" Width="800">
    <Canvas>
        <Button x:Name="btnGet" Click="btnGet_Click" Content="Process image" Canvas.Left="10" Canvas.Top="20"/>
        <Canvas x:Name="cnvOpen" Canvas.Left="150" Canvas.Top="10">
            <TextBlock x:Name="lblOpening" HorizontalAlignment="Left" VerticalAlignment="Top" Text="Processing page 1 of 23" TextWrapping="Wrap" Foreground="Black" FontSize="11" Canvas.Left="0" Canvas.Top="0"/>
            <ProgressBar x:Name="openProgress" HorizontalAlignment="Stretch" Height="15" Width="215" Canvas.Left="0" Canvas.Top="20" />
        </Canvas>
        <Canvas Canvas.Left="500" Canvas.Top="10">
            <TextBlock x:Name="lblOutput" />
        </Canvas>
        <Image x:Name="scannedImage" Canvas.Left="10" Canvas.Top="80" >

        </Image>
    </Canvas>
</Window>

C#:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Collections;
using System.ComponentModel;
using System.Threading;

namespace BackGround
{
    class imPars
    {
        public string imagePath { get; set; }
        public ArrayList images { get; set; }
    }

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            //Start by displaying the image
            System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(@"C:\Users\Rob\Pictures\money.jpg");
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            System.Windows.Media.Imaging.BitmapImage bmImg = new System.Windows.Media.Imaging.BitmapImage();
            bmImg.BeginInit();
            bmImg.StreamSource = new MemoryStream(ms.ToArray());
            bmImg.EndInit();
            scannedImage.Source = (System.Windows.Media.Imaging.BitmapImage)bmImg;
        }

        private void btnGet_Click(object sender, RoutedEventArgs e)
        {
            BackgroundWorker imageworker = new BackgroundWorker();
            imageworker.WorkerReportsProgress = true;
            imageworker.ProgressChanged += imageProgressChanged;
            imageworker.DoWork += getimage;
            imageworker.RunWorkerCompleted += imageWorkerCompleted;
            imPars args = new imPars
            {
                imagePath = @"C:\Users\Rob\Pictures\money.jpg",
            };

            lblOpening.Text = "Extracting images ...";
            cnvOpen.Visibility = Visibility.Visible;
            Cursor = Cursors.Wait;
            imageworker.RunWorkerAsync(args);
        }

        private void getimage(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            imPars arg = e.Argument as imPars;
            string imagefile = arg.imagePath;
            ArrayList imagesB = new ArrayList();

            //Make an ErrayList with 12 images
            for (int i = 1; i < 13; i++)
            {
                System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(imagefile);
                MemoryStream ms = new MemoryStream();
                bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                System.Windows.Media.Imaging.BitmapImage bmImg = new System.Windows.Media.Imaging.BitmapImage();
                bmImg.BeginInit();
                bmImg.StreamSource = new MemoryStream(ms.ToArray());
                bmImg.EndInit();
                imagesB.Add(bmImg);
                int prog = (int)((float)i / 12.0 * 100.0);
                Thread.Sleep(200);
                worker.ReportProgress(prog);
            }

            arg.imagePath = "test";
            arg.images = imagesB;
            e.Result = arg;
        }
        private void imageWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            openProgress.Value = 100;
            imPars arg = e.Result as imPars;
            Cursor = Cursors.Arrow;

            lblOutput.Text = arg.imagePath;

            ArrayList images = new ArrayList();
            images = (ArrayList)arg.images;

            //Show the first image in the ArrayList - Here is where the exception is thrown
            scannedImage.Source = (System.Windows.Media.Imaging.BitmapImage)images[0];
        }

        private void imageProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            openProgress.Value = e.ProgressPercentage;
        }
    }
}

Open in new window

Exception.jpg
RTSolAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

lazyberezovskyCommented:
0
RTSolAuthor Commented:
I thought that when I am in imageWorkerCompleted I am back to the main thread and that I have access to my parameters through e.Result. Everything works fine for lblOutput.Text - see this line:

  lblOutput.Text = arg.imagePath;

However, these lines fails:

  images = (ArrayList)arg.images;
  scannedImage.Source = (System.Windows.Media.Imaging.BitmapImage)images[0];

There seem to be a differnce between a Label and an ArrayList.

Please correct me where I am wrong - or even better would be if you could provide the code that solves my problem. I am stuck!

Best regards
RTSol
0
lazyberezovskyCommented:
Are you sure that lblOutput.Text assigned? On which line of code do you get that exception?
0
CompTIA Cloud+

The CompTIA Cloud+ Basic training course will teach you about cloud concepts and models, data storage, networking, and network infrastructure.

RTSolAuthor Commented:
I am sure,

If I omitt the line

  scannedImage.Source = (System.Windows.Media.Imaging.BitmapImage)images[0];

The program runs fine - except there is no image shown of course. The line above gives the exception. I have examined the ArrayList images before this line and it is populated with 12 images as expected.
0
Mike TomlinsonMiddle School Assistant TeacherCommented:
You're passing the ArrayList out thru e.Result?
0
RTSolAuthor Commented:
Yes
0
RTSolAuthor Commented:
Maybe you can try the code to see what happens - its all there.
0
Todd GerbertIT ConsultantCommented:
What I think is happening...
imagesB is created on the worker thread, in the worker you are calling arg.images = imagesB; - which makes arg.images a reference to imagesB.  So in the main thread when you refer to arg.images you are actually accessing the ArrayList it points to, which is imagesB and was in fact created on a different thread. Try copying the elements instead, using arg.AddRange(imagesB) or adding directly to arg.images
 
Also, I seem to recall reading somewhere that the BackgroundWorker won't wire up it's events correctly if you manually instantiate it in code, but I'm not sure how accurate that is, but it wouldn't hurt to try dropping it onto your form in the designer from the toolbox.
0
RTSolAuthor Commented:
Hi,

I changed the code and removed the imagesB - se attachment. It still trows the exception.

I looked in the toolbox but there is no BackgroundWorker there. Should it be?

Now there is no difference between the Label and the Image in how they are passed - I think ...

I hope you can think of something - getting a bit desperate. Is there anything special with an object that can be instanciated? I feel a bit lost.

Best regards
RTSol
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Collections;
using System.ComponentModel;
using System.Threading;
using System.Windows.Threading;

namespace BackGround
{
    class imPars
    {
        public string imagePath { get; set; }
        public ArrayList images { get; set; }
    }

    public partial class Window1 : Window
    {
        public Window1()
        {
            InitializeComponent();

            //Start by displaying the image
            System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(@"C:\Users\Rob\Pictures\money.jpg");
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
            System.Windows.Media.Imaging.BitmapImage bmImg = new System.Windows.Media.Imaging.BitmapImage();
            bmImg.BeginInit();
            bmImg.StreamSource = new MemoryStream(ms.ToArray());
            bmImg.EndInit();
            scannedImage.Source = (System.Windows.Media.Imaging.BitmapImage)bmImg;
        }

        private void btnGet_Click(object sender, RoutedEventArgs e)
        {
            BackgroundWorker imageworker = new BackgroundWorker();
            imageworker.WorkerReportsProgress = true;
            imageworker.ProgressChanged += imageProgressChanged;
            imageworker.DoWork += getimage;
            imageworker.RunWorkerCompleted += imageWorkerCompleted;
            imPars args = new imPars
            {
                imagePath = @"C:\Users\Rob\Pictures\money.jpg",
                images = new ArrayList(),
            };

            lblOpening.Text = "Extracting images ...";
            cnvOpen.Visibility = Visibility.Visible;
            scannedImage.Source = null;
            Cursor = Cursors.Wait;
            imageworker.RunWorkerAsync(args);
        }

        private void getimage(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            imPars arg = e.Argument as imPars;
            string imagefile = arg.imagePath;

            //Make an ErrayList with 12 images
            for (int i = 1; i < 13; i++)
            {
                System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(imagefile);
                MemoryStream ms = new MemoryStream();
                bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                System.Windows.Media.Imaging.BitmapImage bmImg = new System.Windows.Media.Imaging.BitmapImage();
                bmImg.BeginInit();
                bmImg.StreamSource = new MemoryStream(ms.ToArray());
                bmImg.EndInit();
                arg.images.Add(bmImg);
                int prog = (int)((float)i / 12.0 * 100.0);
                Thread.Sleep(200);
                worker.ReportProgress(prog);
            }

            arg.imagePath = "This text was created by the BackgroundWorker";
            e.Result = arg;
        }
        private void imageWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            openProgress.Value = 100;
            imPars arg = e.Result as imPars;
            Cursor = Cursors.Arrow;

            //Assign the text to lblOutput
            lblOutput.Text = arg.imagePath;
            //Assign the first image to scannedImage
            scannedImage.Source = (System.Windows.Media.Imaging.BitmapImage)arg.images[0];
        }

        private void imageProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            openProgress.Value = e.ProgressPercentage;
        }
    }
}

Open in new window

0
Mike TomlinsonMiddle School Assistant TeacherCommented:
I found that the BitmapImages had to be created in the main thread so I ended up passing out the MemoryStream to the ProgressChanged() event.

This seems to fundamentally behave DIFFERENTLY than a simple WinForms app where a control could be created in the worker thread and then passed out thru the ProgressChanged event and added to the main forms Controls() collection...  =\

Here is the code I got to work:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.IO;
using System.Collections;
using System.ComponentModel;
using System.Threading;

namespace WpfApplication1
{

    class imPars
    {
        public string imagePath { get; set; }
        public ArrayList images { get; set; }
    }

    public partial class Window1 : Window
    {

        private imPars args;
        private string file = @"C:\Users\Mike\Pictures\AmericanFlag.jpg";

        public Window1()
        {
            InitializeComponent();

            //Start by displaying the image
            System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(file);
            MemoryStream ms = new MemoryStream();
            bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);

            System.Windows.Media.Imaging.BitmapImage bmImg = new System.Windows.Media.Imaging.BitmapImage();
            bmImg.BeginInit();
            bmImg.StreamSource = new MemoryStream(ms.ToArray());
            bmImg.EndInit();
            scannedImage.Source = (System.Windows.Media.Imaging.BitmapImage)bmImg;
        }

        private void btnGet_Click(object sender, RoutedEventArgs e)
        {
            BackgroundWorker imageworker = new BackgroundWorker();
            imageworker.WorkerReportsProgress = true;
            imageworker.ProgressChanged += imageProgressChanged;
            imageworker.DoWork += getimage;
            imageworker.RunWorkerCompleted += imageWorkerCompleted;
            
            args = new imPars
            {
                imagePath = file,
                images = new ArrayList()
            };

            lblOpening.Text = "Extracting images ...";
            cnvOpen.Visibility = Visibility.Visible;
            Cursor = Cursors.Wait;

            btnGet.IsEnabled = false;
            imageworker.RunWorkerAsync(args);
        }

        private void getimage(object sender, DoWorkEventArgs e)
        {
            BackgroundWorker worker = sender as BackgroundWorker;
            imPars arg = e.Argument as imPars;
            string imagefile = arg.imagePath;
            //Make an ErrayList with 12 images
            for (int i = 1; i < 13; i++)
            {
                System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(imagefile);
                MemoryStream ms = new MemoryStream();
                bitmap.Save(ms, System.Drawing.Imaging.ImageFormat.Jpeg);
                
                int prog = (int)((float)i / 12.0 * 100.0);
                Thread.Sleep(200);
                worker.ReportProgress(prog, ms);
            }

            arg.imagePath = "test";
            e.Result = arg;
        }

        private void imageProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            MemoryStream ms = (MemoryStream)e.UserState;
            System.Windows.Media.Imaging.BitmapImage bmImg = new System.Windows.Media.Imaging.BitmapImage();
            bmImg.BeginInit();
            bmImg.StreamSource = new MemoryStream(ms.ToArray());
            bmImg.EndInit();
            args.images.Add(bmImg);
            openProgress.Value = e.ProgressPercentage;
        }

        private void imageWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            openProgress.Value = 100;
            imPars arg = e.Result as imPars;
            Cursor = Cursors.Arrow;

            lblOutput.Text = arg.imagePath;
            scannedImage.Source = (System.Windows.Media.Imaging.BitmapImage)arg.images[0];

            btnGet.IsEnabled = true;
        }

    }

}

Open in new window

0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Mike TomlinsonMiddle School Assistant TeacherCommented:
*Note that I am very NEW to WPF programming so there could be a better way to accomplish this that I am not aware of...
0
RTSolAuthor Commented:
Thanks a lot Idle_Mind - I would never have figured out this. Who cares if there is another way of doing this - this is good and simple.

Thanks again!
RTSol
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
.NET Programming

From novice to tech pro — start learning today.