Search code examples
c#.netmultithreadingwinformsbackgroundworker

I'm using backgroundworker and getting: InvalidOperationException: Cross-thread operation not valid - how should I handle it?


Consider:

private void backgroundWorker2_DoWork(object sender, DoWorkEventArgs e)
{
    Bitmap newbmp = new Bitmap(512, 512);
    IEnumerable<Point> CommonList = null;
    StreamWriter w = new StreamWriter(@"c:\diff\diff.txt");
    pixelscoordinatesinrectangle = new List<Point>();
    pixelscoordinatesinrectangle = pointsAffected.ToList();
    DrawIt = false;
    for (int i = 0; i < trackBar1FileInfo.Length; i++)
    {
        DrawIt = true;
        this.BeginInvoke(new MethodInvoker(delegate
        {
            trackBar1.Value = i;

                    LoadPictureAt(trackBar1.Value, sender);
                    pictureBox1.Load(trackBar1FileInfo[i].FullName);

            ConvertedBmp = ConvertTo24(trackBar1FileInfo[trackBar1.Value].FullName);
            ConvertedBmp.Save(ConvertedBmpDir + "\\ConvertedBmp.bmp");
            mymem = ToStream(ConvertedBmp, ImageFormat.Bmp);
        }));
        Button1Code();
        pictureBox1.Refresh();
        newpixelscoordinates = new List<Point>();
        newpixelscoordinates = pointsAffected.ToList();
        CommonList = pixelscoordinatesinrectangle.Intersect(newpixelscoordinates);
        foreach (Point s in CommonList)
        {
            w.WriteLine("The following points are the same" + s);
            newbmp.SetPixel(s.X, s.Y, Color.Red);
        }
    }
    w.Close();
    using (Graphics G = Graphics.FromImage(newbmp))
    {
        G.DrawRectangle(pen, rect);
    }
    newbmp.Save(@"c:\newbmp\newbmp.bmp", ImageFormat.Bmp);
    newbmp.Dispose();
}

Exception message:

Cross-thread operation not valid: Control 'pictureBox1' accessed from a thread other than the thread it was created on.

I tried to use this.BeginInvoke(new MethodInvoker(delegate

Even now when using the code after the BeginInvoke, it is showing the exception so I need to use the BeginInvoke on the other code too.

Is using BeginInvoke a good way? Or I should solve it in other way and if so how?


Solution

  • In Windows GUI programming:

    1. Move the time-consuming task to a separate thread to keep UI responsive, you choose to use BackgroundWorker, that is a good choice.
    2. You can't access UI elements from background thread (the Cross-thread operation not valid exception), that is why you need to call BeginInvoke to access the Picturebox.

    Remember the code called by BeginInvoke is actually running on UI thread. So if you put all the code in the DoWork in BeginInvoke, it is the same as not using Backgroundworker- actually it is even worse, because creating a new thread and marshaling the call to UI thread have significant performance overhead.

    So you should call BeginInvoke only when it is necessary - when the code needs to access UI elements, the rest code should not be wrapped inside BeginInvoke.