Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / Win32

Animated Controls using graphic layers

4.92/5 (28 votes)
25 Feb 2015CPOL9 min read 61K   6.2K  
This article discusses how to create animated controls using graphic layers.

Abstract Table of Contents

This article presents a detailed discussion of using multiple graphic layers to create complex dynamic Windows Forms controls.

Often programmers are faced with significant challenges when implementing a complex graphic object. When multiple graphics layers are incorporated, the complexity can be significantly reduced. An additional benefit, significant execution time reduction, may also be obtained.

Introduction Table of Contents

Attitude Indicator P-20 R-20 Attitude Indicator Attitude Indicator P20 R20

Recently, while developing a demonstration of an interthread messaging system, I needed to create a simulation of a number of aircraft cockpit instruments. I really didn't need to create the instrument graphics, but it was a challenge that I couldn't resist. One of these instruments is known as an attitude indicator.

This instrument displays both pitch (nose up and down) and roll (wing up and down). In modern aircraft, this instrument has been replaced to a large extent by the electronic Attitude and Heading Reference System (AHRS). However, the attitude indicator can be driven by only two inputs (pitch and roll). Therefore, by using the attitude indicator, I can simulate the aircraft's attitude without resorting to a simulation of the AHRS.

The attitude indicator is a composite control. A small symbolic aircraft is fixed within the instrument. The top half of the instrument dial is blue, representing the sky; and the bottom half is brown, representing the ground. A bank index at the top of the instrument shows the angle of bank marked on the banking scale with lines and triangles that represent 0°, 10°, 20°, 30°, 45°, and 60°. A pitch index at the center of the instrument shows the amount of pitch. The symbolic aircraft moves up and down over the pitch index while the roll is represented by a clockwise or counterclockwise rotation of the sky-ground bank index.

In the following discussions, properties that are specified by the developer are displayed in BoldMixedCase text. Variables, used internally by the software, are displayed in italicized_lowercase text with underscores separating words.

Table of Contents

The symbol Table of Contents returns the reader to the top of the Table of Contents.

Visual Properties Table of Contents

The AttitudeIndicator control has properties that affect the user's visual image. The developer specifies the control's top-left corner by dragging the control from the ToolBox to a position on the form. This position becomes ( 0, 0 ) in the control's graphic environment.

The height of the AttitudeIndicator control is the same as the width of the control. The width of the control is specified using the resizing handles of the Visual Studio Designer [^] or by setting a new value for the control's Size [^] property or by specifying a value for the ControlWidth property. The control's width is limited to multiples of 50 from 150 to 500. If the control is resized, the values of all of the control's components are recomputed based upon the control's new width.

The CurrentPitch and CurrentRoll properties are the means by which pitch and roll, respectively, are communicated to the control. A change in either value will cause the display to be updated. Both CurrentPitch and CurrentRoll properties accept decimal values.

Implementation Table of Contents

With the exception of the background and the symbolic aircraft, the control's graphic components are dynamic and reflect the aircraft's instantaneous pitch and roll. This makes the attitude indicator a relatively complex instrument to simulate. However, if the graphic is broken down into separate graphic layers, the complexity can be reduced and the responsiveness of the control can be significantly increased.

For the attitude indicator I chose seven graphic layers:

Background      The instrument body layer.
Stationary      The symbolic aircraft layer.
SkyGround      The blue/brown layer.
Pitch      The pitch scale layer.
TopBottom      The caps that appear at the top and bottom of the SkyGround layer.
Roll      The bank index (roll) scale layer.
Indicator      The layer onto which the SkyGround, Pitch, TopBottom, and Roll layers are combined.

With the exception of the Indicator layer, each layer is created once and is recreated only on a resize event. It is only during the drawing of the Indicator layer that existing SkyGround, Pitch, TopBottom, and Roll layers are combined (translated and rotated) to create the aircraft attitude image. In the OnPaint event handler, the Background, Indicator, and Stationary layers are combined.

Layering Table of Contents

Readers are reminded that, in the graphics environment, objects that are drawn later override objects that were drawn earlier. This is true when layers are created or combined. So the drawing order for layers is:

Background
Indicator
SkyGround
Pitch
TopBottom
Roll
Stationary

Initialization Table of Contents

The attitude indicator is implemented as a Windows Forms Control. The dimensions of all of the graphics are determined by the control's width that can vary from 150 pixels to 500 pixels in width, incremented by 50 pixels. The OnCreateControl and OnResize event handlers invoke revise_geometry_values. That method recomputes the variables used to draw the control's components. It also forces all layers to be redrawn.

