This page demonstrates a simple method for animating graphics in Windows Forms. In this example we use the Starfield graphics model which keeps track of star positions and has a
Render() method that will draw the field onto an existing
Bitmap. Controls on the Form let the user adjust the number of stars and the transparency of stars in real time. These controls serve a secondary purpose as tools to assess GUI responsiveness when the rendering system is under load.
💡 Separating graphics operations from GUI management facilitates testing and promotes maintainability. In this design rendering tasks are performed in the graphics model and platform-specific GUI tasks are isolated in their own project. This strategy allows us to create identical WPF and WinForms applications that use the same
Render()method in the graphics model.
By isolating all rendering methods in our graphics model library, our GUI code remains refreshingly simple. This is all it takes to continuously render our starfield animation and display it in the Form:
readonly Field field = new Field(500);
private void timer1_Tick(object sender, EventArgs e)
// advance the graphics model and render a new Bitmap
Bitmap bmp = new Bitmap(pictureBox1.Width, pictureBox1.Height);
byte alpha = (byte)(trackBar1.Value * 255 / 100);
Color starColor = Color.FromArgb(alpha, Color.White);
// replace the old Bitmap and dispose of the old one
var oldImage = pictureBox1.Image;
pictureBox1.Image = bmp;
💡 Manual double-buffering may improve performance. Creating a new
Bitmapand disposing an old one on every
Render()is costly. It’s not a good idea to render directly on the
Bitmapcurrently displayed in a
PictureBoxbecause it can produce tearing artifacts. Instead you could continuously hold two
Bitmapobjects in memory and alternate between the two (displaying one while drawing on the other). This strategy is called multiple buffering.
Timer triggers this sequence of events:
- Advance the starfield model in time
- Create a
Bitmapthe size of the
- Call the starfield’s
Render()method (passing-in the
Bitmapto be drawn on)
- Apply the
⚠️ WARNING: This method blocks the GUI thread while rendering. This is not noticeable when renders are fast (500 stars), but this results in an unresponsive application when the render takes a lot of CPU effort (100,000 stars). We will explore how to render graphics without blocking the GUI thread in a future article.
💡 Assigning a
Imageproperty of a
Pictureboxlets us take advantage of built-in double-buffering capabilities for flicker-free animations.
💡 1 ms is a good value to use for your render timer. Since the timer isn’t multi-threaded, it will take as long as it needs to perform the render but leave 1 ms free between renders to respond to mouse events and other GUI updates.
💡 Install the
System.Drawing.CommonNuGet package even if you don’t think you need it. Using the common library instead of native
System.Drawingwill ensure your program can be compiled for .NET Core if you decide to upgrade later. It also ensures you can pass
System.Drawingobjects to .NET Standard libraries which use
System.Drawing.Commonunder the hood.
- GitHub: Starfield Windows Forms GUI