Introduction
In my other article, I have explained how you can write a SignalBar control with Designer Support. This article explains how you can write a simple ImageList
-enabled Animation Control (ImAC) in .NET CF. There is another Animation control written in CodeProject that allows you to animate .GIF files. The control that I want to create here is slightly different. It can animate virtually any image that is supported by the .NET CF (.bmp, .gif, .png, .jpg) and allows a user to create custom animation during design time. This is possible because ImAC comes with the Designer Support facility.
Background
Unfortunately, writing custom controls in .NET CF is not as straightforward as in the full .NET Framework. There is no ‘Designer Support’ (DS) provided! Fortunately, there are articles to help you add the DS to .NET CF. One of them is written by Jim Wilson: 'Adding Designer Support to the .NET Compact Framework DateTimePicker Control’. Therefore, I strongly suggest you read his article as the pre-requisite to understand my article. In this article, therefore, I will concentrate more on writing the actual control itself. However, the source code will come complete with the DS and ready for use.
Using the code
You can download the demo ZIP file provided above. There are two assemblies, the run time and design time. Extract the run time assembly (HT.CustomControl.AnimationCtrl.dll) to \Program Files\Microsoft Visual Studio .NET 2003\CompactFrameworkSDK\v1.0.5000\Windows CE\ and create a subfolder Designer if it dosn’t exist and copy over the design time assembly (HT.CustomControl.AnimationCtrl.Design.dll). Next, add it to your VS.NET Toolbox (View->Toolbox or Ctrl+Alt+X). You may want to create your own control tab by right-click and Add Tab. Then add the control assembly by selecting Add/Remove Items… menu item to the newly created tab. Then browse and find the HT.CustomControl.AnimationCtrl.Design.dll assembly from the {Installation Directory}\Designer\ folder, click OK, then you should be able to see and drag-&-drop the control to your form.
The code design
The main single important file in the solution is AnimationCtrl.cs under the runtime AnimationControl project. This file contains all the fields, properties, and methods that the control is using. The file starts with the class AnimationCtrl
declaration which inherits from the Control
class:
public class AnimationCtrl : System.Windows.Forms.Control
{
...
}
In the next block following the class declaration, the fields required in defining the class are specified.
...
#region fields
protected Bitmap m_bmp; protected Graphics m_graphics;
protected int iloop;
protected int nLoop;
protected bool bStarted;
protected int ci; protected Timer tmrAnm = null; private ImageList imglist = null;
#endregion
...
Following the fields declaration, the following properties are defined. Please note that each property has a preamble to it. The preamble defines the attributes required to expose the property to the Visual Studio .NET 2003 (VSNET) Property window.
#region properties
#if NETCFDESIGNTIME
[System.ComponentModel.Browsable(true)]
[System.ComponentModel.Category("Behavior")]
[System.ComponentModel.DefaultValue(-1)]
[System.ComponentModel.Description("Determines the number" +
" of loops, -1 will loop forever")]
#endif
public int Loop {
get
{
return nLoop;
}
set
{
nLoop = value;
}
}
#if NETCFDESIGNTIME
[System.ComponentModel.Browsable(true)]
[System.ComponentModel.Category("Behavior")]
[System.ComponentModel.DefaultValue(1000)]
[System.ComponentModel.Description("The animation" +
" interval in miliseconds")]
#endif
public int Interval {
get
{
return tmrAnm.Interval;
}
set
{
tmrAnm.Interval = value;
}
}
#if NETCFDESIGNTIME
[System.ComponentModel.Browsable(true)]
[System.ComponentModel.Category("Appearance")]
[System.ComponentModel.DefaultValue(null)]
[System.ComponentModel.Description("The list of images to be animated")]
#endif
public ImageList ImageList
{
get
{
return imglist;
}
set
{
imglist = value;
}
}
#endregion
The following is a snapshot of the Property window in VS.NET of AnimationCtrl
. We should see all the exposed properties defined above. Note how the preamble System.ComponentModel.Description
value appear at the bottom of the property window.
Figure 1: Snapshot of AnimationCtrl property window
To operate the control programmatically, you can call the Start()
and Stop()
methods. When you start the animation and stop and start again, the animation should resume from the last frame where it stopped. However, in other circumstances, you can stop animation and when you start it again, it starts from the beginning. The latter operation can be achieved easily by calling the Reset()
method. The following describes the three methods.
public void Reset()
{
Stop();
ci = 0;
}
public void Start()
{
if(!bStarted)
{
bStarted = true;
tmrAnm.Enabled = true;
}
}
public void Stop()
{
bStarted = false;
tmrAnm.Enabled = false;
}
Since the control will be driven by a timer object, tmrAnm
, declared earlier, each time a tick event generated by the timer occurs, it will ask the control to repaint itself with the next image in the ImageList
, imagelist
.
Before it knows which handler will be called when the timer tick event occurs, you should bind the handler (tmrAnm_Tick()
) with the timer object (tmr_Anm
) when initializing the control. When it is done, the handler can call the Invalidate()
function to generate the OnPaint
message. You may want to consult the MSDN library to know more about Invalidate()
, Refresh()
, and Update()
. When the OnPaint
message is received by the control, by default the OnPaint
function is called.
tmrAnm = new Timer();
tmrAnm.Interval = 1000;
tmrAnm.Tick += new EventHandler(tmrAnm_Tick);
private void tmrAnm_Tick(object sender, EventArgs e)
{
Invalidate();
}
The logic of the control lies in the OnPaint
function. We can write a function to separate the logic and the UI for a good design practice. In this example however, I am not going to do so as the function calls create overhead. Although it may not be too much different in reality but for a rapid redrawing it could add some delays to the animation.
The OnPaint
function will iterate the images in the ImageList
starting from the image pointed by 'ci
'. Normally 'ci
' is initialized to zero. It then checks if it should loop forever until a Stop
method is called or loop nLoop
times. The OnPaint
call is ended by blitting memory bitmap, m_bmp
, to the screen.
#region overridden methods
protected override void OnPaint(PaintEventArgs e)
{
CreateMemoryBitmap();
m_graphics.Clear(this.BackColor);
if(imglist.Images.Count > 0)
{
Bitmap bmp = new Bitmap(imglist.Images[ci]);
m_graphics.DrawImage(bmp, 0, 0);
if(bStarted)
{
if(nLoop == -1)
{
ci = (ci + 1) % imglist.Images.Count;
}
else
{
if(iloop >= nLoop)
{
Stop();
iloop = 0;
}
else
{
iloop++;
}
ci = (ci + 1) % imglist.Images.Count;
}
}
}
e.Graphics.DrawImage(m_bmp, 0, 0);
}
protected override void OnPaintBackground(PaintEventArgs e)
{
}
The last thing to do is the control should repaint itself by calling the Invalidate()
function whenever it is resized. Therefore, we need to override the OnResize()
function as described below:
protected override void OnResize(EventArgs e)
{
base.OnResize (e);
this.Invalidate();
}
#endregion
Finally, the initialization of the control is as provided below:
#region initialization
public AnimationCtrl()
{
InitializeComponents();
}
private void InitializeComponents()
{
imglist = new ImageList();
tmrAnm = new Timer();
tmrAnm.Interval = 1000;
tmrAnm.Tick += new EventHandler(tmrAnm_Tick);
ci = 0;
bStarted = false;
nLoop = -1;
iloop = 0;
}
Conclusion
Writing a custom control is an exciting experience, you can customize the control to behave as you want. Subclassing a control is normally quite straightforward. However, in .NET CF it is a little bit painful since you don't have Designer Support available automatically. Thanks to people who have written articles on how to add Designer Support to writing custom controls in .NET CF. This makes my life a lot easier when developing application with .NET CF.
History
- Version 1.0 - Initial release of ImageList-enabled Animation Control (ImAC).