I'm writing a plugin for a trading software (C#, winforms, .NET 3.5) and I'd like to draw a crosshair cursor over a panel (let's say ChartPanel
) which contains data that might be expensive to paint. What I've done so far is:
- I added a
CursorControl
to the panel- this
CursorControl
is positioned over the main drawing panel so that it covers it's entire area - it's
Enabled = false
so that all input events are passed to the parentChartPanel
- it's
Paint
method is implemented so that it draws lines from top to bottom and from left to right at current mouse position
- this
- When MouseMove event is fired, I have two possibilities:
- A) Call
ChartPanel.Invalidate()
, but as I said, the underlying data may be expensive to paint and this would cause everything to redraw everytime I move a mouse, which is wrong (but it is the only way I can make this work now) - B) Call
CursorControl.Invalidate()
and before the cursor is drawn I would take a snapshot of currently drawn data and keep it as a background for the cursor that would be just restored everytime the cursor needs to be repainted ... the problem with this is ... I don't know how to do that.
- A) Call
2.B. Would mean to:
- Turn existing
Graphics
object intoBitmap
(it (the Graphics) is given to me through Paint method and I have to paint at it, so I just can't create a new Graphics object ... maybe I get it wrong, but that's the way I understand it) - before the crosshair is painted, restore the Graphics contents from the Bitmap and repaint the crosshair
I can't control the process of painting the expensive data. I can just access my CursorControl
and it's methods that are called through the API.
So is there any way to store existing Graphics contents into Bitmap and restore it later? Or is there any better way to solve this problem?
RESOLVED: So after many hours of trial and error I came up with a working solution. There are many issues with the software I use that can't be discussed generally, but the main principles are clear:
- existing Graphics with already painted stuff can't be converted to Bitmap directly, instead I had to use
panel.DrawToBitmap
method first mentioned in @Gusman's answer. I knew about it, I wanted to avoid it, but in the end I had to accept, because it seems to be the only way - also I wanted to avoid double drawing of every frame, so the first crosshair paint is always drawn directly to the
ChartPanel
. After the mouse moves without changing the chart image I take a snapshow throughDrawToBitmap
and proceed as described in chosen answer. - The control has to be Opaque (not enabled Transparent background) so that refreshing it doesn't call Paint on it's parent controls (which would cause the whole chart to repaint)
I still experience occasional flicker every few seconds or so, but I guess I can figure that out somehow. Although I picked Gusman's answer, I would like to thank everyone involved, as I used many other tricks mentioned in other answers, like the Panel.BackgroundImage, use of Plot() method instead of Paint() to lock the image, etc.
See Question&Answers more detail:os