Background Layer Table of Contents

Background Layer

The Background layer is the bottommost layer displayed in the control. The width and height of the Background layer equal the width and height of the control. Drawing the Background layer is composed of the following steps:

1.  Draw and fill a rounded rectangle
2.  Draw and fill an instrument circle
3.  Draw an inner circle, inside of which all other components are drawn

Once drawn, unless the AttitudeIndicator control is resized, the Background layer is never redrawn.

Probably the only step of interest is drawing a rounded rectangle.

C#
// ************************************ rounded_rectangle_path

/// <summary>
/// computes the GraphicsPath of a rounded rectangle
/// </summary>
/// <param name="x">
/// x coordinate of the upper left corner of the rectangle
/// </param>
/// <param name="y">
/// y coordinate of the upper left corner of the rectangle
/// </param>
/// <param name="width">
/// width of the rectangle
/// </param>
/// <param name="height">
/// height of the rectangle
/// </param>
/// <param name="radius">
/// radius of the circle that defines the rounded corner
/// </param>
/// <returns>
/// the GraphicsPath that defines the rounded rectangle
/// </returns>
GraphicsPath rounded_rectangle_path ( int x,
                                      int y,
                                      int width,
                                      int height,
                                      int radius )
    {
    int           diameter = 2 * radius;
    GraphicsPath  path = new GraphicsPath ( );

    path.StartFigure ( );
    path.AddArc ( ( x + width - diameter ),
                  ( y + height - diameter ),
                  diameter,
                  diameter,
                  0.0F,
                  90.0F );
    path.AddArc ( x,
                  ( y + height - diameter ),
                  diameter,
                  diameter,
                  90.0F,
                  90.0F );
    path.AddArc ( x,
                  y,
                  diameter,
                  diameter,
                  180.0F,
                  90.0F );
    path.AddArc ( ( x + width - diameter ),
                  y,
                  diameter,
                  diameter,
                  270.0F,
                  90.0F );
    path.CloseFigure ( );

    return ( path );
    }

// ************************************ rounded_rectangle_path

/// <summary>
/// computes the GraphicsPath of a rounded rectangle
/// </summary>
/// <param name="rectangle">
/// the rectangle for which rounding is desired
/// </param>
/// <param name="radius">
/// radius of the circle that defines the rounded corner
/// </param>
/// <returns>
/// the GraphicsPath that defines the rounded rectangle
/// </returns>
GraphicsPath rounded_rectangle_path ( Rectangle  rectangle,
                                      int        radius )
    {

    return ( rounded_rectangle_path ( rectangle.X,
                                      rectangle.Y,
                                      rectangle.Width,
                                      rectangle.Height,
                                      radius ) );
    }

Stationary Layer Table of Contents

Stationary Layer

The Stationary layer contains those components of the AttitudeIndicator that must appear on top of all other AttitudeIndicator components. These include the symbolic aircraft and the fixed roll pointer.

The image is created from thirteen points, drawn or filled in various ways. The order in which the Stationary layer components are drawn is important.

Once drawn, unless the AttitudeIndicator control is resized, the Stationary layer is never redrawn.

SkyGround Layer Table of Contents

SkyGround Layer

The SkyGround layer contains the visual depiction of the sky and ground.

The sky is drawn from the top to the bottom, using a Linear Gradient Brush. The ground is drawn as two rectangles, both of the same color.

The two rectangles are separated by a distance equal to the ground portion of the Pitch layer. This allows the Pitch layer to be moved up and down as the aircraft pitch varies.

Once drawn, unless the AttitudeIndicator control is resized, the SkyGround layer is never redrawn.

Pitch Layer Table of Contents

Pitch Layer

The Pitch layer depicts the pitch index at the center of the instrument. It shows the amount of aircraft pitch.

The Pitch layer contains a pitch scale and a portion of the ground. These two components fit into the gap between the ground rectangles of the SkyGround layer.

Once drawn, unless the AttitudeIndicator control is resized, the Pitch layer is never redrawn.

TopBottom Layer Table of Contents

TopBottom Layer

The TopBottom layer compliments the SkyGround layer by providing upper and lower caps to that layer. It is purely cosmetic in nature.

Once drawn, unless the AttitudeIndicator control is resized, the TopBottom layer is never redrawn.

Roll Layer Table of Contents

Roll Layer

The Roll layer depicts the bank index at the top of the instrument. It shows the angle of bank marked on the banking scale with lines and triangles that represent 0°, 10°, 20°, 30°, 45°, and 60° of aircraft roll.

