Click here to Skip to main content
Click here to Skip to main content

A thermometer control

By , 9 Jul 2007
 

Screenshot - Thermometer_demo.jpg

Introduction

This article demonstrates a complex analog-style thermometer control in C#. The control is in pure managed code. Most visual elements can be adjusted through the control's properties.

Layout

Due to the flexibility of the control, it can be used to display all sorts of data and not just degrees as used in this example.

Screenshot - Layouts.jpg

Architecture

The control consists of 2 classes: ManometerBase and Thermometer.

Screenshot - architecture.jpg

The implementation is done in the Thermometer class.

Code

All painting is done in the OnPaint method of the Thermometer class. Every element has its own Paint method and the methods are marked protected virtual.

protected override void OnPaint(PaintEventArgs e)
{
    // Set smoothingmode to AntiAlias
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    // Shadow
    PaintShadow(e.Graphics);
    // Background
    PaintBackground(e.Graphics);
    // Border
    PaintBorder(e.Graphics);
    // Inner shadow
    PaintInnerShadow(e.Graphics);
    // Bars
    PaintBars(e.Graphics);
    // Numbers
    PaintNumbers(e.Graphics);
    // Paint the text(s)
    PaintText(e.Graphics);
    // Paint the Arrows
    PaintArrows(e.Graphics);
    // Reflex
    PaintReflex(e.Graphics);
    // Reset smoothingmode
    e.Graphics.SmoothingMode = SmoothingMode.Default;
}

Painting the numbers

Most methods are straightforward, but aligning the numbers in the ellipse is somewhat tricky.

protected virtual void PaintNumbers(Graphics g)
{
    double tmpAngle = StartAngle;
    for (double d = Min; d<= Max; d+= Interval)
    {
        String text = Math.Round(d, Decimals).ToString();
        PointF p = 
            CalcTextPosition(
            tmpAngle, MeasureText(g, text, Font, (int)numberRect.Width));
        if (ClockWise)
            tmpAngle -= NumberSpacing;
        else
            tmpAngle += NumberSpacing;
            g.DrawString(text, Font, new SolidBrush(ForeColor), p);
    }
}

The solution is to find the center point of the text and then calculate the offset based on the text's position in the ellipse and its dimensions.

private PointF CalcTextPosition(double a, SizeF size)
{
    PointF p = PointInEllipse(numberRect, a);
    p.X -= (float)((size.Width/2)*(1 + Math.Cos(Convert.ToRadians(a))));
    p.Y -= (float)((size.Height/2)*(1 - Math.Sin(Convert.ToRadians(a))));
    return p;
}

The private method CalcTextPosition makes a call to PointInEllipse. This method calculates a point in an ellipse based on the angle and the size of the ellipse, which is simple math.

private static PointF PointInEllipse(RectangleF rect , double angle)
{
    double r1 = rect.Width/2;
    double r2 = rect.Height/2;
    double x = 
        (float)(r1 * Math.Cos(Convert.ToRadians(angle))) + r1 + rect.X;
    double y = 
        -(float)(r2 * Math.Sin(Convert.ToRadians(angle))) + r2 + rect.Y;
    return new PointF((float)x, (float)y);
}

Using the code

To test the control, download the demo project and then build and run it. To change the appearance of the control, use the properties:

  • Interval - The interval between each number on the control; this is a floating point number.
  • Max - The maximum on the scale.
  • Min - The minimum on the scale.
  • StartAngle - The starting point in degrees.
  • StoreMax - Show the maximum stored value (red arrow).
  • StoredMax - The stored maximum.
  • TextDescription - Set the lower text displayed on the control.
  • TextUnit - Set the text to describe the Units used.
  • Value - The value and point of the arrow.
  • ArrowColor - The color of the arrow.
  • BackColor - The inner color of the control.
  • BarsBetweenNumbers - The number of lines between two displayed numbers. Note that the alignment of the lines is dependent on the properties NumberSpacing and Interval.
  • BorderWidth - The width of the outer border.
  • ClockWise - Lay the numbers out clockwise or counter-clockwise.
  • Decimals - The number of decimals displayed; default is 0.
  • NumberSpacing - The spacing between the numbers in degrees.

History

  • June 09, 2007: Initial release.
  • July 08, 2007:
    • Fixed missing dispose on some objects.
    • Fixed a bug where the designer would hang if the control was added from the toolbar.
    • Changed default text on Unit and Description property.
    • Added the layout section in this document.
    • Added a demo download.
    • Removed solution from source control.

License

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

About the Author

Niel M.Thomas
Software Developer (Senior)
Denmark Denmark
Member
Name: Niel Morgan Thomas
Born: 1970 in Denmark
Education:
Dataengineer from Odense Technical University.
More than 15 years in IT-business.
Current employment:
Working with application development in a major Danish company that produce medical equipment.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalvery very usefulmemberbatsword7 Nov '10 - 18:41 
GeneralRe: very very usefulmemberNiel M.Thomas7 Nov '10 - 19:17 
GeneralRe: very very usefulmemberbatsword11 Nov '10 - 15:02 
GeneralMy vote of 4memberMember 366671421 Jul '10 - 1:39 
GeneralKudos, and more kudosmemberjon_sigler30 Dec '08 - 16:42 
GeneralVisual Studio 6memberCodeHead27 Feb '08 - 3:13 
GeneralRe: Visual Studio 6memberThe Dogcow Farmer14 Sep '08 - 2:31 
GeneralPrograming style suggestions!memberMartin#16 Jul '07 - 23:53 
Hello,
 
First I have to say that I couldn't test your Control, cause I have only .Net1.1 running.
But I looked threw it and I think you did a very good job!
 
My suggestions:
1)
You are mixing up the initialization of your private fields for the properties, which makes it difficult to read the code.
For example you sometimes initialize directly in the decleration (which I would prevere), and sometimes in the constructor.
As I could read in the responses to your article, this behavior made you a problem, because you forgot to initialize a field.
You also use const members and sometimes not for the initialization.
 
2)
The connection to a delegate of the base class is bad style:
Resize += new EventHandler(Termometer_Resize);
You simply could override the method which calls the event (Which I would also suggest for you custom events, as you are firing the events in the setter of the properties):
protected override void OnResize(EventArgs e)
{
    //Do your stuff here!
    base.OnResize (e);
}
3)
Naming a private member of a property 'value' (this.value = value; ), is more than confusing.
 
4)
I would allways use a "!="-check in the setter of a property before doing stuff (performance reason).
if(value != storeMax)
{
    storeMax = value
    //do stuff
}
5)
Resources issues:
5.1)
Your thermometer class should implement a dispose method which disposes the class field:
private Brush textureBrush;
5.2)
It seems that you prevent to use a "using" block inside a "using" block
GraphicsPath path = new GraphicsPath();
path.AddLine(p1, p2);
path.AddLine(p2, p3);
//Fill the arrow
using (Brush b = new SolidBrush(c))
{
  g.FillPath(b, path);
}
path.Dispose();
No reason for that, I think:
using (GraphicsPath path = new GraphicsPath())
{
    path.AddLine(p1, p2);
    path.AddLine(p2, p3);
    //Fill the arrow
    using (Brush b = new SolidBrush(c))
    {
        g.FillPath(b, path);
    }
}
6)
private void InitializeComponent()
{
Name = "Termometer"; //Thermometer
Laugh | :laugh:
 
All the best,
 
Martin

AnswerRe: Programing style suggestions!memberNiel M.Thomas18 Jul '07 - 1:06 
GeneralRe: Programing style suggestions!memberMartin#18 Jul '07 - 1:13 
GeneralRe: Programing style suggestions!memberMichal Brylka19 Jul '07 - 9:31 
GeneralRe: Programing style suggestions!memberJani Mukkavaara24 Jun '08 - 2:32 
GeneralRe: Programing style suggestions!mvpMartin#24 Jun '08 - 7:04 
QuestionAm I doing something wrong?memberrob.iles29 Jun '07 - 11:36 
GeneralRe: Am I doing something wrong?memberrob.iles30 Jun '07 - 12:44 
QuestionVery cool, but...memberThe Marshal20 Jun '07 - 19:28 
AnswerRe: Very cool, fixedmemberNiel M.Thomas20 Jun '07 - 20:43 
AnswerRe: Very cool, but...memberKevin Gallagher21 Jun '07 - 6:03 
GeneralGood controlmembernitikin19 Jun '07 - 1:20 
GeneralGreat Controlmembermerlin98112 Jun '07 - 3:14 
Generalnice, but...memberFZelle11 Jun '07 - 20:40 
GeneralRe: nice, but...memberThe Marshal21 Jun '07 - 8:06 
GeneralRe: nice, but...memberFZelle21 Jun '07 - 21:17 
GeneralRe: nice, but...membercraigg7517 Jul '07 - 3:59 
GeneralRe: nice, but...memberFZelle17 Jul '07 - 4:30 
Generalkudosmemberpita200011 Jun '07 - 16:00 
Generallovelymemberbryce11 Jun '07 - 13:14 
GeneralCoool...memberPaul Selormey11 Jun '07 - 12:29 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 9 Jul 2007
Article Copyright 2007 by Niel M.Thomas
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid