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

A fast and performing gauge

By , 24 Aug 2012
 

Sample image

Introduction

This code shows you how to build a fast and performing control using C# and .NET 2.0.

I wrote a similar control as an ActiveX once, using C++, ATL, and GDI, and wondered if it is possible to write performing code using .NET and GDI+. I needed it for another project. So I wrote this little control to show that it actually works.

How the code works

The code consists of a C# application and a custom control. The custom control really is the interesting part.

Deriving from Control

We derive from Control as this doesn't give us all these properties we don't actually need like a usercontrol would give us, for example.

public partial class AGauge : Control

Dealing with properties

Hiding, shadowing unwanted properties

Well, there are still properties that show up in the designer that are not necessary. In C#, you can use the new keyword to get rid of them (shadows in VB).

public new Boolean AllowDrop, AutoSize, ForeColor, ImeMode

Overriding useful properties

For properties that you want to use but with a different behaviour, you can use the override keyword (if overrideable) to tell the program to call this overridden property instead of the implementation of the base class, which in our case is the implementation in Control.

public override System.Drawing.Color BackColor..
public override System.Drawing.Font Font..
public override System.Windows.Forms.ImageLayout BackgroundImageLayout..

Custom properties

To be able to further customize the control in the designer, we need to add some properties of our own. E.g.,

[System.ComponentModel.Browsable(true),
System.ComponentModel.Category("AGauge"),
System.ComponentModel.Description("The value.")]
public Single Value..

The Browsable attribute tells the designer to show the property in the toolbox or not. The Category attribute tells the designer where to show the property if the categorized view is selected, and the Description attribute adds a description to the property that the designer can show in the toolbox.

Events and Delegates

An event can carry additional information that is sent to the "listening" program, e.g., the form's event handler for this event.

Custom event arguments

We want the event to carry the number of the range the needle is in (if it changes from being in one range to being in another). To add some data to the event, we derive from the standard event args and add a variable which is initialized in the constructor. This will hold the extra information sent along.

public class ValueInRangeChangedEventArgs : EventArgs
{
 public Int32 valueInRange;
 public ValueInRangeChangedEventArgs(Int32 valueInRange)
 {
  this.valueInRange = valueInRange;
 }
}

Event delegate

The event handler "listening" for our event needs to be of a type that "understands" our event. With the delegate statement, we define this type.

public delegate void ValueInRangeChangedDelegate(Object sender, 
                     ValueInRangeChangedEventArgs e);

And the event

[Description("This event is raised if the value falls into a defined range.")]
public event ValueInRangeChangedDelegate ValueInRangeChanged;

The event is of the type we defined in the delegate statement. The Description attribute enables the designer to show a description for the event in the Toolbox.

Constructor

The constructor is called when the control is created, e.g., before it will be shown in the designer. Here, we set the style of the control to enable double buffering. This isn't really necessary since we will do our own double buffering, but it doesn't hurt to do so.

public AGauge()
{
 InitializeComponent();
 SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
}

Overriding member functions

We need to override some of the member functions.

First, we override OnPaintBackground to ensure that the background is not painted each time the control is refreshed, this uses too much CPU even if double buffering is enabled. One drawback is that we need to handle the drawing of a background image ourselves, but this isn't too much of a problem.

protected override void OnPaintBackground(PaintEventArgs pevent)
{
}

If the control is resized, we need to refresh it. So we override OnResize.

protected override void OnResize(EventArgs e)
{
  drawGaugeBackground = true;
  Refresh();
}

The global variable "drawGaugeBackground" is set to true to tell the control to completely redraw itself. Refresh forces the control to redraw, or if you like to call OnPaint, under the hood, a Windows message is sent, but this is a different story.

Finally, we need to override OnPaint to show some output to the user.

This is what our control really does, it shows the output to the user. It doesn't handle user input like a scrollbar would do. A scrollbar would override OnMouseMove, OnMouseDown, OnKeyPressed, and so on. OnPaint is the heart of our control.

protected override void OnPaint(PaintEventArgs pe)

