Click here to Skip to main content
11,435,451 members (62,730 online)
Click here to Skip to main content

Circular gauge custom control for Silverlight 3 and WPF

, 22 Jul 2009 BSD
Rate this:
Please Sign up or sign in to vote.
An article on creating a circular gauge custom control for Silverlight 3.

gauge_cb.png

Introduction

The Silverlight 3 SDK and toolkit includes more than a hundred controls but is missing a circular gauge control, so I decided to create one. My goal was to create a customizable gauge that is easy to use and also looks professional. You can see a live demo here.

Using the code

In this section, I will explain how you can use the gauge control in your application. The easiest way to get started would be to copy this template code snippet in your project after referencing CircularGauge.dll and the CircularGauge namespace.

<!--Black Gauge -->
<gauge:CircularGaugeControl x:Name="myGauge1" 
  Grid.Column="0" Grid.Row="0" 
  Radius="150" 
  ScaleRadius="110" 
  ScaleStartAngle="120" 
  ScaleSweepAngle="300"
  PointerLength="85" 
  PointerCapRadius="35" 
  MinValue="0" 
  MaxValue="1000" 
  MajorDivisionsCount="10" 
  MinorDivisionsCount="5" 
  CurrentValue="{Binding Score}"
  ImageSource="silverlightlogo.png"
  ImageSize="60,50"
  RangeIndicatorThickness="8"
  RangeIndicatorRadius="120"
  RangeIndicatorLightRadius="10"
  RangeIndicatorLightOffset="80"
  ScaleLabelRadius="90"
  ScaleLabelSize="40,20"
  ScaleLabelFontSize="10"
  ScaleLabelForeground="LightGray"
  MajorTickSize="10,3"
  MinorTickSize="3,1"
  MajorTickColor="LightGray"
  MinorTickColor="LightGray"
  ImageOffset="-50"
  GaugeBackgroundColor="Black"
  PointerThickness ="16"
  OptimalRangeStartValue="300"
  OptimalRangeEndValue="700" 
  DialTextOffset="40" 
  DialText="Black"
  DialTextColor="Black"> 
</gauge:CircularGaugeControl>

The CircularGauge control has several options to completely customize all its elements. Most of the property settings are intuitive to use, so I will only discuss a few here.

Properties Comments
Background Setting the background color would automatically create a gradient and glass effect using the set color as the base. This makes it very easy to drastically change the look.
ScaleRadius, ScaleLabelRadius, RangeIndicatorRadius These properties provide options to place the scale, range indicator, and scale labels anywhere within the gauge.
CurrentValue The CurrentValue property is a dependency property so you can data bind to it.
ImageOffset, DialTextOffset, RangeIndicatorLightOffset These properties control the placement of the image, dial text, and the range indicator light along the Y-axis with respect to the center of the gauge.
OptimalRangeStartValue, OptimalRangeEndValue These properties help define the recommended range of values. The range indicators are drawn based on these values. In addition to the range indicators, the RangeIndicatorLight also provides visual feedback using the same information. The colors representing these value zones can be customized using the BelowOptimalRangeColor, AboveOptimalRangeColor, and OptimalRangeColor values.

Creating a custom template

This is a look-less control, so all the details of appearance is described in the default style specified in generic.xaml. In case you would like to create a new control template, the gauge control expects the following elements in its control template:

Element Name Element Type
LayoutRoot Grid
Pointer Path
RangeIndicatorLight Ellipse
PointerCap Ellipse

You can find the complete markup for the default style in the generic.xaml file included in the source code.

Points of interest

This section outlines the main steps in creating this control. Most parts of the visual representation of the gauge control were drawn using Expression Blend. The container that contains all the elements of the gauge is a Grid. The gauge/rim, pointer cap, pointer, range indicator light, and the glass effect which is another ellipse with reduced opacity, were all drawn using Expression Blend 3. I drew the shapes and then template bound the height, width, and other attributes. Here is the markup for the pointer:

 <!-- Pointer -->
<Path x:Name="Pointer" Stroke="#FFE91C1C" StrokeThickness="2" 
  Width="{TemplateBinding PointerLength}" 
  Height="{TemplateBinding PointerThickness}" HorizontalAlignment="Center"
  Data="M1,1 L1,10 L156,6 z" Stretch="Fill" RenderTransformOrigin="0,0.5" 
  RenderTransform="{Binding RelativeSource={RelativeSource TemplatedParent}, 
  Path=PointerLength, Converter={StaticResource pointerCenterConverter}}">
<Path.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF890A0A" Offset="0.197"/>
<GradientStop Color="#FFC40808" Offset="1"/>
<GradientStop Color="#FFE32323" Offset="0.61"/>
</LinearGradientBrush>
</Path.Fill>

</Path>

All the items in the template that you would like to access in code would need to be named appropriately. This is the way to establish binding between the user interface and the control. We then override the OnApplyTemplate method to get access to the named elements in the UI.

Drawing the scale and range indicator

