This article has been provided courtesy of MSDN.
Contents
Introduction
During a recent project, one of the requirements was to show an animated GIF on a Microsoft® .NET Compact Framework Windows® Form. Version 1.0 of the .NET Compact Framework does not include the capability to display animated GIF files nor does it incorporate the ImageAnimator
helper class from the full .NET Framework. The ImageAnimator
class allows animation of an image that has time-based frames.
Even though it is possible to write C# code to read the animated GIF in GIF86a format, I have chosen a more simplistic and straightforward way to display animation in my program.
Creating a Storyline
If you open an animated GIF in the GIF editor of your choice, you will see that this file consists of a few images (frames) that follow each other:
Figure 1. Animation frames
These images are stored in a compressed format with information on the size, quantity and delay time between the frames. This information is read by the program that displays the animation.
Many of the GIF editors allow you to extract the image frames into a sequential "storyboard" of the frames:
Figure 2. Storyboard
I saved this into a single bitmap file, which I later converted into GIF format as it uses less memory within the .NET Compact Framework. Now I am going to show you how to use this image to create a .NET Compact Framework-based Animation control.
Let's animate
The way in which we're going to animate the bitmap is rather simple. It relies on the fact that when you're using an image in the .NET Compact Framework, you don't necessarily have to display the entire image you've loaded into memory. One of the overloaded methods of the graphics.DrawImage
method accepts a Rectangle
object as a parameter. This rectangle will be our way of framing each image in the storyboard bitmap. By moving the position of the frame rectangle, we can dynamically load a different section of the bitmap to be displayed on our form.
We add a new class AnimateCtl
into a .NET Compact Framework project and derive this class from System.Windows.Forms.Control
:
using System;
using System.Windows.Forms;
using System.Drawing;
using System.Drawing.Imaging;
public class AnimateCtl : System.Windows.Forms.Control
{
}
Let's add a public
Bitmap
property to the class that will be used to pass the bitmap from the client. Don't forget to declare a private
member for the bitmap, for use within the class:
private Bitmap bitmap;
public Bitmap Bitmap
{
get
{
return bitmap;
}
set
{
bitmap = value;
{
{
The control we create will draw the frames by using the DrawImage
method of the Graphics
object retrieved from the control:
private void Draw(int iframe)
{
int XLocation = iframe * frameWidth;
Rectangle rect = new Rectangle(XLocation, 0, frameWidth, frameHeight);
graphics.DrawImage(bitmap, 0, 0, rect, GraphicsUnit.Pixel);
}
This method accepts the current frame number that needs to be drawn. We then create the drawing rectangle by calculating its left position.
In order to implement the looping logic of the control, I've chosen to utilize the System.Windows.Forms.Timer
.
Although quite a few other options exist to provide the same functionality such as System.Threading.Timer
or even create a separate thread, the usage of the System.Windows.Forms.Timer
proved to be a more simple and convenient approach. Let's add the following code in the control's constructor:
public AnimateCtl()
{
graphics = this.CreateGraphics();
fTimer = new System.Windows.Forms.Timer();
fTimer.Tick += new System.EventHandler(this.timer1_Tick);
}
In the constructor, we cache the Graphics
object from the control's instance and create a new instance of the Timer
. And we should not forget to hook into the Timer
's Tick
event. We are ready to insert the StartAnimation
method that will actually start the animation:
public void StartAnimation(int frWidth, int DelayInterval, int LoopCount)
{
frameWidth = frWidth;
loopCount = LoopCount;
loopCounter = 0;
frameCount = bitmap.Width / frameWidth;
frameHeight = bitmap.Height;
this.Size(frameWidth, frameHeight);
fTimer.Interval = DelayInterval;
fTimer.Enabled = true;
}
This method accepts a few parameters that are very important for animation: frame width, delay interval and loop count.
And don't forget the looping logic:
private void timer1_Tick(object sender, System.EventArgs e)
{
if (loopCount == -1) {
this.DrawFrame();
}
else
{
if (loopCount == loopCounter) fTimer.Enabled = false;
else
this.DrawFrame();
}
}
private void DrawFrame()
{
if (currentFrame < frameCount-1)
{
currentFrame++;
}
else
{
loopCounter++;
currentFrame = 0;
}
Draw(currentFrame);
}
In the code above, in the timer1_Tick
event, we check the loopCount
that keeps track of how many loops have already been drawn and compare it to the loopCounter
that we captured when the StartAnimation
method was called.
Movie Time!
We are done with the AnimateCtl
and ready to test it in action. As a first step, we need to add the image file with "storyboard" into your project. We can do this by making this file an embedded resource or just by telling Visual Studio .NET 2003 to copy this file as a part of the project. Right click on the project in the solution explorer and select "Add Existing Item…" from the pop up menu. Browse to the image file and make sure that the Build Action
property for this file is set to Content
.
Now let's insert the following into your Form
's constructor:
public Form1()
{
InitializeComponent();
animCtl = new AnimateCtl();
animCtl.Bitmap = new Bitmap(@"\Program Files\AnimateControl\guestbk.gif");
animCtl.Location = new Point(50, 50);
this.Controls.Add(animCtl);
}
In the code above, we assign the Bitmap
property of the animation control with the Bitmap
object that was created from our image file.
Place two buttons on your form in the designer and add the code to their click events:
private void button1_Click(object sender, System.EventArgs e)
{
animCtl.StartAnimation(92, 100, 3);
}
private void button2_Click(object sender, System.EventArgs e)
{
animCtl.StopAnimation();
}
When running the project and tapping on the "Start Animation" button, you should see the animation:
Figure 3. The final product
The amount of frames incorporated into the pseudo-animated GIF files could vary as well as the delay time between the frames. You would certainly need to adjust the DelayInterval
parameter when calling the StartAnimation
method for different animations.
In no way is this code considered in its final version. The AnimateCtl
does not provide all required functionality that could be incorporated into animated GIFs. For example, AnimateCtl
control can't handle a different delay time between the frames. Such as, you might want to show the very first frame a little bit longer than others. The code provided with this article is a good starting point for you to extend this control for your needs.
Please keep in mind that displaying a high-resolution graphics animation could impose a heavy load on the system resources. Be aware of the memory and resource constraints of some of the devices you could be running this code on. Don't forget to test it thoroughly and make sure that your application is not hogging up all the memory or taking up all the processor time.
Conclusion
Although the .NET Compact Framework is a subset of the full .NET Framework, developers still have the power to create compelling user interfaces which are more attractive to end users. By utilizing available GIF editor tools and the drawing capabilities of the .NET Compact Framework, we are able to display the animation in their Smart Device projects.
Links