OnPaint, which is called every time the control is redrawn, e.g., if the value of the gauge changed, determines if it should completely redraw itself or simply paint the background part with the performant function DrawImage. If the background hasn't changed, it only needs to draw the needle, thus avoiding costly GDI+ functions to be called every time. The background changes, e.g., if a property like a color has changed, or the control is resized, for example.

Conclusion

So it really is possible to write fast and performing controls with GDI+ if we use double buffering and blitting (DrawImage).

If you like VB better than C#, you can search for "SpeedyHMI" on SourceForge, this project I wrote contains this gauge written in VB.

Download, build, run and, Enjoy!

License

This article, along with any associated source code and files, is licensed under The zlib/libpng License

About the Author

A.J.Bauer
Software Developer (Senior)
Germany Germany
Member
Once upon a time there was a C64.
He was a very friendly bilingual fellow looking like a bread box..
His user could always wake him up in no time and ask him to LOAD "*",8,1
C64's friends in school were called CBM, PET and Schneider.
They were boring to talk to after a while so his user got him a girlfriend called Amiga and a business partner called IBM.
Since vitamins are good, his user also got him an Apple to chew on.
As time went by the C64 and his friends got old and eventually were buried in oblivion.
In good memory his user did light a fire for them with a Sparc.
Since then cloning became very popular and the descendants of the C64's business partner rule the scene of his former user.
The descendants did breed many multilingual children of all sizes and shapes and the C64's former user is living happily with them ever after.

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

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionIs it possible to get an image of a gauge.memberClark French1 Apr '13 - 13:54 
I am trying to get an image of a gauge and am running the code as a console app.
If I call the OnPaint function I can get an image but the needle does not display.
 
Any guidance appreciated,
 
Clark
SuggestionRe: Is it possible to get an image of a gauge.memberA.J.Bauer1 Apr '13 - 20:04 
Maybe you can use 'DrawToBitmap' on the gauge control.
I am not sure if this will work for a console app though.
 
A.J.
Don't Panic, debug it!

GeneralRe: Is it possible to get an image of a gauge.memberClark French2 Apr '13 - 1:50 
Thanks that did it.
GeneralMy vote of 5memberjon124817 Feb '13 - 7:11 
This is exactly what I have been looking for.
GeneralExcellent controlmemberTom Guidarelli1 Feb '13 - 6:51 
Just wanted to say thank you! I needed to quickly make an app that displayed magnetic sensor data and your gauge control was very easy to drop in configure.
Questionhow can I add muti-Needle on one control ? [modified]memberxzzxg17 Dec '12 - 18:36 
Hi A.J,
Great job, One question: how can I add multiple needles on one control ?
 
Regards,
xzzxg

modified 18 Dec '12 - 0:52.

QuestionProblem in using various Gaugesmembersaber_ordv918 Oct '12 - 12:13 
Hi
One question: In "A fast and performing gauge" how can I change the gauges display?When I am using it in other program I just have one circular gauge
QuestionAGauge - small bugmembermikerj2 Oct '12 - 23:40 
No 'ValueInRangeChanged' event is raised when the value is in region index 4, even though the region is correctly configured and enabled. The problem is caused by an off-by-one error in line 240 of AGauge.cs:
 
Was:
for (Int32 counter = 0; counter < NUMOFRANGES - 1; counter++)
 
Should be:
for (Int32 counter = 0; counter <= NUMOFRANGES - 1; counter++)
QuestionGauge Control in C++, ATLmemberK ARUN KUMAR28 Sep '12 - 6:29 
Hi,
 
Great work!!!
 
It will be really helpful if you can share the C++, ATL code of this gauge control.
 
Thanks in advance.
 
Regards,
Arun
AnswerRe: Gauge Control in C++, ATLmemberA.J.Bauer1 Oct '12 - 2:16 
Sorry the ATL component (with automatic resizing) is commercial only - not for free.
Don't Panic, debug it!

QuestionImprovementmemberCode Artist27 Aug '12 - 3:23 
Hi A.J,
 
I have some idea to make some improvement to this component.
Do you have any concern if I publish my changes to Code Project and my blog as well?
 