The elements which cannot be defined in the generic.xaml file are the scale and the range indicator which need to be drawn in code. The Major and Minor tick marks are just rectangles that have been rotated and moved using render transforms. Similar to the tick marks, the scale labels are text blocks that have been rotated and moved. In order to draw the scale, we need to compute the location of the points on the circumference of the scale circle. This can be computed using this formula:

gauge_formula4.png

Formula to find a point on the circumference, given an angle and a radius:

x = a + r * cos(θ)
y = b + r * sin(θ)

where:

  • r is the radius of the circle
  • (a,b) is the center of the circle
  • (x,y) is the point on the circumference
  • θ is the angle in degrees
  • radian = degree * π/180

Similar to drawing the scale, we use the same formula to compute the four points that comprise the range segment.

gauge_formula3.png

Animating the pointer

Finally, animating the pointer only involves creating a double animation and setting its target to the pointer's rotate transform angle.

void AnimatePointer(double oldcurrentvalueAngle, double newcurrentvalueAngle)
{
    if (pointer != null)
    {
        DoubleAnimation da = new DoubleAnimation();
        da.From = oldcurrentvalueAngle;
        da.To = newcurrentvalueAngle;

        double animDuration = Math.Abs(oldcurrentvalueAngle - newcurrentvalueAngle) * 
                                       animatingSpeedFactor;
        da.Duration = new Duration(TimeSpan.FromMilliseconds(animDuration));

        Storyboard sb = new Storyboard();
        sb.Completed += new EventHandler(sb_Completed);
        sb.Children.Add(da);
        Storyboard.SetTarget(da, pointer);
        Storyboard.SetTargetProperty(da, new PropertyPath(
          "(Path.RenderTransform).(TransformGroup.Children)[0].(RotateTransform.Angle)"));

        if (newcurrentvalueAngle != oldcurrentvalueAngle)
        {
            sb.Begin();
        }
    }
}

WPF version

Porting to WPF was very easy, all I had to do was create a WPF custom control project and move the code from the Silverlight version, and everything worked without any changes.

Conclusion

This code is licensed under the Free BSD license, so feel free to use in commercial applications. I hope this gauge is useful, and would really appreciate any comments/feedback.

History

  • Version 1.0 - 07/22/2009.
  • WPF version updated - 07/28/2009.

License

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

Share

About the Author

EvelynT
Web Developer
United States United States
I have worked mainly with ASP.NET but also have exposure to Windows forms, WPF and Silverlight. I also enjoy developing user interfaces using Expression Blend.

Comments and Discussions

 
QuestionScaling down size of gauge Pin
Member 1141705131-Jan-15 9:02
memberMember 1141705131-Jan-15 9:02 
AnswerRe: Scaling down size of gauge Pin
Julien Barbier6-Feb-15 4:08
memberJulien Barbier6-Feb-15 4:08 
QuestionDesign Rendering Error Pin
cohay29-Jan-15 21:30
membercohay29-Jan-15 21:30 
SuggestionVery good - Show start value Pin
FriedhelmEichin7-Jul-14 2:24
memberFriedhelmEichin7-Jul-14 2:24 
QuestionDo not see the CircularGauge.dll in either the WPF or Silverlight zip files Pin
Member 1048922610-Apr-14 12:56
memberMember 1048922610-Apr-14 12:56 
QuestionRemoving the outer ring Pin
Member 940689827-Jan-14 5:04
memberMember 940689827-Jan-14 5:04 
QuestionChanging "optimal" ranges Pin
cw229021-Jan-14 0:50
membercw229021-Jan-14 0:50 
QuestionThank you Pin
Petr Ivankov7-Jan-14 1:12
memberPetr Ivankov7-Jan-14 1:12 
Questionvery good article! Pin
nettian22-Dec-13 13:40
membernettian22-Dec-13 13:40 
QuestionPre-compiling show in XAML page Pin
Антон Протасов16-Sep-13 3:01
memberАнтон Протасов16-Sep-13 3:01 
QuestionThank you Pin
hossein bakhtiari27-Mar-13 11:01
memberhossein bakhtiari27-Mar-13 11:01 
Questionvery nice Pin
CIDev26-Mar-13 11:59
memberCIDev26-Mar-13 11:59 
Questionhi thanks for your sharing but i have a question Pin
mistergamer26-Nov-12 3:57
membermistergamer26-Nov-12 3:57 
QuestionSetting StartValue for the Pointer Pin
Bullimann15-Jul-12 21:27
memberBullimann15-Jul-12 21:27 
Questionbinding with Min/Max ? Pin
pourheidar22-Jun-12 5:02
memberpourheidar22-Jun-12 5:02 
AnswerRe: binding with Min/Max ? Pin
Encodedberk22-Jul-14 4:57
memberEncodedberk22-Jul-14 4:57 
QuestionRefreshing the scale at runtime when changing max and min values. Pin
mesta2-Mar-12 9:36
membermesta2-Mar-12 9:36 
BugBUGFIX: Incorrect Optimal Range drawing when (MaxValue - MinValue) < 1.0 Pin
Member 815194614-Aug-11 6:47
memberMember 815194614-Aug-11 6:47 
QuestionPlease help me !! Everything appears well but the pointer doesent work (stays at 0). Pin
radesi3-Aug-11 4:55
memberradesi3-Aug-11 4:55 
QuestionCan use it while defined in XAML but i am unable to create and get to work this object programmatically in WPF VB, can you post an example of creation of the gauge enterely in VB.NEt ? Pin
radesi1-Aug-11 10:14
memberradesi1-Aug-11 10:14 
GeneralRe: Can use it while defined in XAML but i am unable to create and get to work this object programmatically in WPF VB, can you post an example of creation of the gauge enterely in VB.NEt ? [modified] Pin
TooPercent225-Jun-12 8:47
memberTooPercent225-Jun-12 8:47 
QuestionExcellent , thanks for sharing Pin
radesi1-Aug-11 5:47
memberradesi1-Aug-11 5:47 
GeneralSilverlight 4 Pin
CodeJerry3-Jun-11 3:01
memberCodeJerry3-Jun-11 3:01 
GeneralRange Pin
AshishCodeProject_Singhal31-May-11 4:34
memberAshishCodeProject_Singhal31-May-11 4:34 
QuestionSpliting the Scale Pin
Member 775974222-Mar-11 17:22
memberMember 775974222-Mar-11 17:22 
GeneralChanging MaxValue, OptimalRangeStartValue and OptimalRangeEndValue on Runtime Pin
Superbase11-Mar-11 5:16
memberSuperbase11-Mar-11 5:16 
Generalproblem with RangeIndicator Pin
wulouvre4-Mar-11 15:41
memberwulouvre4-Mar-11 15:41 
Generalvery nice Pin
Badea Florin1-Feb-11 10:24
memberBadea Florin1-Feb-11 10:24 
Generalvery good, thanks! I added a more property to control... Pin
EnricoOliva23-Jan-11 23:49
memberEnricoOliva23-Jan-11 23:49 
QuestionResizing while running application Pin
Robert Veringa6-Jan-11 3:43
memberRobert Veringa6-Jan-11 3:43 
GeneralThanks a lot! Pin
Robert Veringa29-Nov-10 2:12
memberRobert Veringa29-Nov-10 2:12 
QuestionBinding the MaxValue Pin
padawandamien13-Oct-10 4:11
memberpadawandamien13-Oct-10 4:11 
GeneralThanks! Pin
mktschudi15-Aug-10 11:28
membermktschudi15-Aug-10 11:28 
GeneralStandalone usage Pin
JamieMeyer0110-Aug-10 12:51
memberJamieMeyer0110-Aug-10 12:51 
GeneralRe: Standalone usage Pin
Rob@Love2Code1-Feb-11 23:46
memberRob@Love2Code1-Feb-11 23:46 
GeneralTo use the project in windows 8 Pin
dheeraj pk27-Aug-12 3:00
memberdheeraj pk27-Aug-12 3:00 
GeneralCannot find CircularGauge.dll Pin
Cataliz3er1-Jun-10 12:29
memberCataliz3er1-Jun-10 12:29 
QuestionHow do I make this dial as hyperlink Pin
dtvravi20-May-10 4:47
memberdtvravi20-May-10 4:47 
GeneralThanks and ... Pin
Jerry Evans11-Feb-10 16:06
memberJerry Evans11-Feb-10 16:06 
Generalbinding to CurrentValue Pin
maarten77231-Jan-10 23:51
membermaarten77231-Jan-10 23:51 
GeneralSizing Gauge Pin
canard_16-Jan-10 3:00
membercanard_16-Jan-10 3:00 
GeneralRe: Sizing Gauge Pin
kintz22-Jan-10 3:03
memberkintz22-Jan-10 3:03 
GeneralRe: Sizing Gauge Pin
iamveritas2-Jul-11 3:31
memberiamveritas2-Jul-11 3:31 
Generalproblem with RangeIndicator Pin
_dros12-Jan-10 5:19
member_dros12-Jan-10 5:19 
GeneralRe: problem with RangeIndicator Pin
ozkar garcia25-Jan-10 12:42
memberozkar garcia25-Jan-10 12:42 
GeneralRe: problem with RangeIndicator Pin
_dros25-Jan-10 23:15
member_dros25-Jan-10 23:15 
GeneralRe: problem with RangeIndicator Pin
ozkar garcia26-Jan-10 8:27
memberozkar garcia26-Jan-10 8:27 
QuestionRe: problem with RangeIndicator Pin
_dros27-Jan-10 2:02
member_dros27-Jan-10 2:02 
AnswerRe: problem with RangeIndicator Pin
GaborTorok10-Feb-10 0:02
memberGaborTorok10-Feb-10 0:02 
QuestionHow can we support binding with Min/Max and OptimalRange values? Pin
akjoshi6-Jan-10 0:17
memberakjoshi6-Jan-10 0:17 

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

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web03 | 2.8.150428.2 | Last Updated 22 Jul 2009
Article Copyright 2009 by EvelynT
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid