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

Aqua Gauge

By , 4 Sep 2007
 
Screenshot - AquaGauge.gif

Introduction

I have chosen to develop this .NET user control to explore the easy yet powerful .NET GDI+. This simple gauge control developed using .NET 2.0 can cater to the entire range of monitoring purposes. Let's see how to develop such a glossy control using GDI+.

Overridden UserControl Methods

Normally, If we create user controls that have been fully drawn by the user, we should override the OnPaint and OnPaintBackground methods. Additionally, the control styles are to be set as appropriate. The following common styles can be set using this.SetStyle(ControlStyles.XXXXX, true/false);.

SupportsTransparentBackColor This will enable your control to support a transparent backcolor if set to true.
ControlStyles.ResizeRedraw Allows repainting when the control is resized.
ControlStyles.AllPaintingInWmPaint If true, the control ignores the window message WM_ERASEBKGND to reduce flicker.
This style should only be applied if the UserPaint bit is set to true.
ControlStyles.UserPaint If true, the control paints itself rather than the operating system doing so.
ControlStyles.OptimizedDoubleBuffer If true, the control is first drawn to a buffer rather than directly to the screen, which can reduce flicker. If you set this property to true, you should also set the AllPaintingInWmPaint to true.

The OnPaint and OnPaintBackground methods will be called whenever the control needs to be repainted. For example, when the control is resized or the form is minimized and maximized, the OnPaint method will be called.

OnPaintBackground vs. OnPaint

OnPaintBackground paints the background (and thereby the shape) of the Window and is guaranteed to be fast. In contrast, OnPaint paints the details and might be slower because individual paint requests are combined into one Paint event that covers all areas that have to be redrawn. You might want to invoke the OnPaintBackground if, for instance, you want to draw a gradient-colored background for your control.

While OnPaintBackground has an event-like nomenclature and takes the same argument as the OnPaint method, OnPaintBackground is not a true event method. There is no PaintBackground event and OnPaintBackground does not invoke event delegates. When overriding the OnPaintBackground method, a derived class is not required to invoke the OnPaintBackground method of its base class.

Drawing the Guage Dial

First, let's see how to draw the dial. The dial requires a Scale, Threshold Indicator, some text and the current value to be displayed.

Drawing the scale requires calculating the positions for the rules that are to be drawn at the circumference. Let's say we need to draw a scale starting from 0 to 10 from angle 90 degrees to 270 degrees on the dial. In this case, the difference in the degrees (270-90 = 180) must be divided into 10 parts. To find the position for each part to be drawn, we need the following formula:

x = centerX + radius * cos(180/partNo)
y = centerY + radius * sin(180/partNo)

Note: when using Math.Cos or Math.Sin we should give angles in radians.

Circle Formula

After finding the position, we can draw any type of scale mark on the circumference. I have chosen to draw a line as a scale mark. Since the dial area is not going to be changed often, it can be drawn in OnPaintBackground overridden method.

Drawing the Pointer

The pointer may need to be repainted often. So, it is better to draw it in the OnPaint method. Finding the pointer position is the same as the logic for drawing the scale. The pointer can be drawn using graphicsObj.FillPolygon() method and it can be transformed to any angle that will represent the current value. Otherwise, the pointer can be redrawn for every change made for the current value.

Drawing the Glossiness

Drawing the glossiness is very simple. All you have to do is, after painting all the dial and pointer, fill two ellipses with gradient coloring. The LinearGradientBrush class provides the ability to draw gradient fills. Masking the gradient layer over the dial gives the glossiness as shown in the below figure.

Glosiness

Using the AquaGauge Control

This AquaGauge control can be used as any other user control provided by Windows. The following are the control-specific properties that can be used to configure this gauge to suit your requirements.

Property Name Type Description
DialColor Color Gets or Sets the background color for the gauge.
DialText String Gets or Sets the Text displayed on the gauge dial.
EnableTransparentBackground bool Enables or Disables Transparent Background color. Note: Enabling this will reduce the performance and may make the control flicker.
Glossiness float Gets or Sets the strength of the Glossiness.
MaxValue float Gets or Sets the maximum value shown on the gauge scale.
MinValue float Gets or Sets the minimum value shown on the gauge scale.
NoOfDivisions int Gets or Sets the number of divisions on the gauge scale.
NoOfSubDivisions int Gets or Sets the number of subdivisions displayed on the scale for each division.
RecommendedValue float Gets or Sets the recommended value on the scale. This will be used as a pivot point for drawing the threshold area.
ThresholdPercent float Gets or Sets the Threshold area percentage on the scale.
Value float Gets or Sets the value to which the pointer will point.

