I have a WinForms control on which I want to display two things:
- An underlying image painstakingly loaded bit-by-bit from an external input device; and
- A series of DrawLine calls that create a visual pattern over that image.
Thing #1, for our purposes, doesn't change, and I'd rather not have to redraw it.
Thing #2 has to be redrawn relatively quickly, as it rotates when the user turns another control.
In my fantasy, I want to put each Thing in its own Graphics object, give #2 a transparent background, and simply hit #2 with a rotational transformation to match the user control setting. But I don't see a way to make a Graphics object transparent, nor a way to rotate what's already been drawn on one. So I'm probably asking Graphics to do something it wasn't designed for.
Here's my question: What's the best way to set this up? Should I attempt to overlap my Graphics objects, or is there some completely different and better way to do this that I'm not thinking of?
GDI+ isn't retained-mode, so you'll need to redraw the entire control on every Paint. So unfortunately you can't just have two "things" and apply a rotation to one of them. Your best bet with GDI+ is probably:
Then in the Paint handler redraw #1 quickly using Graphics.DrawImage, set a rotate transform using Graphics.RotateTransform, and draw your lines. You should be able to make this appear smooth using double-buffering (ControlStyles.DoubleBuffer).
As for "completely different" ways to do this, well, the "fantasy" you're describing is called Windows Presentation Foundation. WPF does have a retained-mode graphics system and might be able to handle the "rotate one layer while keeping the other constant" more conveniently. And you can host WPF in WinForms using the ElementHost control. The rough idea would be to use a Grid to overlay a Canvas on an Image, add Line objects to the Canvas, set the Canvas' RenderTransform to a RotateTransform, and bind the RotateTransform's Angle to the other control's value. However this does raise project considerations (target platform, learning curve) and also technical ones (initial overhead of loading WPF DLLs, interop constraints).
The Windows painting model is a good match for your requirements. It separates drawing the background (OnPaintBackground) from the foreground (OnPaint). That however doesn't mean that you can only paint the background once and be done with it. Window surface invalidation invokes both. This is above all required to make anti-aliasing effects work properly, they can only look good against a known background color.
Punt this and draw the Image in the OnPaintBackground() override. You can let Control do this automatically for you by assigning the BackgroundImage property. You'll probably need to set the DoubleBuffer property to true to avoid the flicker you'll see when the background is drawn, temporarily wiping out the foreground pixels. Call Invalidate() if you need to update the foreground.
To be complete, your fantasy is in fact possible. You'd need to overlay the image with a toplevel layered window. That is easy to get with a Form whose TransparencyKey property is set. Here is a sample implementation:
One interesting artifact: minimizing the form and restoring it again looks, erm, special.