Title: Draw on top of a background image in C#
Drawing on a background image is actually very easy. It seems to be a common question on the internet, however, so I decided to make this example.
This example draws on a background image in two ways. If you just move the mouse around, it draws the smiley face shown above. If you click and drag the mouse, the program draws an area selection rectangle as shown in the following picture.
All you need to do to draw on a background image is to set a PictureBox control's Image property and then draw in its Paint event handler without calling the Graphics object's Clear method. If that's all you wanted to know, you're done with this post. If you want to learn a bit more about what the example program does, read on.
The program does all of its drawing in a PictureBox control's Paint event handler. Before I explain how that works, I'll explain the mouse events that control the area selection rectangle. (If you only want to know how to draw on the background image, skip to the section "Drawing on a Background Image.")
Mouse Events
The program uses the following variables to track the selection rectangle.
private bool SelectingArea = false;
private Point StartPoint, EndPoint;
The program sets the SelectingArea to true while you are selecting an area. The StartPoint and EndPoint values keep track of where you press the mouse button and the mouse's current location.
When you press the mouse button over the PictureBox, the following event handler executes.
// Start drawing.
private void picCanvas_MouseDown(object sender, MouseEventArgs e)
{
StartPoint = e.Location;
EndPoint = e.Location;
SelectingArea = true;
this.Cursor = Cursors.Cross;
// Refresh.
picCanvas.Refresh();
}
This code saves the mouse's location in the StartPoint and EndPoint variables. It then sets SelectingArea to true to indicate that an area selection is in progress. It sets the cursor to a crosshair and finishes by refreshing the PictureBox to make it redraw itself.
When you move the mouse, the following event handler executes.
// Continue drawing.
private void picCanvas_MouseMove(object sender, MouseEventArgs e)
{
// Update the end point.
EndPoint = e.Location;
// Refresh.
this.Refresh();
}
This code simply saves the mouse's new position and refreshes the PictureBox to make it redraw itself. Note that this code does not check the SelectingArea variable; it executes whether of not the mouse is pressed. That allows the Paint event handler to draw either the smiley face or the selection rectangle.
When you release the mouse button, the following event handler executes.
// Continue drawing.
private void picCanvas_MouseUp(object sender, MouseEventArgs e)
{
SelectingArea = false;
this.Cursor = Cursors.Default;
// Do something with the selection rectangle.
Rectangle rect = MakeRectangle(StartPoint, EndPoint);
Console.WriteLine(rect.ToString());
}
This code first sets SelectingArea to false to indicate that the area selection is over. It then calls the MakeRectangle helper method described shortly to create a rectangle determined by the start and end points. It then displays the rectangle in the Output window. If you really wanted to allow the user to select an area, you would make the program do something with the rectangle here.
The following code shows the MakeRectangle helper method.
// Make a rectangle from two points.
private Rectangle MakeRectangle(Point p1, Point p2)
{
int x = Math.Min(p1.X, p2.X);
int y = Math.Min(p1.Y, p2.Y);
int width = Math.Abs(p1.X - p2.X);
int height = Math.Abs(p1.Y - p2.Y);
return new Rectangle(x, y, width, height);
}
This method finds the two points' minimum X and Y coordinates and the area's width and height. It then uses those values to create and return a new Rectangle.
Drawing on a Background Image
To draw on the background image, first set the PictureBox control's Image property to the background image. You may also want to set its SizeMode property to AutoSize to make the control fit the image.
Now simply draw on the control in the control's Paint event handler. The Paint event automatically resets the control's image whenever it needs to redraw so you don't need to clear the event handler's Graphics object. In fact, if you call the e.Graphics.Clear method, you will erase the background image.
The following code shows the picCanvas control's Paint event handler.
// Draw the selection rectangle.
private void picCanvas_Paint(object sender, PaintEventArgs e)
{
if (SelectingArea)
{
using (Pen pen = new Pen(Color.Yellow, 2))
{
e.Graphics.DrawRectangle(pen,
MakeRectangle(StartPoint, EndPoint));
pen.Color = Color.Red;
pen.DashPattern = new float[] { 5, 5 };
e.Graphics.DrawRectangle(pen,
MakeRectangle(StartPoint, EndPoint));
}
}
else
{
DrawSmiley(e.Graphics, EndPoint, 50);
}
}
If you are currently selecting an area, then the code creates a thick yellow pen. It uses the MakeRectangle helper method to define the currently selected rectangle and draws it.
Next, the code changes the pen's color to red and gives it a {5, 5} dash pattern so it draws 5 units and then skips 5 units. (Here a unit is a multiple of the pen's width, so in this example the pen draws 10 pixels and then skips 10 pixels.) The code redraws the rectangle with the new pen parameters to give it a yellow and red dashed appearance.
(Multi-colored dashed lines show up relatively well no matter what colors the background image has. The dash pattern's origin also changes as the mouse moves, giving the rectangle making the "marching ants" effect and making it even easier to see.)
If you are not currently selecting an area, the event handler simply calls the DrawSmiley method to draw a smiley face at the mouse's current location, which is stored in variable EndPoint. That method is straightforward so I won't describe it here. Download the example program to see how it works.
That's all there is to drawing over the background. Just draw without erasing anything.
Drawing on Forms
You can draw on a form's background image much as you can draw on a PictureBox, but you need to deal with two potential problems: tiling and tearing.
Tiling
A form does not have an Image property so you can't place the background image there. You can set the form's BackgroundImage property to the image, but then by default the form tiles the image to fill itself with copies of the image. There are a few ways that you can handle that.
The easiest approach is to just let the form tile its background image and not worry about it.
A second approach is to size the form to fit its image and then set its FromBorderStyle property to prevent the user from resizing the form and displaying the tiled copies. If you like, you can use the following Load event handler to fit the form to its background image and make it non-resizable.
// Size the form to fit its bacground image.
private void Form1_Load(object sender, EventArgs e)
{
ClientSize = BackgroundImage.Size;
FormBorderStyle = FormBorderStyle.FixedDialog;
}
A third approach is to change the form's BackgroundImageLayout property to prevent the form from tiling its background image. If you set this property to None, Center, Stretch, or Zoom, then the form won't tile the image.
The fourth and final approach that I'll mention is to place the image in a PictureBox that is on the form.
Tearing
If you modify the example program slightly so it draws on the form's background, the result suffers from some extremely annoying image tearing. As you move the mouse, the image flickers madly, often displaying only part of the background image and pieces of the drawing. For example, the image might sometimes look like the following.
Fortunately you can make this problem disappear if you simply set the form's DoubleBuffered property to true. That makes the form buffer its image until the image is complete before it displays the result.
Summary
All you really need to do to draw on a background image is to set a PictureBox control's Image property and then draw in its Paint event handler without calling the Graphics object's Clear method. Download the example and give it a try. For some extra practice, remove the PictureBox, display the image on the form, and attach the form's events to the provided event handlers to see what happens.
Download the example to experiment with it and to see additional details.
|