Points of Interest

Whenever we draw images with lots of manipulations, it is recommended to draw it on an image object and then paint. For example, drawing the gauge dial requires lots of CPU-consuming operations. So, we can draw the dial onto an image and then draw using graphicsObj.DrawImage(). Whenever changes are made on the dial properties, we can recreate the image object. It would improve the performance.

History

  • Version 1.0 - Initial Version

All comments and suggestions are welcome.

License

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

About the Author

Ambalavanar Thirugnanam
Architect BNY Mellon
India India
Member
Ambalavanar, working as a .NET Solutions Architect at BNY Mellon (iNautix), Chennai. Enjoys designing and developing UI & highly scalable apps.

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   
GeneralMy vote of 5membercesarms895 Apr '13 - 6:35 
Excellent it's easy to use and very usefull thanks Smile | :)
Questionprogramatically create a gauge?memberleos7915 Feb '13 - 20:22 
Hi!
 
Thank you for your contribution!
 
I'm trying to dinamically crate gauges and put them on a tablelayoutpanel. But the problem I have is, although i get no error, i cant get the graph done.
 
I've tried 2 things; the one you recommend (getting the gague image) and, as that didn't worked, dinamically create a gauge and assigning it to a existent control. I've done this:
 
 
         g = New AquaControls.AquaGauge
         g.MaxValue = 100
         g.MinValue = 0
         g.DialText = Titulo
         g.NoOfDivisions = 10
         g.NoOfSubDivisions = 4
         g.Glossiness = 10
         g.DialColor = Color.Gray
         g.ForeColor = Color.Red
         g.BackColor = System.Drawing.Color.Transparent
         g.Size = New System.Drawing.Size(163, 163)
         g.Value = CSng(m_Actual)
         g.RecommendedValue = CSng((Maximo + Minimo) / 2)
         g.ThresholdPercent = CSng(Maximo - Minimo)
 
         g.Width = Ancho
         g.Height = Alto
         g.Update()
 
I return g, then, if I do this:
 
F.agCargaCPU = CargaCPU.g
 
(being "F" my form and "agCargaCPU" another aquagauge control I have on it), agcargacpu stays blank.
 
Of course, I have another aquagauge control on the form, which i initialize at design time and it shows OK.
 
Any tips?
 
Regards!
QuestionGauge Needle Update, But Value Does NotmemberMember 920084522 Aug '12 - 17:00 
Hi guys
 
I am attempting to use Aqua Gauge to display live values from six (6) data sources over the serial port. Within the one application I have six (6) gauges.
 
As the application reads the data, the gauge needles will change and update showing the correct values on the gauges, however the numerical value to the bottom of the gauges will not update.
 
Any ideas on how to get the numerical values to update as the dial changes?
AnswerRe: Gauge Needle Update, But Value Does NotmemberJoerg_Laukemper29 Aug '12 - 0:34 
Hi,
I had the same problem and found the solution in the post "digit value quick fix": Within OnPaintBackground you must set "requiresRedraw = true;" .
Good luck.
GeneralMy vote of 5membermanoj kumar choubey21 Aug '12 - 18:56 
Nice
QuestionSmoothnessmemberazizib11 Jun '12 - 2:46 
thanks for your very nice implementation
Is there any method that help in smooth movement? for example if my value changed from 50 to 100 , I want to have a smooth movement from 50 to 100 in my gauge.
I know that I can do that by an external method by myself, but I want to know that is there any way already exist in your solution?
 
Regards
Bamshad
AnswerRe: SmoothnessmemberAmbalavanar Thirugnanam11 Jun '12 - 16:58 
Sorry, there is no implementation for smooth movement in the code. Thanks.
-----------------------------
Go far with your knowledge...
Further with google...

GeneralRe: Smoothnessmemberazizib20 Jun '12 - 1:18 
Thanks for your reply
 
I implemented it by myself. but the problem is the gauge digital value does not change any more even using your default method for setting the gauge pointer value.
 
I did not modify part of your code, I just added my method for smoothness in pointer movement.
 
I checked the other comments but "reDraw = true" just helps me in changing the digital value , but it misses the stability instead and make a lot of exception during working. Is there any way to change the digital value by myself?
 
Regards
Bamshad
GeneralRe: Smoothnessmemberarenaxp11 Sep '12 - 22:24 
Could you share source code for smoothness and digital value?
 
