1

I'm designing an app that loads an image into a PictureBox and allows the user to draw a selection rectangle on it. Currently, I use the Paint event and a boolean to clear the previously-drawn rectangle (since it's a drag-able selection box).

Question:

The code fails because the previous rectangle isn't cleared from the image. Although each rectangle that is drawn is transparent, the effect is an opaque rectangle because the previous rectangles aren't cleared. How can I clear these rectangles?

Logic:

saveState defaults to true. When the Paint event is triggered the first time, the state containing the normal image is saved. When a MouseDown event is triggered, we register the starting position of the rectangle and a boolean that indicates a rectangle is being drawn.

When a MouseMove event is triggered, we draw a rectangle at the current coordinates. Since the Paint event is triggered (I think) when this is drawn and saveState is false, we restore the normal image before drawing the rectangle.

Finally, when a MouseUp event is triggered, saveState is set to true, so the graphics state, with the last rectangle drawn, is saved, and we're back at the beginning.

I read about ControlPaint.DrawReversibleFrame, but since this article and this question give me the impression that it isn't designed for drawing on images, but rather on the screen or the form directly, I'm not sure that's what I need.

Code:

public partial class MainWindow : Form
{
    private bool drawingRectangle;
    private int x1, y1, x2, y2;
    private Image currentImage;
    private GraphicsState previousState;
    private bool saveState;

    public MainWindow()
    {
        InitializeComponent();
        this.drawingRectangle = false;
        this.saveState = true;
    }

    private void EditorPictureBox_MouseDown(object sender, MouseEventArgs e)
    {
        this.x1 = e.X;
        this.y1 = e.Y;
        this.drawingRectangle = true;
    }

    private void EditorPictureBox_MouseMove(object sender, MouseEventArgs e)
    {
        if (this.drawingRectangle)
        {
            this.x2 = e.X;
            this.y2 = e.Y;
            Graphics g = Graphics.FromImage(this.currentImage);
            int[] dim = ImageLibrary.CalculateRectangleDimensions(this.x1, this.y1, this.x2, this.y2);
            g.FillRectangle(new SolidBrush(Color.FromArgb(100, 128, 255, 255)), dim[0], dim[1], dim[2], dim[3]);
            this.Refresh();
        }
    }

    private void EditorPictureBox_Paint(object sender, PaintEventArgs e)
    {
        if (this.saveState)
        {
            this.previousState = e.Graphics.Save();
            this.saveState = false;
        }
        else
            e.Graphics.Restore(this.previousState);
    }

    private void EditorPictureBox_MouseUp(object sender, MouseEventArgs e)
    {
        if (this.drawingRectangle)
        {
            this.drawingRectangle = false;
            // When the mouse click is released, save the graphics state
            this.saveState = true; 
        }
    }

    private void LoadImage2Button_Click(object sender, EventArgs e)
    {
        this.currentImage = Image.FromFile("goat2.jpg");
        this.EditorPictureBox.Image = this.currentImage;
    }   
}

This is the code for CalculateRectangleDimensions (stored in a static library):

public static int[] CalculateRectangleDimensions(int x1, int y1, int x2, int y2)
{
    int[] dimensions = new int[4]; // x1, y1, width, height
    if (x1 <= x2) // Mouse was dragged to the right
    {
        dimensions[0] = x1;
        dimensions[2] = x2 - x1;
    }
    else // Mouse was dragged to the right
    {
        dimensions[0] = x2;
        dimensions[2] = x1 - x2;
    }

    if (y1 <= y2) // Mouse was dragged up
    {
        dimensions[1] = y1;
        dimensions[3] = y2 - y1;
    }
    else // Mouse was dragged down
    {
        dimensions[1] = y2;
        dimensions[3] = y1 - y2;
    }
    return dimensions;
}
Community
  • 1
  • 1
Ricardo Altamirano
  • 14,650
  • 21
  • 72
  • 105

1 Answers1

1

Graphics.Save doesn't save the entire contents of the graphics when you call it, it just saves state information, like translations, scale, transforms, etc.

if you want to undo a draw you've already done, you'd have to do something like reversible paint does, or you'll have to redraw the original image when you want to undo your draw.

John Gardner
  • 24,225
  • 5
  • 58
  • 76
  • Should I use [`ControlPaint.DrawReversibleFrame`](http://msdn.microsoft.com/en-us/library/system.windows.forms.controlpaint.drawreversibleframe.aspx)? Does it allow me to draw on the image itself? – Ricardo Altamirano Jul 25 '12 at 18:30
  • for what you are doing (temporary drawing while mouse dragging), i think `DrawReversibleFrame` is *exactly* what you want. you don't need to draw on the image, you need to draw on the screen where the user is dragging the mouse, temporarily, right? – John Gardner Jul 25 '12 at 18:55
  • Basically, yes. Once the user lets the mouse up, I need to have the transparent rectangle saved with the image, but for the selection process, it does not need to be saved. – Ricardo Altamirano Jul 25 '12 at 19:11