Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Sliding Scale Gauge

0.00/5 (No votes)
17 Oct 2008 1  
An endless moving graphical scale gauge with a linear representation of data.

Introduction

The sliding scale is a very simple, but fast, performing, and powerful .NET user control, figured as a graphical scale gauge with a linear representation of data. The instrument consists of a static pointer (needle) and a movable linear scale. The static needle refers to the current position of the moving scale, and shows the input values through a graphical position along the scale. The sliding scale is endless, and can represent an unlimited value (type double) at a certain moment.

Background

On the CodeProject, there are several very useful and good-looking instruments already available. I want to take the trouble to add one more to the chain.

This project is developed in C#, with SharpDevelop and .NET 2.0, using GDI+.

The sliding scale is inherited from System.Windows.Forms.UserControl, with an overridden OnPaint method and some new properties:

Value The current position of the scale.
ScaleRange The visible range of the scale.
LargeTicksCount The number of large ticks.
SmallTickCount The number of small ticks.
ShadowEnabled Is the shadow enabled?
ShadowColor The shadow color of the component.
NeedleColor The color of the scale needle.

In the code, the following properties from the parent control are used:

BackColor The background color of the component.
BorderStyle Indicates whether the control should have a border.
ForeColor The foreground color used to display the text and ticks.

The sliding scale is composed of four layers.

  1. Background (with a background color or transparent) and border.
  2. Linear scale (with a number of small and / or large ticks with their values).
  3. Shadow (for 3D-like look that can be disabled).
  4. Needle.

The complete logic is placed in the OnPaint method. There is a small invariant part for purists, closed in a signed region, which can be moved outside the OnPaint. Everything else happens in a few simple steps:

  1. Calculate the position of the first large tick, that can be showed, and its value.
  2. Iterate over all other large ticks, and draw them and their value numbers.
  3. Iterate over all small ticks, if exists, and draw them.
  4. Draw the shadow, if enabled, with a LinearGradientBrush.
  5. Draw the needle over all others in its defined color.
protected override void OnPaint(PaintEventArgs e)
{
    base.OnPaint(e);
        
    // Draw simple text, don't waste time with luxus render:
    e.Graphics.TextRenderingHint = TextRenderingHint.SingleBitPerPixelGridFit;
            
    #region Can be moved outside OnPaint ...
            
    // Calculate help variables
    int W = this.ClientRectangle.Width;
    int H = this.ClientRectangle.Height;

    int Wm = W / 2;
    int Hm = H / 2;
            
    // Calculate distances between ticks
    double largeTicksDistance = scaleRange / largeTicksCount;
    double smallTicksDistance = largeTicksDistance / (smallTicksCount+1);
            
    // Calculate number of pixel between small ticks
    float smallTicksPixels = (float)(W/scaleRange*smallTicksDistance);
            
    #endregion Can be moved outside OnPaint
            
    // Calculate first large tick value and position
    double tickValue = Math.Floor((curValue-scaleRange/2) / 
                       largeTicksDistance) * largeTicksDistance;
    float  tickPosition = (float)Math.Floor(Wm - W/scaleRange*(curValue-tickValue));
            
    // Create drawing resources
    Pen pen = new Pen(ForeColor);
    Brush brush = new SolidBrush(ForeColor);
            
    // For all large ticks
    for (int L=0; L<=largeTicksCount; L++)
    {
        // Draw large tick
        e.Graphics.DrawLine(pen, tickPosition-0,0, tickPosition-0,15);
        e.Graphics.DrawLine(pen, tickPosition-1,0, tickPosition-1,15);

        // Draw large tick numerical value
        StringFormat sf = new StringFormat();
        sf.Alignment = StringAlignment.Center;
        e.Graphics.DrawString(Math.Round(tickValue,2).ToString(),
                              Font, brush,
                              new PointF(tickPosition, Hm),sf);
                
        // For all small ticks
        for (int S=1; S<=smallTicksCount; S++)
        {
            // Update tick value and position
            tickValue += smallTicksDistance;
            tickPosition += smallTicksPixels;
                    
            // Draw small tick
            e.Graphics.DrawLine(pen, tickPosition,0,tickPosition,10);
        }
                
        // Update tick value and position
        tickValue += smallTicksDistance;
        tickPosition += smallTicksPixels;
    }
            
    // Dispose drawing resources
    brush.Dispose();
    pen.Dispose();
            
            
    if (ShadowEnabled)
    {
        LinearGradientBrush LGBrush = null;
                
        // Draw left side shadow
        LGBrush = new LinearGradientBrush(new Rectangle(0, 0, Wm, H),
                                          Color.FromArgb(255, ShadowColor),
                                          Color.FromArgb(0, BackColor),
                                                  000, true);
        e.Graphics.FillRectangle(LGBrush, new Rectangle(0,0, Wm, H));
        
        // Draw right side shadow
        LGBrush = new LinearGradientBrush(new Rectangle(Wm+1, 0, Wm, H),
                                          Color.FromArgb(255, ShadowColor),
                                          Color.FromArgb(0, BackColor),
                                          180, true);
        e.Graphics.FillRectangle(LGBrush, new Rectangle(Wm+1, 0, Wm, H));
          
        LGBrush.Dispose();
    }

    // Draw scale needle
    e.Graphics.DrawLine(new Pen(NeedleColor), Wm-0,0, Wm-0,H);
    e.Graphics.DrawLine(new Pen(NeedleColor), Wm-1,0, Wm-1,H);
}

Using the code

This control has a good set of default property values so that, once included in your own solution, it can be simply selected from the tools palette, dropped to your form, and used immediately. Otherwise, there are only a few other listed properties to be configured. You just need one line of code to get it working:

slidingScale1.Value = your value here!

The included archive files contain the complete source code and the compiled binaries of the component, and a simple test application.

Possible enhancements

There are many possibilities to improve this control. Here are some ideas, if someone wants to do it:

Owing to the LinearGradientBrush shading, there is a three dimensional feel, the sliding scale looks already like a real instrument. I personally miss some flexibility in order to draw the numbers on the face of the scale and to customize the lengths of the ticks.

In order to set up the instrument so that it behaves as a real instrument, we must look at two important areas:

  1. The scale should be limited and/or round.
    • A real scale is not an endless ribbon, but it has a real size.
    • Limiting the scale is not a hard problem. We can do that by adding two new properties, MinValue and MaxValue, and by controlling the changes of the existing Value property. Only values from a defined range should be accepted.
    • A round scale is not that simple to realize. For this feature, we need several new properties and a good dose of calculations.
  2. The scale shouldn't react promptly to value changing.
    • A real scale has a self moment of inertia, and cannot jump in a short time from one end to the other. Value changing must be realized so that the scale moves itself and slides smoothly in small steps from the old position to the new one. It should immediately begin sliding/rotating from the current position towards and arrive at the end position after a certain time.
    • This behavior can be realized so that we first set a new value as the desired value when the current value should be changed, and enable a timer which increments (decrements) the current value in small steps until it will arrive at the desired one.
    • Simpler said, we should make an animated sliding scale.

These are some other desired features:

  • Vertical scale representation.
  • Scale with marked tolerance limits (OTL, UTL, ...).
  • Scale with different colored regions.

The sliding scale shown here, in terms of measurement technology, is an “indicator”. We can make it a “control”. Therefore, we should override the OnMouseDown and OnMouseMove methods to process mouse events, and implement a ChangeValue method to announce value changing.

Conclusion

This is the preliminary version of the control, and many features have to be implemented yet. I will attempt to cover this as soon as possible.

Any suggestions/comments/feedback is welcome and highly appreciated.

If you like it, please vote, and if you use it commercially, describe your success story in the discussion board below.

History

  • 08.10.2008 - Version 1.0 released.
  • 16.10.2008 - Version 2.0 released. Added new properties and vertical orientation.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here