thanks in advance.
GeneralRe: Smoothnessmemberazizib12 Sep '12 - 1:13 
I added the "smooth value" property, but the smoothness of changing the value was affecting the gauge label so I deleted it.
But you can easily implement it by just changing the value by adding 1. for example if you want to change the value from 10 -> 30 then add the value by one in a loop for 20 times : 10+1=11 ; 11+1=12 ;...; 29+1 = 30;
just take care about the multi threading , because your GUI will locked (freeze) so you need to use multi threading techniques like Background worker. Easily you can find in internet how to do it.
 
Regards
Bamshad
GeneralRe: Smoothnessmemberarenaxp12 Sep '12 - 18:27 
Thank you.
GeneralVery Good!memberFrancescoC5 Mar '12 - 22:54 
Congrats.....very good object.
QuestionHow to refresh the value?memberalfred_lok11 Feb '12 - 6:25 
I have a timer to refresh the gauge's value.

I call the refresh method but the gauge does not refresh the value, how to do this?

Thanks.
Generalnice!memberjjbubka7 Feb '12 - 22:42 
this is a wonderful one.
Thank you Ambalavanar!
GeneralMy vote of 5memberRedemon7 Dec '11 - 1:45 
That's great!
GeneralAwesome!memberMember 803356210 Aug '11 - 9:17 
You are my hero! hehehe
GeneralMy vote of 5memberversion_2.018 Jun '11 - 0:17 
Superb... My vote of 5
GeneralWonderful controlmemberBeauGauge00120 Mar '11 - 16:28 
Thank you for offering this control. There is also a wonderful one :Circular Gauge ActiveX Control
http://beaugauge.wordpress.com/2011/02/23/circular-gauge-activex-control/[^]
GeneralMy vote of 5memberFrédéric Pailloux25 Jan '11 - 23:41 
Very helpfull article. Thank a lot.
GeneralMy vote of 5 and modifications by memembersettorezero2 Jan '11 - 22:44 
Hi Ambalavanar!! I'm Bernardo Giovanni of www.settorezero.com. Here is the page with my modifications: http://settorezero.blogspot.com/p/my-software.html#AquaGauge[^]
GeneralMy vote of 5membersettorezero2 Jan '11 - 11:46 
Your tool is great
GeneralA little patchmembermythzxp31 Dec '09 - 14:49 
Improved:
1) Adjust the size automaticly to fit the space
2) Keep requiresRedraw=true when OnPaint
3) Make dialText center, support multi-line.
 
The patch is here:
 
---AquaGauge.cs	Sat Sep 01 15:25:48 2007
+++AquaGauge.cs	Fri Jan 01 09:29:36 2010
@@ -30,7 +30,6 @@
         private string dialText;
         private Color dialColor = Color.Lavender;
         private float glossinessAlpha = 25;
-        private int oldWidth, oldHeight;
         int x, y, width, height;
         float fromAngle = 135F;
         float toAngle = 405F;
@@ -38,6 +37,7 @@
         private bool requiresRedraw;
         private Image backgroundImg;
         private Rectangle rectImg;
+        private StringFormat drawFormat;
         #endregion
 
         public AquaGauge()
@@ -57,6 +57,11 @@
             this.BackColor = Color.Transparent;
             this.Resize += new EventHandler(AquaGauge_Resize);
             this.requiresRedraw = true;
+
+            // by heromyth
+            drawFormat = new StringFormat();
+            drawFormat.Alignment = StringAlignment.Center;
+            drawFormat.LineAlignment = StringAlignment.Center;
         }
 
         #region Public Properties
@@ -283,6 +288,7 @@
             width = this.Width - x*2;
             height = this.Height - y*2;
             DrawPointer(e.Graphics, ((width) / 2) + x, ((height) / 2) + y);
+            requiresRedraw = true;
         }
                 
         /// <summary>
@@ -353,8 +359,8 @@
                 DisplayNumber(g, this.currentValue, digiFRect);
 
                 SizeF textSize = g.MeasureString(this.dialText, this.Font);
-                RectangleF digiFRectText = new RectangleF(this.Width / 2 - textSize.Width / 2, (int)(this.height / 1.5), textSize.Width, textSize.Height);
-                g.DrawString(dialText, this.Font, new SolidBrush(this.ForeColor), digiFRectText);
+                RectangleF digiFRectText = new RectangleF(this.Width / 2 - textSize.Width / 2, (int)(this.height / 1.45), textSize.Width, textSize.Height+1);
+                g.DrawString(dialText, this.Font, new SolidBrush(this.ForeColor), digiFRectText, drawFormat);
                 requiresRedraw = false;
             }
             e.Graphics.DrawImage(backgroundImg, rectImg);
