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;
}