Once drawn, unless the AttitudeIndicator control is resized, the Roll layer is never redrawn.

Indicator Layer Table of Contents

Indicator Layer

The Indicator layer provides the canvas for the dynamic effects of aircraft pitch and roll. Whenever pitch or roll change, the Indicator layer is redrawn.

In the Indicator layer, each of the SkyGround, Pitch, TopBottom, and Roll layers are combined (translated and rotated) to create the aircraft attitude image.

The steps taken to create this image in the Indicator layer are:

  1. A clipping region is defined. This prevents any subordinate layer from overflowing the control bounds (set in the OnCreateControl and OnResize event handlers).
  2. The SkyGround layer image (bitmap) is rotated to the aircraft's CurrentRoll and drawn into the Indicator layer.
  3. The x- and y-coordinates for the Pitch layer are computed from the aircraft's CurrentPitch; the Pitch layer image (bitmap) is rotated to the aircraft's CurrentRoll; the origin of the Indicator layer is translated to the x- and y-coordinates computed earlier; the rotated Pitch layer image is drawn into the Indicator layer.
  4. The TopBottom layer image (bitmap) is rotated to the aircraft's CurrentRoll and drawn into the Indicator layer.
  5. The Roll layer image (bitmap) is rotated to the aircraft's CurrentRoll and drawn into the Indicator layer.

The method used to rotate the bitmaps was:

C#
// ********************************************* rotate_bitmap

// http://stackoverflow.com/questions/5172906/
//     c-rotating-graphics

/// <summary>
/// rotate a bitmap by a specified angle, in degrees
/// </summary>
/// <param name="bitmap">
/// bitmap to rotate
/// </param>
/// <param name="angle">
/// angle, in degrees, by which to rotate bitmap
/// </param>
/// <returns>
/// the rotated bitmap
/// </returns>
Bitmap rotate_bitmap ( Bitmap  bitmap,
                       float   angle )
    {
    Bitmap return_bitmap;
                                // create empty bitmap to hold
                                // rotated image
    return_bitmap = new Bitmap ( bitmap.Width,
                                 bitmap.Height );
                                // make a graphic object from
                                // the empty bitmap
    using ( Graphics graphic = Graphics.FromImage (
                                            return_bitmap ) )
        {
                                // translate rotation point to
                                // center of image
        graphic.TranslateTransform (
                            ( float ) bitmap.Width / 2.0F,
                            ( float ) bitmap.Height / 2.0F );
                                // rotate image
        graphic.RotateTransform ( angle );
                                //move image back
        graphic.TranslateTransform (
                            -( float ) bitmap.Width / 2.0F,
                            -( float ) bitmap.Height / 2.0F );
                                // draw passed in image onto
                                // graphic object
        graphic.DrawImage ( bitmap, new Point ( 0, 0 ) );

        graphic.ResetTransform ( );
        }

    return ( return_bitmap );
    }

Demonstration Table of Contents

Test Attitude Indicator

The demonstration program depicts how the AttitudeIndicator control reacts to changing pitch and roll. It also allows choosing which layers will be displayed.

When the program is launched, the Background, Indicator, and Stationary layers are displayed. If it is desired that layers that make up the Indicator layer be displayed, the Indicator layer checkbox must be cleared. This is required because the Indicator layer is a composite layer, made up of the SkyGround, Pitch, TopBottom, and Roll layers.

Conclusion Table of Contents

This article has presented a detailed discussion of using multiple graphic layers to create complex dynamic Windows Forms controls.

References Table of Contents

Aircraft Cockpit Instruments Table of Contents

For interested readers, I have included the following two links. A complete set of FAA Aviation Handbooks and Manuals can be found at the FAA site [^].

Programming Table of Contents

Development Environment Table of Contents

The AttitudeIndicator control was developed in the following environment:

Microsoft Windows 7 Professional Service Pack 1
Microsoft Visual Studio 2008 Professional
Microsoft .Net Framework Version 3.5 SP1
Microsoft Visual C# 2008

Downloads Table of Contents

In addition to the AttitudeIndicator control (the main topic of this article), the downloads also include a number of additional aircraft cockpit controls:

  • AirspeedIndicator
  • AltitudeIndicator
  • HeadingIndicator
  • RPMIndicator
  • VerticalSpeedIndicator

All of these controls use multiple graphic layers to achieve their effects. However, the AttitudeIndicator control is, by far, the most complex and thus was chosen for this article.

History Table of Contents

02/23/2015       Original article
02/25/2015       Modified article formatting

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)