@@ -795,20 +801,17 @@
         /// <param name="e"></param>
         private void AquaGauge_Resize(object sender, EventArgs e)
         {
-            if (this.Width < 136)
-            {
-                this.Width = 136;
-            }
-            if (oldWidth != this.Width)
-            {
-                this.Height = this.Width;
-                oldHeight = this.Width;
-            }
-            if (oldHeight != this.Height)
-            {
-                this.Width = this.Height;
-                oldWidth = this.Width;
-            }
+            int newSize;
+
+            if (this.Width > this.Height)
+                newSize = this.Height;
+            else
+                newSize = this.Width;
+
+            if (newSize < 50)
+                newSize = 50;
+      
+            this.Size =  new Size(newSize, newSize);
         }
         #endregion
     }
 

QuestionNeed your helpmemberJeffreychou7 Jun '09 - 23:10 
Can you send me the source code of the AquaGauge.dll?
I want to study it.
Please....
GeneralDigit Value quick fixmemberkelvin199710 May '09 - 2:05 
In this function:
protected override void OnPaintBackground(PaintEventArgs e)
...
...
...
//Draw Digital Value
RectangleF digiRect = new RectangleF((float)this.Width / 2F - (float)this.width / 5F, (float)this.height / 1.2F, (float)this.width / 2.5F, (float)this.Height / 9F);
RectangleF digiFRect = new RectangleF(this.Width / 2 - this.width / 7, (int)(this.height / 1.18), this.width / 4, this.Height / 12);
g.FillRectangle(new SolidBrush(Color.FromArgb(30, Color.Gray)), digiRect);
DisplayNumber(g, this.currentValue, digiFRect);
 
SizeF textSize = g.MeasureString(this.dialText, this.Font);
RectangleF digiFRectText = new RectangleF(this.Width / 2 - textSize.Width / 2, (int)(this.height / 1.5), textSize.Width, textSize.Height);
g.DrawString(dialText, this.Font, new SolidBrush(this.ForeColor), digiFRectText);
requiresRedraw = true; <---------- CHANGE THIS !!!
 
P.S.
Don't get confused about these 2 values:
DialText = "WRX" (The title of my car gauge, you need to set this manually)
DigiFRectText = "100" kmph (speed value coresponding to the meter pointer, automatically displayed after you fixed the code)
 
