1

In my Form, I'm trying to make my undo button work. I use the pictureBox1 to draw my squares and pictureBox2 to display the drawn history.

Functionaility

On Picturebox mouseup event I'm adding all my drawn objects to my list array so later when I click on the undo button it should show me my history of rectangles I drew. So If I have 4 rectangles on picturebox1 and I click undo picturebox2 should show 3 rectangles. If I click on the undo button again picturebox2 should show me 2 rectangles.

The problem is when I click on the undo button It doesn't work as I hoped. It keeps on showing me all the rectangles I drew and doesn't show my the history of my drawn objects.

enter image description here

Code

    Point startArea;   // mouse-down position
    Point currentArea; // current mouse position
    bool drawingBox;     // busy drawing
    List<Rectangle> rectangleList = new List<Rectangle>(); //previous rectangles
    List<Image> ImageCollection = new List<Image>();
    int ImageHistory;

    private void Test_Load(object sender, EventArgs e)
    {
        pictureBox1.Image = Properties.Resources.background;

    }

    private Rectangle getRectangle()
    {
        return new Rectangle(
            Math.Min(startArea.X, currentArea.X),
            Math.Min(startArea.Y, currentArea.Y),
            Math.Abs(startArea.X - currentArea.X),
            Math.Abs(startArea.Y - currentArea.Y));
    }

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        currentArea = startArea = e.Location;
        drawingBox = true;

    }

    private void pictureBox1_MouseMove(object sender, MouseEventArgs e)
    {
        currentArea = e.Location;
        if (drawingBox)
        {
            pictureBox1.Invalidate();
        }
    }



    private void pictureBox1_MouseUp(object sender, MouseEventArgs e)
    {
        if(drawingBox)
        {
            drawingBox = false;
            var rc = getRectangle();
            if(rc.Width > 0 && rc.Height > 0)
            {
                rectangleList.Add(rc);
                pictureBox1.Invalidate();
            }
        }
        ImageHistory++;
        ImageCollection.Add(pictureBox1.Image);

    }

    private void pictureBox1_Paint(object sender, PaintEventArgs e)
    {
        if (rectangleList.Count > 0)
        {
            using (var g = Graphics.FromImage(pictureBox1.Image))
            {
                g.DrawRectangles(Pens.Black, rectangleList.ToArray());
                pictureBox1.Invalidate();
            }
        }

        if(drawingBox)
        {                                                
            e.Graphics.DrawRectangle(Pens.Red, getRectangle());

            pictureBox1.Invalidate();                
        }

    }

    private void button1_Click(object sender, EventArgs e)
    {
        ImageHistory--;
        pictureBox2.Image = ImageCollection[ImageHistory];
    }
}
Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398
taji01
  • 2,527
  • 8
  • 36
  • 80

2 Answers2

3

Change the following line

ImageCollection.Add(pictureBox1.Image);

To

ImageCollection.Add((Image)pictureBox1.Image.Clone())
Júlio Murta
  • 555
  • 2
  • 15
3

While the accepted answer could solve the problem, but there are some important notes that should be taken into account when trying to solve the problem:

  • You don't need to use the List<Image> as undo buffer, It uses a lot of memory. And Currently since you don't dispose unused images, your application will get into memory leak soon.

  • It's enough to use List<Rectangle> as undo buffer. It uses less memory noticeably. You don't need to keep track of anything to dispose, it doesn't need to dispose.

  • To implement the idea, it's better to keep List<Rectangle> as undo buffer and each time you draw a rectangle, add it to the buffer and by each undo, just remove the last rectangle and invalidate the drawing surface. This way you don't need to change the image of the picture box and you should just draw rectangles on drawing surface rather than drawing them on the image. At the end, if you need to export the image including all rectangles, just draw them on a bitmap and export the image.

  • You can encapsulate the Undo method and Redo method and CanUndo property in the DrawingSurface control and use those methods in your application rather than directly putting those logic behind a button.

Example

You can see an example about Draw multiple freehand Poly-line or Curve drawing - Adding Undo Feature.

Reza Aghaei
  • 120,393
  • 18
  • 203
  • 398