There seems to be a million questions out there on this, yet I can't find one that will work. So, I guess it's time for question 1,000,001.
I have a custom control with a PictureBox
and a Panel
. The Panel
is the child of PictureBox
with a transparent background. This allows me tp draw on top of whatever image is loaded in the PictureBox
.
The drawing part works, but the erasing part does not. And if I use Invalidate()
I just get a bunch of flickering, and the line never even shows.
If the end goal isn't obvious, it should work like any decent drawing application, where you click in one spot, drag around, and the line moves with the mouse until you let go.
Code:
private void drawLine(Point pt) {
// Erase the last line
if (m_lastPoints != null) {
m_graphics.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy;
m_graphics.DrawLine(m_transPen, m_lastPoints[0], m_lastPoints[1]);
}
// Set the last points
m_lastPoints = new Point[] { m_mouseStartPoint, pt };
m_graphics.DrawLine(new Pen(m_color), m_mouseStartPoint, pt);
}
m_transPen
is defined as new Pen(Color.FromArgb(0, 0, 0, 0));
And the result:
Now, if I change it to:
m_graphics.DrawLine(Pens.White, m_lastPoints[0], m_lastPoints[1]);
I get this, which shows what it should be doing, only instead of with white lines, they should be transparent.
No need to erase the old line! Just invalidate the Panel
and draw the fresh one, best in the Paint
event.
But for this to work, the Panel
must not overlay the PictureBox
. It must be inside it! Put this in the load or constructor event :
yourPanel.Parent = yourPictureBox;
yourPanel.Size = yourPictureBox.Size;
yourPanel.Location = Point.Empty;
(I know you got that one right already, but maybe the next person only looks at the answer ;-)
To avoid flicker use a double-buffered Panel
..:
class DrawPanel : Panel
{
public DrawPanel()
{
DoubleBuffered = true;
}
}
..or, better yes, a Picturebox
or a Label
(with Autosize=false
); both have the DoubleBuffered
property turned on out of the box and support drawing better than Panels
do.
Actually, if you only want to draw something on top of the loaded Image
, you don't even need a separate Panel
. Just draw on the PictureBox
itself! It has three independent layers: BackgroundImage
, Image
and the Control surface
..
Here is the minimal code to draw a Cursor controlled line:
pictureBox1.MouseDown += pictureBox1_MouseDown;
pictureBox1.MouseMove += pictureBox1_MouseMove;
pictureBox1.MouseUp += pictureBox1_MouseUp;
pictureBox1.Paint += pictureBox1_Paint;
// class level
Point mDown = Point.Empty;
Point mCurrent = Point.Empty;
void pictureBox1_Paint(object sender, PaintEventArgs e)
{
if (mDown != Point.Empty) e.Graphics.DrawLine(Pens.White, mDown, mCurrent);
}
void pictureBox1_MouseUp(object sender, MouseEventArgs e)
{
mDown = Point.Empty;
}
void pictureBox1_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == System.Windows.Forms.MouseButtons.Left)
{
mCurrent = e.Location;
pictureBox1.Invalidate();
}
}
void pictureBox1_MouseDown(object sender, MouseEventArgs e)
{
mDown = e.Location;
}
The line disappears when you release the mouse button.
To make it permanent you need to store its two points in a list of data needed to draw them and work through that list in the Paint
event.
That list should probably also include the color, pen width and then some, so designing a class 'drawAction' will help..