Rose | [Rose]
GeneralRe: Digit Value quick fixmemberCurros17 May '12 - 7:12 
The problem is that fix consume 10% of my processor =( any help!?
GeneralNot truly transparentmembermmcguirk14 May '09 - 10:44 
Create 2 gauges of size 100, 100
put one at 0,0 the other at 50, 50
 
The corner covers over the control on the bottom.
 
I've tried many ways to fix this problem
 
If you come across a solution, please let me know.
GeneralThank!memberbuidinhba28 Apr '09 - 16:48 
I find some help about buld User Control in C#. Your Project is very good and easy to understand! Thank alot
 
buidinhba5@gmail.com

GeneralUse in ASP.NETmembergirishdpatil25 Feb '09 - 0:16 
This is very good Guage. Can you explain how to use this control in ASP.NET or any SAmple demo can you upadate.
 
Thanks
Girish
QuestionDouble buffering problem?memberc3p00029 Dec '08 - 10:35 
It seems that when I use this control it's not double buffered. I've exchanged it for another meter-control from Codeproject.com and that control was double buffered, so it seems it's related to this particular control.
 
Do I need to change anything to enable double buffering? Has anyone else experienced this?
 
The control looks great otherwise!
 
BR,
Martin
AnswerRe: Double buffering problem?memberAmbalavanar Thirugnanam29 Dec '08 - 18:15 
In the 'EnableTransparentBackground' property implementation of this control, the 'ControlStyles.OptimizedDoubleBuffer' is disabled when the 'EnableTransparentBackground' is set to true. You could change this line of code as required to enable double buffering.
 
You can refer Don't Flicker! Double Buffer! article by Gil.Schmidt for more details.
 
-----------------------------
Go far with your knowledge...
Further with google...

GeneralVery Good ControlmemberMember 313324111 Dec '08 - 10:25 
Thanks a Lot Ambalavanar!!!!!!!!!!!! this is a great control!
QuestionGreat job....minor problem with drawing digital value??membersome1s6910 Dec '08 - 10:04 
Great job with the gauge. Looks very awesome and works great. I have made the couple minor changes as noted in previous posts, but I seem to have an issue with the digital value.
 
The value starts out with the original VALUE as saved in the property, but when I change the VALUE property at run-time, the digital value display doesn't update. Am I missing something?
 
Thanks
GeneralVery NicememberJJMatthews4 Dec '08 - 15:46 
Thank you for the beautiful example.
GeneralThanks!memberColores214 Nov '08 - 18:33 
Thanks for share your work!
The code is clear and compact, not use images and looks good. I´m very impressed!
 
Thanks again.
GeneralDial Textmemberashesman12 Oct '08 - 20:41 
Hi there,
 
Good work on coming up with such an attractive gauge! I have a couple of quick questions...
 
1 - What is the font you used in the example picture above?
2 - I noticed in your example above the font size is proportional to the gauge. In my application the font seems to be a fixed size. I havn't looked too hard into why yet but thought I'd ask first.
 
Cheers
 
Ashley
GeneralRe: Dial TextmemberAmbalavanar Thirugnanam17 Oct '08 - 2:44 
Hi Ashley,
 
Thanks for your comment.
For big ones I used "Trebuchet MS" font. And for small ones I used "Verdana" & "Tahoma". Generally Verdana look good at smaller size than other fonts.
The scale font size is set based on the gauge size.
 
Thanks.
 
-----------------------------
Go far with your knowledge...
Further with google...

QuestionHow can I use this control in other language?memberjeffri1516 Sep '08 - 14:29 
I don't know about C#.
 
But this control is so cool.
 
How can I use this control in Delphi or MFC? Is that possible?
GeneralC++ VersionmemberJimmyO7 Aug '08 - 23:14 
Hello,
 
Very nice control.
Do you have a C++ version of this ?
 
Jimmy
GeneralGood and UsefulmemberAngelo Cresta19 Jun '08 - 2:53 
Hi Ambalavanar,
I want to thank you for this control (got my 5), it's usefull.
I'm working on your control to expose every properties and I'm trying to extend some capabilities. You can check it on line in my blog: http://www.angelonline.net/Portal/Blog/tabid/73/EntryID/23/Default.aspx[^]
GeneralRe: Good and UsefulmemberAmbalavanar Thirugnanam19 Jun '08 - 17:19 
Hi Angelo,
Thanks for that.
I am currently rewriting this control using WPF. Its simple, robust and cool.
Will release soon.
 
-----------------------------
Go far with your knowledge...
Further with google...

GeneralAwesome [modified]memberErnestMoore2 May '08 - 4:57 
Looks as good as a Dundas control.
 
VERY GOOD WORK!
 
Any plans to add an updated dll (with resizig fixed)?
 
thanks!
 
modified on Friday, May 2, 2008 12:07 PM

GeneralRe: AwesomememberAngelo Cresta18 Jun '08 - 5:32 
Hi ErnestMoore,
 
add this to the overridden method:
 
protected override void OnResize(EventArgs e)
{
base.OnResize(e);
requiresRedraw = true;
this.Refresh();
}
 
and that's all ...
 
Hope that helps
 
Angel
GeneralVery Nice Very Professional lookingmemberlokiblack18 Apr '08 - 12:32 
I will be sure to learn alot from your code here. Thank you
GeneralRe: Very Nice Very Professional lookingmemberAmbalavanar Thirugnanam18 Apr '08 - 20:14 
Thanks. Also do look at others comment to fix bugs in this code when you really want to use. Smile | :)
 
-----------------------------
Go far with your knowledge...
Further with google...

QuestionCan we used this control in ASP.Net 2.0?memberNikhil_pune13 Mar '08 - 21:41 
hi Ambalavanar hats off to your work...
 
Can we used this control in ASP.Net 2.0?
 
thanks
 
Nikhil
GeneralLove this controlmemberpduffin29 Nov '07 - 8:45 
This is a great control which is even better when the re-size issue is fixed with the suggestionsSmile | :) on this board.
Did I say I love this control!
GeneralRe: Love this controlmemberAmbalavanar Thirugnanam17 Dec '07 - 2:58 
Thanks for the comment. Smile | :)
-----------------------------
Go far with your knowledge...
Further with google...

Generaldemo project requestmembercncbashing16 Nov '07 - 13:33 
appriciate a demo project showing an example , i am new to net programing and would greatly appriciate a head start , showing a working gauge etc
 
a fantastic control , your work is appriciated
 
dave
GeneralRe: demo project requestmemberBob10005 May '08 - 2:34 
Agree - A working application with source code would make the article near perfect!
Very frustrating not having one........more useful than any explanation!
Generalsmooth movementmemberoneteam12 Nov '07 - 0:59 
is it possible to add a routine which allows a smooth movement of the needle?

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

Permalink | Advertise | Privacy | Mobile
Web04 | 2.6.130523.1 | Last Updated 4 Sep 2007
Article Copyright 2007 by Ambalavanar Thirugnanam
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid