This can be done in several ways, always storing the graphics as a Bitmap
. The most direct and efficient way is to let the Panel
do all the work for you.
Here is the idea: Most winforms Controls
have a two-layered display.
In the case of a Panel
the two layers are its BackgroundImage
and its Control
surface.
The same is true for many other controls, like Label, CheckBox, RadioButton
or Button
.
(One interesting exception is PictureBox
, which in addition has an (Foreground) Image
. )
So we can move the expensive stuff into the BackgroundImage
and draw the crosshair on the surcafe.
In our case, the Panel
, all nice extras are in place and you could pick all values for the BackgroundImageLayout
property, including Tile, Stretch, Center
or Zoom
. We choose None
.
Now we add one flag to your project:
bool panelLocked = false;
and a function to set it as needed:
void lockPanel( bool lockIt)
{
if (lockIt)
{
Bitmap bmp = new Bitmap(panel1.ClientSize.Width, panel1.ClientSize.Width);
panel1.DrawToBitmap(bmp, panel1.ClientRectangle);
panel1.BackgroundImage = bmp;
}
else
{
if (panel1.BackgroundImage != null)
panel1.BackgroundImage.Dispose();
panel1.BackgroundImage = null;
}
panelLocked = lockIt;
}
Here you can see the magic at work: Before we actually lock the Panel
from doing the expensive stuff, we tell it to create a snapshot of its graphics and put it into the BackgroundImage
..
Now we need to use the flag to control the Paint
event:
private void panel1_Paint(object sender, PaintEventArgs e)
{
Size size = panel1.ClientSize;
if (panelLocked)
{
// draw a full size cross-hair cursor over the whole Panel
// change this to suit your own needs!
e.Graphics.DrawLine(Pens.Red, 0, mouseCursor.Y, size.Width - 1, mouseCursor.Y);
e.Graphics.DrawLine(Pens.Red, mouseCursor.X, 0, mouseCursor.X, size.Height);
}
// expensive drawing, you insert your own stuff here..
else
{
List<Pen> pens = new List<Pen>();
for (int i = 0; i < 111; i++)
pens.Add(new Pen(Color.FromArgb(R.Next(111),
R.Next(111), R.Next(111), R.Next(111)), R.Next(5) / 2f));
for (int i = 0; i < 11111; i++)
e.Graphics.DrawEllipse(pens[R.Next(pens.Count)], R.Next(211),
R.Next(211), 1 + R.Next(11), 1 + R.Next(11));
}
}
Finally we script the MouseMove
of the Panel
:
private void panel1_MouseMove(object sender, MouseEventArgs e)
{
mouseCursor = e.Location;
if (panelLocked) panel1.Invalidate();
}
using a second class level variable:
Point mouseCursor = Point.Empty;
You call lockPanel(true)
or lockPanel(false)
as needed..
If you implement this directly you will notice some flicker. This goes away if you use a double-buffered Panel
:
class DrawPanel : Panel
{
public DrawPanel() { this.DoubleBuffered = true; }
}
This moves the crosshair over the Panels
in a perfectly smooth way. You may want to turn on & off the Mouse cursor upon MouseLeave
and MouseEnter
..