Clinton Pierce Clinton Pierce - 4 years ago 274
C# Question

Simple animation using C#/Windows Forms

I need to knock out a quick animation in C#/Windows Forms for a Halloween display. Just some 2D shapes moving about on a solid background. Since this is just a quick one-off project I really don't want to install and learn an entire new set of tools for this. (DirectX dev kits, Silverlight, Flash, etc..) I also have to install this on multiple computers so anything beyond the basic .Net framework (2.0) would be a pain in the arse.

For tools I've got VS2k8, 25 years of development experience, a wheelbarrow, holocaust cloak, and about 2 days to knock this out. I haven't done animation since using assembler on my Atari 130XE (hooray for page flipping and player/missile graphics!)

Advice? Here's some of the things I'd like to know:

  • I can draw on any empty widget (like a panel) by fiddling with it's OnPaint handler, right? That's how I'd draw a custom widget. Is there a better technique than this?

  • Is there a page-flipping technique for this kind of thing in Windows Forms? I'm not looking for a high frame rate, just as little flicker/drawing as necessary.


Post Mortem Edit ... "a couple of coding days later"

Well, the project is done. The links below came in handy although a couple of them were 404. (I wish SO would allow more than one reply to be marked "correct"). The biggest problem I had to overcome was flickering, and a persistent bug when I tried to draw on the form directly.

  • Using the OnPaint event for the Form: bad idea. I never got that to work; lots of mysterious errors (stack overflows, or ArgumentNullExceptions). I wound up using a panel sized to fill the form and that worked fine.

  • Using the OnPaint method is slow anyway. Somewhere online I read that building the PaintEventArgs was slow, and they weren't kidding. Lots of flickering went away when I abandoned this. Skip the OnPaint/Invalidate() and just paint it yourself.

  • Setting all of the "double buffering" options on the form still left some flicker that had to be fixed. (And I found conflicting docs that said "set them on the control" and "set them on the form". Well controls don't have a .SetStyle() method.) I haven't tested without them, so they might be doing something (
    is the form):

    this.SetStyle(ControlStyles.UserPaint, true);
    this.SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
    this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

So the workhorse of the code wound up looking like (
is the panel control):

void PaintPlayField()
Bitmap bufl = new Bitmap(pf.Width, pf.Height);
using (Graphics g = Graphics.FromImage(bufl))
g.FillRectangle(Brushes.Black, new Rectangle(0, 0, pf.Width, pf.Height));
pf.CreateGraphics().DrawImageUnscaled(bufl, 0, 0);

And I just called PaintPlayField from the inside of my Timer loop. No flicker at all.

Answer Source

Set off a timer at your desired frame rate. At each timer firing twiddle the internal representation of the shapes on the screen (your model) per the animation motion you want to achieve, then call Invalidate(true). Inside the OnPaint just draw the model on the screen.

Oh yeah, and you probably want to turn Double Buffering on (this is like automatic page flipping).

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download