osos95
osos95

Reputation: 169

Object is currently used elsewhere. PictureBox

I was trying to implement Langton's Ant algorithm in an application. It has 1 Button and 1 PictureBox control. This is the code:

    int x = 100, y = 100;
    int angle = 90;

    private void button1_Click(object sender, EventArgs e)
    {
        Bitmap resultImage = new Bitmap(pictureBox1.Width, pictureBox1.Height, PixelFormat.Format24bppRgb);
        for (int i = 0; i < resultImage.Height; i++)
        {
            for (int z = 0; z < resultImage.Width; z++)
            {
                resultImage.SetPixel(z, i, Color.White);
            }
        }
        pictureBox1.Image = resultImage;
        Thread t = new Thread(new ThreadStart(run));
        t.Start();
    }

    public void run()
    {
        while (true)
        {
            lock (pictureBox1.Image)
            {
                //Exception takes place here.
                Bitmap b = (Bitmap)pictureBox1.Image;
                string name = b.GetPixel(x, y).Name;
                if (b.GetPixel(x, y).Name == "ffffffff")
                {
                    angle -= 90;
                    b.SetPixel(x, y, Color.Black);
                }
                else
                {
                    angle += 90;
                    b.SetPixel(x, y, Color.White);
                }
                pictureBox1.Image = b;
                x += (int)Math.Cos(angle * Math.PI / 180);
                y += (int)Math.Sin(angle * Math.PI / 180);
            }
            Thread.Sleep(50);
        }
    }

In run() method there's always an exception at Bitmap b = (Bitmap)pictureBox1.Image; and it says InvalidOperatopnException: Object is currently in use elsewhere. It doesn't matter whether I lock the image or not. The exception doesn't occur when I increase the interval to 250 ms or above

Upvotes: 0

Views: 1873

Answers (1)

Dai
Dai

Reputation: 155035

Two things:

  1. You're accessing WinForms UI components from another thread. GUI components can only be touched (even read) from the UI thread.
  2. You're manipulating a Bitmap that's already in-use by the PictureBox, instead you should either outright replace it with a new image, or use the Clone method first, then re-assign, and don't forget to Dispose of the old bitmap, if any.

Try this:

private void ButtonClick() {

    Thread thread = new Thread( this.BackgroundThread );
    thread.Start(); // ideally, replace this with the .NET Task library
}

private void BackgroundThread() {

    Bitmap bmp = new Bitmap( ... );
    // do bitmap pixel-setting here

    this.Invoke( () => {
        Bitmap old = this.pictureBox1.Image as Bitmap;
        this.pictureBox1.Image = bmp;
        if( old != null ) {
            old.Dispose();
        }
    } );
}

Also, avoid GetPixel and SetPixel, they're very slow. Instead do this:

BitmapData data = bmp.LockBits();
Byte[] buffer = new Byte[ ... ];
Marshal.Copy( data.Scan0, buffer, ... );

// Manipulate raw pixel data contained in buffer here

bmp.UnlockBits( data );

Upvotes: 3

Related Questions