Regards,
Code Artist.
AnswerRe: ImprovementmemberA.J.Bauer27 Aug '12 - 3:35 
Hi Code Artist,
 
go ahead. No concerns whatsoever.
 
Regards
A.J.
Don't Panic, debug it!

GeneralRe: ImprovementmemberCode Artist27 Aug '12 - 3:41 
Thank you.
GeneralMy vote of 5memberChristian Amado24 Aug '12 - 16:43 
5
QuestionHow should I change Gauge type at design time ?memberd.barile24 Aug '12 - 6:04 
HI all
I'm trying the lib but I'm not able to change gauge type...what hell I need to change in properties ?
 
Thanks a lot !
 
PS Sorry for silly question !
QuestionGreat ComponentmemberCode Artist24 Aug '12 - 3:35 
Hi A.J,
 
Great job, I like the way you create this component.
It's light and very flexible.
 
Noticed that AGuage.bmp is not included in the attached source code.
Did you accidentally removed it?
 
Thanks and Regards,
Code Artist
AnswerRe: Great Component [modified]memberA.J.Bauer24 Aug '12 - 4:18 
Seems like nobody noticed before Smile | :)
I put he bitmap in the zip file "AGaugeBmp.zip", it should be up soon.
 
In order to see the bitmap in the toolbox:
- You need to build a standalone control library containing the gauge control.
- Right click on the toolbox/ Choose items / Browse to the built control library..

enjoy
 
A.J.
Don't Panic, debug it!


modified 24 Aug '12 - 10:30.

GeneralRe: Great ComponentmemberCode Artist24 Aug '12 - 6:27 
Thanks.
QuestionGreat Piece of Work!memberMember 30266636 Apr '12 - 6:21 
I am using your control and love it. Commercial alternatives cost $600 on up!
QuestionHow to drag your control to a Form?memberjunli202016 Mar '12 - 1:02 
Hi,
It is really an aweful control.
How to drag your control to a Form?
 
Thank you
SuggestionRe: How to drag your control to a Form?memberA.J.Bauer16 Mar '12 - 1:12 
Well if you meant "awful" I would recommend not using it Big Grin | :-D
Don't Panic, debug it!

QuestionFull Screenmemberchad77722 Sep '11 - 8:32 
Kind of new...
 
What would be the best way to make these guage able to be dock (fill) and expand to full screen ?
GeneralFlip range numbersmemberMember 775283418 May '11 - 21:07 
Hi,
 
Great control! saves me loads of time. I have a question though:
 
I'm using the control to show the position of a ships rudder (the needle being the rudder blade). The value zero is where the needle is when the ship is now steering, like shown below Smile | :)

........./.......O........\
......../........|.........\
......./.........|..........\
....../..........|...........\
...../...........|............\
..../............|.............\
.../.............|..............\
../..............|...............\
./................................\
 
60 45 30 15 0 -15 -30 -45 -60
 
This works very well except the pointer is rotating the wrong way. Full to port (left) should be -60 and not 60. The range should be like this -60 -45 -30 -15 0 15 30 45 60
 
I've tried several settings and did some hacks in the code, but can't figure out how to change the range numbers and get the needle to rotate the other way. Can you point me in the direction?
 
Regards,
 
Rick
GeneralRe: Flip range numbersmemberA.J.Bauer18 May '11 - 21:52 
Hello Rick,
 
a quick hack would be:
1)
set the values for the gauge multiplied by -1.
e.g. yourgauge.value=-yourvalue
This should take care of the needle moving into the "right", well opposite direction.
 
2)
find somewhere near line 1437 in the code of AGauge.cs:
valueText=(m_MinValue+countValue).ToString.......
replace with:
valueText=(-(m_MinValue+countValue)).ToString.......
This should take care of value texts
 
Not tested - but you can try.
 
Regards
A.J.
Don't Panic, debug it!

GeneralRe: Flip range numbersmemberMember 775283418 May '11 - 23:31 
Hi A.J.
 
That seems to work! Thanks very much.
 
Regard, Rick

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 24 Aug 2012
Article Copyright 2007 by A.J.Bauer
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid