Click here to Skip to main content
15,880,503 members
Articles / Desktop Programming / Windows Forms

A Fast and Performing Gauge

Rate me:
Please Sign up or sign in to vote.
4.95/5 (169 votes)
24 Aug 2012Zlib4 min read 731.4K   36.3K   421   181
This article shows you how to write a performing control using .NET 2.0 and GDI+.

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.

C#
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).

C#
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.

C#
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. For example:

C#
[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.

C#
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.

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

And the Event

C#
[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.

C#
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.

C#
protected override void OnPaintBackground(PaintEventArgs pevent)
{
}

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

C#
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.

C#
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


Written By
Software Developer (Senior)
Germany Germany
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.

Comments and Discussions

 
QuestionThank you. Nice Component. Any Hint how to make the background transparent? Pin
softvision8-Sep-10 0:51
softvision8-Sep-10 0:51 
GeneralThank you Pin
gruber3894-Sep-10 23:11
gruber3894-Sep-10 23:11 
GeneralVB Net version Pin
Juwi_uk9-Aug-10 1:14
Juwi_uk9-Aug-10 1:14 
Hi,

The code below is AJ's original post, where I've then converted to VB NET and added some fixes and enhancements to improve the component in my environment.

To use in Visual Studio, from the solutions Tree select "add\new Item", select "component class" and add it, paste in the code below and compile. It should then appear in your toolbox.

Hopefully may help others using VB and like the great work AJ has done here with this control!. Smile | :)

Regards

Julian

--------------------------------------------------------

Imports System
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Drawing
Imports System.Text
Imports System.Windows.Forms
Imports System.Drawing.Drawing2D
Imports System.Diagnostics

' Copyright (C) 2007 A.J.Bauer
'
' This software is provided as-is, without any express or implied
' warranty. In no event will the authors be held liable for any damages
' arising from the use of this software.

' Permission is granted to anyone to use this software for any purpose,
' including commercial applications, and to alter it and redistribute it
' freely, subject to the following restrictions:

' 1. The origin of this software must not be misrepresented; you must not
' claim that you wrote the original software. if you use this software
' in a product, an acknowledgment in the product documentation would be
' appreciated but is not required.
' 2. Altered source versions must be plainly marked as such, and must not be
' misrepresented as being the original software.
' 3. This notice may not be removed or altered from any source distribution.

' Changes by Julian Wigman, April 2010
'
' 1. Converted original project to VB.Net.
' 2. Ranamed component name to "Gauge".
' 3. Fixed Step error (changed ScaleLinesMajorStep property).
' 4. SubCol & SubCol2 ("+360" fix) for needles as posted on forum.
' 5. Added forum suggested fix to hide unwanted controls from property grid.
' 6. ScaleNumbersStartScale Line property corrections.
' 7. Add support for ScaleNumbersStepScaleLines as per code from AJ on forum.
' 8. Add support for Gauge Borders
' 9. Added extra refresh() where needed

Namespace Gauge
<ToolboxBitmapAttribute(GetType(Gauge), "Gauge.bmp"), DefaultEvent("ValueInRangeChanged"), Description("Displays a value on an analog gauge. Raises an event if the value enters one of the definable ranges.")> _
Partial Public Class Gauge
Inherits Control
#Region "enum, var, delegate, event"
Public Enum NeedleColorEnum
Gray = 0
Red = 1
Green = 2
Blue = 3
Yellow = 4
Violet = 5
Magenta = 6
End Enum

Private Const ZERO As [Byte] = 0
Private Const NUMOFCAPS As [Byte] = 5
Private Const NUMOFRANGES As [Byte] = 5

Private fontBoundY1 As [Single]
Private fontBoundY2 As [Single]
Private gaugeBitmap As Bitmap
Private drawGaugeBackground As [Boolean] = True

Private m_value As [Single]
Private m_valueIsInRange As [Boolean]() = {False, False, False, False, False}
Private m_CapIdx As [Byte] = 1
Private m_CapColor As Color() = {Color.Black, Color.Black, Color.Black, Color.Black, Color.Black}
Private m_CapText As [String]() = {"", "", "", "", ""}
Private m_CapPosition As Point() = {New Point(10, 10), New Point(10, 10), New Point(10, 10), New Point(10, 10), New Point(10, 10)}
Private m_Center As New Point(100, 100)
Private m_MinValue As [Single] = -100
Private m_MaxValue As [Single] = 400

Private m_BaseArcColor As Color = Color.Gray
Private m_BaseArcRadius As Int32 = 80
Private m_BaseArcStart As Int32 = 135
Private m_BaseArcSweep As Int32 = 270
Private m_BaseArcWidth As Int32 = 2

Private m_ScaleLinesInterColor As Color = Color.Black
Private m_ScaleLinesInterInnerRadius As Int32 = 73
Private m_ScaleLinesInterOuterRadius As Int32 = 80
Private m_ScaleLinesInterWidth As Int32 = 1

Private m_ScaleLinesMinorNumOf As Int32 = 9
Private m_ScaleLinesMinorColor As Color = Color.Gray
Private m_ScaleLinesMinorInnerRadius As Int32 = 75
Private m_ScaleLinesMinorOuterRadius As Int32 = 80
Private m_ScaleLinesMinorWidth As Int32 = 1

Private m_ScaleLinesMajorStep As [Single] = 50.0F
Private m_ScaleLinesMajorColor As Color = Color.Black
Private m_ScaleLinesMajorInnerRadius As Int32 = 70
Private m_ScaleLinesMajorOuterRadius As Int32 = 80
Private m_ScaleLinesMajorWidth As Int32 = 2

Private m_RangeIdx As [Byte]
Private m_RangeEnabled As [Boolean]() = {True, True, False, False, False}
Private m_RangeColor As Color() = {Color.LightGreen, Color.Red, Color.FromKnownColor(KnownColor.Control), Color.FromKnownColor(KnownColor.Control), Color.FromKnownColor(KnownColor.Control)}
Private m_RangeStartValue As [Single]() = {-100.0F, 300.0F, 0.0F, 0.0F, 0.0F}
Private m_RangeEndValue As [Single]() = {300.0F, 400.0F, 0.0F, 0.0F, 0.0F}
Private m_RangeInnerRadius As Int32() = {70, 70, 70, 70, 70}
Private m_RangeOuterRadius As Int32() = {80, 80, 80, 80, 80}

Private m_ScaleNumbersRadius As Int32 = 95
Private m_ScaleNumbersColor As Color = Color.Black
Private m_ScaleNumbersFormat As [String]
Private m_ScaleNumbersStartScaleLine As Int32 = 1
Private m_ScaleNumbersStepScaleLines As Int32 = 1
Private m_ScaleNumbersRotation As Int32 = 0

Private m_NeedleType As Int32 = 0
Private m_NeedleRadius As Int32 = 80
Private m_NeedleColor1 As NeedleColorEnum = NeedleColorEnum.Gray
Private m_NeedleColor2 As Color = Color.DimGray
Private m_NeedleWidth As Int32 = 2

Private m_BorderEnabled As Boolean = False
Private m_BorderColor As Color = Color.Black
Private m_BorderWidth As Byte = 1

Public Class ValueInRangeChangedEventArgs
Inherits EventArgs
Public valueInRange As Int32

Public Sub New(ByVal valueInRange As Int32)
Me.valueInRange = valueInRange
End Sub
End Class

Public Delegate Sub ValueInRangeChangedDelegate(ByVal sender As [Object], ByVal e As ValueInRangeChangedEventArgs)
<Description("This event is raised if the value falls into a defined range.")> _
Public Event ValueInRangeChanged As ValueInRangeChangedDelegate
#End Region

#Region "hidden , overridden inherited properties"
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Shadows Property AllowDrop() As [Boolean]
Get
Return False
End Get
Set(ByVal value As [Boolean])

End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Shadows Property AutoSize() As [Boolean]
Get
Return False
End Get
Set(ByVal value As [Boolean])

End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Shadows Property ForeColor() As [Boolean]
Get
Return False
End Get
Set(ByVal value As [Boolean])
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Shadows Property ImeMode() As [Boolean]
Get
Return False
End Get
Set(ByVal value As [Boolean])
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Overloads Overrides Property BackColor() As System.Drawing.Color
Get
Return MyBase.BackColor
End Get
Set(ByVal value As System.Drawing.Color)
MyBase.BackColor = value
drawGaugeBackground = True
Refresh()
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Overloads Overrides Property Font() As System.Drawing.Font
Get
Return MyBase.Font
End Get
Set(ByVal value As System.Drawing.Font)
MyBase.Font = value
drawGaugeBackground = True
Refresh()
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Overloads Overrides Property BackgroundImage() As System.Drawing.Image
Get
Return MyBase.BackgroundImage
End Get
Set(ByVal value As System.Drawing.Image)
MyBase.BackgroundImage = value
drawGaugeBackground = True
Refresh()
End Set
End Property
<Browsable(False), EditorBrowsable(EditorBrowsableState.Never), DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden)> _
Public Overloads Overrides Property BackgroundImageLayout() As System.Windows.Forms.ImageLayout
Get
Return MyBase.BackgroundImageLayout
End Get
Set(ByVal value As System.Windows.Forms.ImageLayout)
MyBase.BackgroundImageLayout = value
drawGaugeBackground = True
Refresh()
End Set
End Property
#End Region

Private components As System.ComponentModel.IContainer = Nothing
Private Sub InitializeComponent()
Me.SuspendLayout()
Me.ResumeLayout(False)

End Sub
Public Sub New()
InitializeComponent()

SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
End Sub

#Region "properties"
<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The value.")> _
Public Property Value() As [Single]
Get
Return m_value
End Get
Set(ByVal value As [Single])
If m_value <> value Then
m_value = Math.Min(Math.Max(value, m_MinValue), m_MaxValue)

If Me.DesignMode Then
drawGaugeBackground = True
End If

For counter As Int32 = 0 To NUMOFRANGES - 2
If (m_RangeStartValue(counter) <= m_value) AndAlso (m_value <= m_RangeEndValue(counter)) AndAlso (m_RangeEnabled(counter)) Then
If Not m_valueIsInRange(counter) Then
RaiseEvent ValueInRangeChanged(Me, New ValueInRangeChangedEventArgs(counter))
End If
Else
m_valueIsInRange(counter) = False
End If
Next
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.RefreshProperties(RefreshProperties.All), System.ComponentModel.Description("The caption index. set this to a value of 0 up to 4 to change the corresponding caption's properties.")> _
Public Property Cap_Idx() As [Byte]
Get
Return m_CapIdx
End Get
Set(ByVal value As [Byte])
If (m_CapIdx <> value) AndAlso (0 <= value) AndAlso (value < 5) Then
m_CapIdx = value
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The color of the caption text.")> _
Private Property CapColor() As Color
Get
Return m_CapColor(m_CapIdx)
End Get
Set(ByVal value As Color)
If m_CapColor(m_CapIdx) <> value Then
m_CapColor(m_CapIdx) = value
CapColors = m_CapColor
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(False)> _
Public Property CapColors() As Color()
Get
Return m_CapColor
End Get
Set(ByVal value As Color())
m_CapColor = value
Refresh() ' JKW
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The text of the caption.")> _
Public Property CapText() As [String]
Get
Return m_CapText(m_CapIdx)
End Get
Set(ByVal value As [String])
If m_CapText(m_CapIdx) <> value Then
m_CapText(m_CapIdx) = value
CapsText = m_CapText
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(False)> _
Public Property CapsText() As [String]()
Get
Return m_CapText
End Get
Set(ByVal value As [String]())
For counter As Int32 = 0 To 4
m_CapText(counter) = value(counter)
Next
Refresh() ' JKW
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The position of the caption.")> _
Public Property CapPosition() As Point
Get
Return m_CapPosition(m_CapIdx)
End Get
Set(ByVal value As Point)
If m_CapPosition(m_CapIdx) <> value Then
m_CapPosition(m_CapIdx) = value
CapsPosition = m_CapPosition
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(False)> _
Public Property CapsPosition() As Point()
Get
Return m_CapPosition
End Get
Set(ByVal value As Point())
m_CapPosition = value
Refresh() ' JKW
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The center of the gauge (in the control's client area).")> _
Public Property Center() As Point
Get
Return m_Center
End Get
Set(ByVal value As Point)
If m_Center <> value Then
m_Center = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The minimum value to show on the scale.")> _
Public Property MinValue() As [Single]
Get
Return m_MinValue
End Get
Set(ByVal value As [Single])
If (m_MinValue <> value) AndAlso (value < m_MaxValue) Then
m_MinValue = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The maximum value to show on the scale.")> _
Public Property MaxValue() As [Single]
Get
Return m_MaxValue
End Get
Set(ByVal value As [Single])
If (m_MaxValue <> value) AndAlso (value > m_MinValue) Then
m_MaxValue = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The color of the base arc.")> _
Public Property BaseArcColor() As Color
Get
Return m_BaseArcColor
End Get
Set(ByVal value As Color)
If m_BaseArcColor <> value Then
m_BaseArcColor = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The radius of the base arc.")> _
Public Property BaseArcRadius() As Int32
Get
Return m_BaseArcRadius
End Get
Set(ByVal value As Int32)
If m_BaseArcRadius <> value Then
m_BaseArcRadius = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The start angle of the base arc.")> _
Public Property BaseArcStart() As Int32
Get
Return m_BaseArcStart
End Get
Set(ByVal value As Int32)
If m_BaseArcStart <> value Then
m_BaseArcStart = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The sweep angle of the base arc.")> _
Public Property BaseArcSweep() As Int32
Get
Return m_BaseArcSweep
End Get
Set(ByVal value As Int32)
If m_BaseArcSweep <> value Then
m_BaseArcSweep = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The width of the base arc.")> _
Public Property BaseArcWidth() As Int32
Get
Return m_BaseArcWidth
End Get
Set(ByVal value As Int32)
If m_BaseArcWidth <> value Then
m_BaseArcWidth = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The color of the inter scale lines which are the middle scale lines for an uneven number of minor scale lines.")> _
Public Property ScaleLinesInterColor() As Color
Get
Return m_ScaleLinesInterColor
End Get
Set(ByVal value As Color)
If m_ScaleLinesInterColor <> value Then
m_ScaleLinesInterColor = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The inner radius of the inter scale lines which are the middle scale lines for an uneven number of minor scale lines.")> _
Public Property ScaleLinesInterInnerRadius() As Int32
Get
Return m_ScaleLinesInterInnerRadius
End Get
Set(ByVal value As Int32)
If m_ScaleLinesInterInnerRadius <> value Then
m_ScaleLinesInterInnerRadius = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The outer radius of the inter scale lines which are the middle scale lines for an uneven number of minor scale lines.")> _
Public Property ScaleLinesInterOuterRadius() As Int32
Get
Return m_ScaleLinesInterOuterRadius
End Get
Set(ByVal value As Int32)
If m_ScaleLinesInterOuterRadius <> value Then
m_ScaleLinesInterOuterRadius = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The width of the inter scale lines which are the middle scale lines for an uneven number of minor scale lines.")> _
Public Property ScaleLinesInterWidth() As Int32
Get
Return m_ScaleLinesInterWidth
End Get
Set(ByVal value As Int32)
If m_ScaleLinesInterWidth <> value Then
m_ScaleLinesInterWidth = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The number of minor scale lines.")> _
Public Property ScaleLinesMinorNumOf() As Int32
Get
Return m_ScaleLinesMinorNumOf
End Get
Set(ByVal value As Int32)
If m_ScaleLinesMinorNumOf <> value Then
m_ScaleLinesMinorNumOf = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The color of the minor scale lines.")> _
Public Property ScaleLinesMinorColor() As Color
Get
Return m_ScaleLinesMinorColor
End Get
Set(ByVal value As Color)
If m_ScaleLinesMinorColor <> value Then
m_ScaleLinesMinorColor = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The inner radius of the minor scale lines.")> _
Public Property ScaleLinesMinorInnerRadius() As Int32
Get
Return m_ScaleLinesMinorInnerRadius
End Get
Set(ByVal value As Int32)
If m_ScaleLinesMinorInnerRadius <> value Then
m_ScaleLinesMinorInnerRadius = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The outer radius of the minor scale lines.")> _
Public Property ScaleLinesMinorOuterRadius() As Int32
Get
Return m_ScaleLinesMinorOuterRadius
End Get
Set(ByVal value As Int32)
If m_ScaleLinesMinorOuterRadius <> value Then
m_ScaleLinesMinorOuterRadius = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The width of the minor scale lines.")> _
Public Property ScaleLinesMinorWidth() As Int32
Get
Return m_ScaleLinesMinorWidth
End Get
Set(ByVal value As Int32)
If m_ScaleLinesMinorWidth <> value Then
m_ScaleLinesMinorWidth = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The step value of the major scale lines.")> _
Public Property ScaleLinesMajorStep() As [Single]
Get
Return m_ScaleLinesMajorStep
End Get
Set(ByVal value As [Single])
If (m_ScaleLinesMajorStep <> value) AndAlso (value > 0) Then
' JKW This line below caused problems!
' m_ScaleLinesMajorStep = Math.Max(Math.Min(value, m_MaxValue), m_MinValue)
m_ScaleLinesMajorStep = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The color of the major scale lines.")> _
Public Property ScaleLinesMajorColor() As Color
Get
Return m_ScaleLinesMajorColor
End Get
Set(ByVal value As Color)
If m_ScaleLinesMajorColor <> value Then
m_ScaleLinesMajorColor = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The inner radius of the major scale lines.")> _
Public Property ScaleLinesMajorInnerRadius() As Int32
Get
Return m_ScaleLinesMajorInnerRadius
End Get
Set(ByVal value As Int32)
If m_ScaleLinesMajorInnerRadius <> value Then
m_ScaleLinesMajorInnerRadius = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The outer radius of the major scale lines.")> _
Public Property ScaleLinesMajorOuterRadius() As Int32
Get
Return m_ScaleLinesMajorOuterRadius
End Get
Set(ByVal value As Int32)
If m_ScaleLinesMajorOuterRadius <> value Then
m_ScaleLinesMajorOuterRadius = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The width of the major scale lines.")> _
Public Property ScaleLinesMajorWidth() As Int32
Get
Return m_ScaleLinesMajorWidth
End Get
Set(ByVal value As Int32)
If m_ScaleLinesMajorWidth <> value Then
m_ScaleLinesMajorWidth = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.RefreshProperties(RefreshProperties.All), System.ComponentModel.Description("The range index. set this to a value of 0 up to 4 to change the corresponding range's properties.")> _
Public Property Range_Idx() As [Byte]
Get
Return m_RangeIdx
End Get
Set(ByVal value As [Byte])
If (m_RangeIdx <> value) AndAlso (0 <= value) AndAlso (value < NUMOFRANGES) Then
m_RangeIdx = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("Enables or disables the range selected by Range_Idx.")> _
Public Property RangeEnabled() As [Boolean]
Get
Return m_RangeEnabled(m_RangeIdx)
End Get
Set(ByVal value As [Boolean])
If m_RangeEnabled(m_RangeIdx) <> value Then
m_RangeEnabled(m_RangeIdx) = value
RangesEnabled = m_RangeEnabled
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(False)> _
Public Property RangesEnabled() As [Boolean]()
Get
Return m_RangeEnabled
End Get
Set(ByVal value As [Boolean]())
m_RangeEnabled = value
Refresh() ' JKW
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The color of the range.")> _
Public Property RangeColor() As Color
Get
Return m_RangeColor(m_RangeIdx)
End Get
Set(ByVal value As Color)
If m_RangeColor(m_RangeIdx) <> value Then
m_RangeColor(m_RangeIdx) = value
RangesColor = m_RangeColor
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(False)> _
Public Property RangesColor() As Color()
Get
Return m_RangeColor
End Get
Set(ByVal value As Color())
m_RangeColor = value
Refresh() ' JKW
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The start value of the range, must be less than RangeEndValue.")> _
Public Property RangeStartValue() As [Single]
Get
Return m_RangeStartValue(m_RangeIdx)
End Get
Set(ByVal value As [Single])
If (m_RangeStartValue(m_RangeIdx) <> value) AndAlso (value < m_RangeEndValue(m_RangeIdx)) Then
m_RangeStartValue(m_RangeIdx) = value
RangesStartValue = m_RangeStartValue
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(False)> _
Public Property RangesStartValue() As [Single]()
Get
Return m_RangeStartValue
End Get
Set(ByVal value As [Single]())
m_RangeStartValue = value
Refresh() ' JKW
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The end value of the range. Must be greater than RangeStartValue.")> _
Public Property RangeEndValue() As [Single]
Get
Return m_RangeEndValue(m_RangeIdx)
End Get
Set(ByVal value As [Single])
If (m_RangeEndValue(m_RangeIdx) <> value) AndAlso (m_RangeStartValue(m_RangeIdx) < value) Then
m_RangeEndValue(m_RangeIdx) = value
RangesEndValue = m_RangeEndValue
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(False)> _
Public Property RangesEndValue() As [Single]()
Get
Return m_RangeEndValue
End Get
Set(ByVal value As [Single]())
m_RangeEndValue = value
Refresh() ' JKW
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The inner radius of the range.")> _
Public Property RangeInnerRadius() As Int32
Get
Return m_RangeInnerRadius(m_RangeIdx)
End Get
Set(ByVal value As Int32)
If m_RangeInnerRadius(m_RangeIdx) <> value Then
m_RangeInnerRadius(m_RangeIdx) = value
RangesInnerRadius = m_RangeInnerRadius
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(False)> _
Public Property RangesInnerRadius() As Int32()
Get
Return m_RangeInnerRadius
End Get
Set(ByVal value As Int32())
m_RangeInnerRadius = value
Refresh() ' JKW
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The inner radius of the range.")> _
Public Property RangeOuterRadius() As Int32
Get
Return m_RangeOuterRadius(m_RangeIdx)
End Get
Set(ByVal value As Int32)
If m_RangeOuterRadius(m_RangeIdx) <> value Then
m_RangeOuterRadius(m_RangeIdx) = value
RangesOuterRadius = m_RangeOuterRadius
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(False)> _
Public Property RangesOuterRadius() As Int32()
Get
Return m_RangeOuterRadius
End Get
Set(ByVal value As Int32())
m_RangeOuterRadius = value
Refresh() ' JKW
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The radius of the scale numbers.")> _
Public Property ScaleNumbersRadius() As Int32
Get
Return m_ScaleNumbersRadius
End Get
Set(ByVal value As Int32)
If m_ScaleNumbersRadius <> value Then
m_ScaleNumbersRadius = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The color of the scale numbers.")> _
Public Property ScaleNumbersColor() As Color
Get
Return m_ScaleNumbersColor
End Get
Set(ByVal value As Color)
If m_ScaleNumbersColor <> value Then
m_ScaleNumbersColor = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The format of the scale numbers.")> _
Public Property ScaleNumbersFormat() As [String]
Get
Return m_ScaleNumbersFormat
End Get
Set(ByVal value As [String])
If m_ScaleNumbersFormat <> value Then
m_ScaleNumbersFormat = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The number of the scale line to start writing numbers next to.")> _
Public Property ScaleNumbersStartScaleLine() As Int32
Get
Return m_ScaleNumbersStartScaleLine
End Get
Set(ByVal value As Int32)
If m_ScaleNumbersStartScaleLine <> value Then
m_ScaleNumbersStartScaleLine = Math.Max(value, 1)
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The number of scale line steps for writing numbers.")> _
Public Property ScaleNumbersStepScaleLines() As Int32
Get
Return m_ScaleNumbersStepScaleLines
End Get
Set(ByVal value As Int32)
If m_ScaleNumbersStepScaleLines <> value Then
m_ScaleNumbersStepScaleLines = Math.Max(value, 1)
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The angle relative to the tangent of the base arc at a scale line that is used to rotate numbers. set to 0 for no rotation or e.g. set to 90.")> _
Public Property ScaleNumbersRotation() As Int32
Get
Return m_ScaleNumbersRotation
End Get
Set(ByVal value As Int32)
If m_ScaleNumbersRotation <> value Then
m_ScaleNumbersRotation = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The type of the needle, currently only type 0 and 1 are supported. Type 0 looks nicer but if you experience performance problems you might consider using type 1.")> _
Public Property NeedleType() As Int32
Get
Return m_NeedleType
End Get
Set(ByVal value As Int32)
If m_NeedleType <> value Then
m_NeedleType = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The radius of the needle.")> _
Public Property NeedleRadius() As Int32
Get
Return m_NeedleRadius
End Get
Set(ByVal value As Int32)
If m_NeedleRadius <> value Then
m_NeedleRadius = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The first color of the needle.")> _
Public Property NeedleColor1() As NeedleColorEnum
Get
Return m_NeedleColor1
End Get
Set(ByVal value As NeedleColorEnum)
If m_NeedleColor1 <> value Then
m_NeedleColor1 = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The second color of the needle.")> _
Public Property NeedleColor2() As Color
Get
Return m_NeedleColor2
End Get
Set(ByVal value As Color)
If m_NeedleColor2 <> value Then
m_NeedleColor2 = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The width of the needle.")> _
Public Property NeedleWidth() As Int32
Get
Return m_NeedleWidth
End Get
Set(ByVal value As Int32)
If m_NeedleWidth <> value Then
m_NeedleWidth = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("Enable/Disable the border around the gauge.")> _
Public Property BorderEnabled() As [Boolean]
Get
Return m_BorderEnabled
End Get
Set(ByVal value As [Boolean])
m_BorderEnabled = value
drawGaugeBackground = True
Refresh()
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The color of the gauge border.")> _
Public Property BorderColor() As Color
Get
Return m_BorderColor
End Get
Set(ByVal value As Color)
If m_BorderColor <> value Then
m_BorderColor = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property

<System.ComponentModel.Browsable(True), System.ComponentModel.Category("Gauge"), System.ComponentModel.Description("The color of the gauge border.")> _
Public Property BorderWidth() As Byte
Get
Return m_BorderWidth
End Get
Set(ByVal value As Byte)
If m_BorderWidth <> value AndAlso value <= 10 Then
m_BorderWidth = value
drawGaugeBackground = True
Refresh()
End If
End Set
End Property
#End Region

#Region "helper"
Private Sub FindFontBounds()
'find upper and lower bounds for numeric characters
Dim c1 As Int32
Dim c2 As Int32
Dim boundfound As [Boolean]
Dim b As Bitmap
Dim g As Graphics
Dim backBrush As New SolidBrush(Color.White)
Dim foreBrush As New SolidBrush(Color.Black)
Dim boundingBox As SizeF

b = New Bitmap(5, 5)
g = Graphics.FromImage(b)
boundingBox = g.MeasureString("0123456789", Font, -1, StringFormat.GenericTypographic)
b = New Bitmap(CType((boundingBox.Width), Int32), CType((boundingBox.Height), Int32))
g = Graphics.FromImage(b)
g.FillRectangle(backBrush, 0.0F, 0.0F, boundingBox.Width, boundingBox.Height)
g.DrawString("0123456789", Font, foreBrush, 0.0F, 0.0F, StringFormat.GenericTypographic)

fontBoundY1 = 0
fontBoundY2 = 0
c1 = 0
boundfound = False
While (c1 < b.Height) AndAlso (Not boundfound)
c2 = 0
While (c2 < b.Width) AndAlso (Not boundfound)
If b.GetPixel(c2, c1) <> backBrush.Color Then
fontBoundY1 = c1
boundfound = True
End If
c2 += 1
End While
c1 += 1
End While

c1 = b.Height - 1
boundfound = False
While (0 < c1) AndAlso (Not boundfound)
c2 = 0
While (c2 < b.Width) AndAlso (Not boundfound)
If b.GetPixel(c2, c1) <> backBrush.Color Then
fontBoundY2 = c1
boundfound = True
End If
c2 += 1
End While
c1 -= 1
End While
End Sub

#End Region

#Region "base member overrides"
Protected Overloads Overrides Sub OnPaintBackground(ByVal pevent As PaintEventArgs)
End Sub

Protected Overloads Overrides Sub OnPaint(ByVal pe As PaintEventArgs)

If (Width < 10) OrElse (Height < 10) Then
Exit Sub
End If

If drawGaugeBackground Then
drawGaugeBackground = False

FindFontBounds()

gaugeBitmap = New Bitmap(Width, Height, pe.Graphics)
Dim ggr As Graphics = Graphics.FromImage(gaugeBitmap)
ggr.FillRectangle(New SolidBrush(BackColor), ClientRectangle)

If BackgroundImage IsNot Nothing Then
Select Case BackgroundImageLayout
Case ImageLayout.Center
ggr.DrawImageUnscaled(BackgroundImage, Width / 2 - BackgroundImage.Width / 2, Height / 2 - BackgroundImage.Height / 2)
Exit Select
Case ImageLayout.None
ggr.DrawImageUnscaled(BackgroundImage, 0, 0)
Exit Select
Case ImageLayout.Stretch
ggr.DrawImage(BackgroundImage, 0, 0, Width, Height)
Exit Select
Case ImageLayout.Tile
Dim pixelOffsetX As Int32 = 0
Dim pixelOffsetY As Int32 = 0
While pixelOffsetX < Width
pixelOffsetY = 0
While pixelOffsetY < Height
ggr.DrawImageUnscaled(BackgroundImage, pixelOffsetX, pixelOffsetY)
pixelOffsetY += BackgroundImage.Height
End While
pixelOffsetX += BackgroundImage.Width
End While
Exit Select
Case ImageLayout.Zoom
If CType((BackgroundImage.Width / Width), [Single]) < CType((BackgroundImage.Height / Height), [Single]) Then
ggr.DrawImage(BackgroundImage, 0, 0, Height, Height)
Else
ggr.DrawImage(BackgroundImage, 0, 0, Width, Width)
End If
Exit Select
End Select
End If

' JKW Add border (enabled, color and width) to Gauge
If m_BorderEnabled Then
Dim BorderRectangle As Rectangle = ClientRectangle
BorderRectangle.Inflate(-Math.Ceiling(BorderWidth / 2), -Math.Ceiling(BorderWidth / 2))
ggr.DrawRectangle(New Pen(BorderColor, BorderWidth), BorderRectangle)
End If

ggr.SmoothingMode = SmoothingMode.HighQuality
ggr.PixelOffsetMode = PixelOffsetMode.HighQuality

Dim gp As New GraphicsPath()
Dim rangeStartAngle As [Single]
Dim rangeSweepAngle As [Single]
For counter As Int32 = 0 To NUMOFRANGES - 1
If m_RangeEndValue(counter) > m_RangeStartValue(counter) AndAlso m_RangeEnabled(counter) Then
rangeStartAngle = m_BaseArcStart + (m_RangeStartValue(counter) - m_MinValue) * m_BaseArcSweep / (m_MaxValue - m_MinValue)
rangeSweepAngle = (m_RangeEndValue(counter) - m_RangeStartValue(counter)) * m_BaseArcSweep / (m_MaxValue - m_MinValue)

gp.Reset()
gp.AddPie(New Rectangle(m_Center.X - m_RangeOuterRadius(counter), m_Center.Y - m_RangeOuterRadius(counter), _
2 * m_RangeOuterRadius(counter), 2 * m_RangeOuterRadius(counter)), rangeStartAngle, rangeSweepAngle)
gp.Reverse()
gp.AddPie(New Rectangle(m_Center.X - m_RangeInnerRadius(counter), m_Center.Y - m_RangeInnerRadius(counter), _
2 * m_RangeInnerRadius(counter), 2 * m_RangeInnerRadius(counter)), rangeStartAngle, rangeSweepAngle)
gp.Reverse()
ggr.SetClip(gp)
ggr.FillPie(New SolidBrush(m_RangeColor(counter)), New Rectangle(m_Center.X - m_RangeOuterRadius(counter), m_Center.Y - m_RangeOuterRadius(counter), _
2 * m_RangeOuterRadius(counter), 2 * m_RangeOuterRadius(counter)), rangeStartAngle, rangeSweepAngle)
End If
Next

ggr.SetClip(ClientRectangle)
If m_BaseArcRadius > 0 Then
ggr.DrawArc(New Pen(m_BaseArcColor, m_BaseArcWidth), New Rectangle(m_Center.X - m_BaseArcRadius, m_Center.Y - m_BaseArcRadius, 2 * m_BaseArcRadius, _
2 * m_BaseArcRadius), m_BaseArcStart, m_BaseArcSweep)
End If

Dim valueText As [String] = ""
Dim boundingBox As SizeF
Dim countValue As [Single] = 0
Dim counter1 As Int32 = 0
While countValue <= (m_MaxValue - m_MinValue)
valueText = (m_MinValue + countValue).ToString(m_ScaleNumbersFormat)
ggr.ResetTransform()
boundingBox = ggr.MeasureString(valueText, Font, -1, StringFormat.GenericTypographic)

gp.Reset()
gp.AddEllipse(New Rectangle(m_Center.X - m_ScaleLinesMajorOuterRadius, m_Center.Y - m_ScaleLinesMajorOuterRadius, 2 * m_ScaleLinesMajorOuterRadius, _
2 * m_ScaleLinesMajorOuterRadius))
gp.Reverse()
gp.AddEllipse(New Rectangle(m_Center.X - m_ScaleLinesMajorInnerRadius, m_Center.Y - m_ScaleLinesMajorInnerRadius, 2 * m_ScaleLinesMajorInnerRadius, _
2 * m_ScaleLinesMajorInnerRadius))
gp.Reverse()
ggr.SetClip(gp)

ggr.DrawLine(New Pen(m_ScaleLinesMajorColor, m_ScaleLinesMajorWidth), CType((Center.X), [Single]), CType((Center.Y), [Single]), _
CType((Center.X + 2 * m_ScaleLinesMajorOuterRadius * Math.Cos((m_BaseArcStart + countValue * m_BaseArcSweep / (m_MaxValue - m_MinValue)) * Math.PI / 180.0R)), [Single]), _
CType((Center.Y + 2 * m_ScaleLinesMajorOuterRadius * Math.Sin((m_BaseArcStart + countValue * m_BaseArcSweep / (m_MaxValue - m_MinValue)) * Math.PI / 180.0R)), [Single]))

gp.Reset()
gp.AddEllipse(New Rectangle(m_Center.X - m_ScaleLinesMinorOuterRadius, m_Center.Y - m_ScaleLinesMinorOuterRadius, 2 * m_ScaleLinesMinorOuterRadius, 2 * m_ScaleLinesMinorOuterRadius))
gp.Reverse()
gp.AddEllipse(New Rectangle(m_Center.X - m_ScaleLinesMinorInnerRadius, m_Center.Y - m_ScaleLinesMinorInnerRadius, 2 * m_ScaleLinesMinorInnerRadius, 2 * m_ScaleLinesMinorInnerRadius))
gp.Reverse()
ggr.SetClip(gp)

If countValue < (m_MaxValue - m_MinValue) Then
For counter2 As Int32 = 1 To m_ScaleLinesMinorNumOf
If ((m_ScaleLinesMinorNumOf Mod 2) = 1) AndAlso (CType((m_ScaleLinesMinorNumOf / 2), Int32) + 1 = counter2) Then
gp.Reset()
gp.AddEllipse(New Rectangle(m_Center.X - m_ScaleLinesInterOuterRadius, m_Center.Y - m_ScaleLinesInterOuterRadius, _
2 * m_ScaleLinesInterOuterRadius, 2 * m_ScaleLinesInterOuterRadius))
gp.Reverse()
gp.AddEllipse(New Rectangle(m_Center.X - m_ScaleLinesInterInnerRadius, m_Center.Y - m_ScaleLinesInterInnerRadius, _
2 * m_ScaleLinesInterInnerRadius, 2 * m_ScaleLinesInterInnerRadius))
gp.Reverse()
ggr.SetClip(gp)

ggr.DrawLine(New Pen(m_ScaleLinesInterColor, m_ScaleLinesInterWidth), CType((Center.X), [Single]), CType((Center.Y), [Single]), _
CType((Center.X + 2 * m_ScaleLinesInterOuterRadius * Math.Cos((m_BaseArcStart + countValue * m_BaseArcSweep / (m_MaxValue - m_MinValue) + counter2 * m_BaseArcSweep / (CType(((m_MaxValue - m_MinValue) / m_ScaleLinesMajorStep), [Single]) * (m_ScaleLinesMinorNumOf + 1))) * Math.PI / 180.0R)), [Single]), _
CType((Center.Y + 2 * m_ScaleLinesInterOuterRadius * Math.Sin((m_BaseArcStart + countValue * m_BaseArcSweep / (m_MaxValue - m_MinValue) + counter2 * m_BaseArcSweep / (CType(((m_MaxValue - m_MinValue) / m_ScaleLinesMajorStep), [Single]) * (m_ScaleLinesMinorNumOf + 1))) * Math.PI / 180.0R)), [Single]))

gp.Reset()
gp.AddEllipse(New Rectangle(m_Center.X - m_ScaleLinesMinorOuterRadius, m_Center.Y - m_ScaleLinesMinorOuterRadius, 2 * m_ScaleLinesMinorOuterRadius, _
2 * m_ScaleLinesMinorOuterRadius))
gp.Reverse()
gp.AddEllipse(New Rectangle(m_Center.X - m_ScaleLinesMinorInnerRadius, m_Center.Y - m_ScaleLinesMinorInnerRadius, 2 * m_ScaleLinesMinorInnerRadius, _
2 * m_ScaleLinesMinorInnerRadius))
gp.Reverse()
ggr.SetClip(gp)
Else
ggr.DrawLine(New Pen(m_ScaleLinesMinorColor, m_ScaleLinesMinorWidth), CType((Center.X), [Single]), CType((Center.Y), [Single]), _
CType((Center.X + 2 * m_ScaleLinesMinorOuterRadius * Math.Cos((m_BaseArcStart + countValue * m_BaseArcSweep / (m_MaxValue - m_MinValue) + counter2 * m_BaseArcSweep / (CType(((m_MaxValue - m_MinValue) / m_ScaleLinesMajorStep), [Single]) * (m_ScaleLinesMinorNumOf + 1))) * Math.PI / 180.0R)), [Single]), _
CType((Center.Y + 2 * m_ScaleLinesMinorOuterRadius * Math.Sin((m_BaseArcStart + countValue * m_BaseArcSweep / (m_MaxValue - m_MinValue) + counter2 * m_BaseArcSweep / (CType(((m_MaxValue - m_MinValue) / m_ScaleLinesMajorStep), [Single]) * (m_ScaleLinesMinorNumOf + 1))) * Math.PI / 180.0R)), [Single]))
End If
Next
End If

ggr.SetClip(ClientRectangle)

If m_ScaleNumbersRotation <> 0 Then
ggr.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias
ggr.RotateTransform(90.0F + m_BaseArcStart + countValue * m_BaseArcSweep / (m_MaxValue - m_MinValue))
End If

ggr.TranslateTransform(CType((Center.X + m_ScaleNumbersRadius * Math.Cos((m_BaseArcStart + countValue * m_BaseArcSweep / (m_MaxValue - m_MinValue)) * Math.PI / 180.0F)), [Single]), _
CType((Center.Y + m_ScaleNumbersRadius * Math.Sin((m_BaseArcStart + countValue * m_BaseArcSweep / (m_MaxValue - m_MinValue)) * Math.PI / 180.0F)), [Single]), _
System.Drawing.Drawing2D.MatrixOrder.Append)

If (counter1 >= ScaleNumbersStartScaleLine - 1) AndAlso ((counter1 - ScaleNumbersStartScaleLine + 1) Mod ScaleNumbersStepScaleLines = 0) Then
ggr.DrawString(valueText, Font, New SolidBrush(m_ScaleNumbersColor), -boundingBox.Width / 2, -fontBoundY1 - (fontBoundY2 - fontBoundY1 + 1) / 2, StringFormat.GenericTypographic)
End If

countValue += m_ScaleLinesMajorStep
counter1 += 1
End While

ggr.ResetTransform()
ggr.SetClip(ClientRectangle)

If m_ScaleNumbersRotation <> 0 Then
ggr.TextRenderingHint = System.Drawing.Text.TextRenderingHint.SystemDefault
End If

For counter As Int32 = 0 To NUMOFCAPS - 1
If m_CapText(counter) <> "" Then
ggr.DrawString(m_CapText(counter), Font, New SolidBrush(m_CapColor(counter)), m_CapPosition(counter).X, m_CapPosition(counter).Y, StringFormat.GenericTypographic)
End If
Next

End If

pe.Graphics.DrawImageUnscaled(gaugeBitmap, 0, 0)
pe.Graphics.SmoothingMode = SmoothingMode.AntiAlias
pe.Graphics.PixelOffsetMode = PixelOffsetMode.HighQuality

Dim brushAngle As [Single] = CType((m_BaseArcStart + (m_value - m_MinValue) * m_BaseArcSweep / (m_MaxValue - m_MinValue)), Int32) Mod 360
Dim needleAngle As [Double] = brushAngle * Math.PI / 180

Select Case m_NeedleType
Case 0
Dim points As PointF() = New PointF(2) {}
Dim brush1 As Brush = Brushes.White
Dim brush2 As Brush = Brushes.White
Dim brush3 As Brush = Brushes.White
Dim brush4 As Brush = Brushes.White

Dim brushBucket As Brush = Brushes.White
Dim subcol As Int32 = CType((((brushAngle + 360 + 225) Mod 180) * 100 / 180), Int32) ' JKW Modified "+360" added
Dim subcol2 As Int32 = CType((((brushAngle + 360 + 135) Mod 180) * 100 / 180), Int32) ' JKW Modified "+360" added

pe.Graphics.FillEllipse(New SolidBrush(m_NeedleColor2), Center.X - m_NeedleWidth * 3, Center.Y - m_NeedleWidth * 3, m_NeedleWidth * 6, m_NeedleWidth * 6)
Select Case m_NeedleColor1
Case NeedleColorEnum.Gray
brush1 = New SolidBrush(Color.FromArgb(80 + subcol, 80 + subcol, 80 + subcol))
brush2 = New SolidBrush(Color.FromArgb(180 - subcol, 180 - subcol, 180 - subcol))
brush3 = New SolidBrush(Color.FromArgb(80 + subcol2, 80 + subcol2, 80 + subcol2))
brush4 = New SolidBrush(Color.FromArgb(180 - subcol2, 180 - subcol2, 180 - subcol2))
pe.Graphics.DrawEllipse(Pens.Gray, Center.X - m_NeedleWidth * 3, Center.Y - m_NeedleWidth * 3, m_NeedleWidth * 6, m_NeedleWidth * 6)
Exit Select
Case NeedleColorEnum.Red
brush1 = New SolidBrush(Color.FromArgb(145 + subcol, subcol, subcol))
brush2 = New SolidBrush(Color.FromArgb(245 - subcol, 100 - subcol, 100 - subcol))
brush3 = New SolidBrush(Color.FromArgb(145 + subcol2, subcol2, subcol2))
brush4 = New SolidBrush(Color.FromArgb(245 - subcol2, 100 - subcol2, 100 - subcol2))
pe.Graphics.DrawEllipse(Pens.Red, Center.X - m_NeedleWidth * 3, Center.Y - m_NeedleWidth * 3, m_NeedleWidth * 6, m_NeedleWidth * 6)
Exit Select
Case NeedleColorEnum.Green
brush1 = New SolidBrush(Color.FromArgb(subcol, 145 + subcol, subcol))
brush2 = New SolidBrush(Color.FromArgb(100 - subcol, 245 - subcol, 100 - subcol))
brush3 = New SolidBrush(Color.FromArgb(subcol2, 145 + subcol2, subcol2))
brush4 = New SolidBrush(Color.FromArgb(100 - subcol2, 245 - subcol2, 100 - subcol2))
pe.Graphics.DrawEllipse(Pens.Green, Center.X - m_NeedleWidth * 3, Center.Y - m_NeedleWidth * 3, m_NeedleWidth * 6, m_NeedleWidth * 6)
Exit Select
Case NeedleColorEnum.Blue
brush1 = New SolidBrush(Color.FromArgb(subcol, subcol, 145 + subcol))
brush2 = New SolidBrush(Color.FromArgb(100 - subcol, 100 - subcol, 245 - subcol))
brush3 = New SolidBrush(Color.FromArgb(subcol2, subcol2, 145 + subcol2))
brush4 = New SolidBrush(Color.FromArgb(100 - subcol2, 100 - subcol2, 245 - subcol2))
pe.Graphics.DrawEllipse(Pens.Blue, Center.X - m_NeedleWidth * 3, Center.Y - m_NeedleWidth * 3, m_NeedleWidth * 6, m_NeedleWidth * 6)
Exit Select
Case NeedleColorEnum.Magenta
brush1 = New SolidBrush(Color.FromArgb(subcol, 145 + subcol, 145 + subcol))
brush2 = New SolidBrush(Color.FromArgb(100 - subcol, 245 - subcol, 245 - subcol))
brush3 = New SolidBrush(Color.FromArgb(subcol2, 145 + subcol2, 145 + subcol2))
brush4 = New SolidBrush(Color.FromArgb(100 - subcol2, 245 - subcol2, 245 - subcol2))
pe.Graphics.DrawEllipse(Pens.Magenta, Center.X - m_NeedleWidth * 3, Center.Y - m_NeedleWidth * 3, m_NeedleWidth * 6, m_NeedleWidth * 6)
Exit Select
Case NeedleColorEnum.Violet
brush1 = New SolidBrush(Color.FromArgb(145 + subcol, subcol, 145 + subcol))
brush2 = New SolidBrush(Color.FromArgb(245 - subcol, 100 - subcol, 245 - subcol))
brush3 = New SolidBrush(Color.FromArgb(145 + subcol2, subcol2, 145 + subcol2))
brush4 = New SolidBrush(Color.FromArgb(245 - subcol2, 100 - subcol2, 245 - subcol2))
pe.Graphics.DrawEllipse(Pens.Violet, Center.X - m_NeedleWidth * 3, Center.Y - m_NeedleWidth * 3, m_NeedleWidth * 6, m_NeedleWidth * 6)
Exit Select
Case NeedleColorEnum.Yellow
brush1 = New SolidBrush(Color.FromArgb(145 + subcol, 145 + subcol, subcol))
brush2 = New SolidBrush(Color.FromArgb(245 - subcol, 245 - subcol, 100 - subcol))
brush3 = New SolidBrush(Color.FromArgb(145 + subcol2, 145 + subcol2, subcol2))
brush4 = New SolidBrush(Color.FromArgb(245 - subcol2, 245 - subcol2, 100 - subcol2))
pe.Graphics.DrawEllipse(Pens.Violet, Center.X - m_NeedleWidth * 3, Center.Y - m_NeedleWidth * 3, m_NeedleWidth * 6, m_NeedleWidth * 6)
Exit Select
End Select

If Math.Floor(CType((((brushAngle + 225) Mod 360) / 180.0R), [Single])) = 0 Then
brushBucket = brush1
brush1 = brush2
brush2 = brushBucket
End If

If Math.Floor(CType((((brushAngle + 135) Mod 360) / 180.0R), [Single])) = 0 Then
brush4 = brush3
End If

points(0).X = CType((Center.X + m_NeedleRadius * Math.Cos(needleAngle)), [Single])
points(0).Y = CType((Center.Y + m_NeedleRadius * Math.Sin(needleAngle)), [Single])
points(1).X = CType((Center.X - m_NeedleRadius / 20 * Math.Cos(needleAngle)), [Single])
points(1).Y = CType((Center.Y - m_NeedleRadius / 20 * Math.Sin(needleAngle)), [Single])
points(2).X = CType((Center.X - m_NeedleRadius / 5 * Math.Cos(needleAngle) + m_NeedleWidth * 2 * Math.Cos(needleAngle + Math.PI / 2)), [Single])
points(2).Y = CType((Center.Y - m_NeedleRadius / 5 * Math.Sin(needleAngle) + m_NeedleWidth * 2 * Math.Sin(needleAngle + Math.PI / 2)), [Single])
pe.Graphics.FillPolygon(brush1, points)

points(2).X = CType((Center.X - m_NeedleRadius / 5 * Math.Cos(needleAngle) + m_NeedleWidth * 2 * Math.Cos(needleAngle - Math.PI / 2)), [Single])
points(2).Y = CType((Center.Y - m_NeedleRadius / 5 * Math.Sin(needleAngle) + m_NeedleWidth * 2 * Math.Sin(needleAngle - Math.PI / 2)), [Single])
pe.Graphics.FillPolygon(brush2, points)

points(0).X = CType((Center.X - (m_NeedleRadius / 20 - 1) * Math.Cos(needleAngle)), [Single])
points(0).Y = CType((Center.Y - (m_NeedleRadius / 20 - 1) * Math.Sin(needleAngle)), [Single])
points(1).X = CType((Center.X - m_NeedleRadius / 5 * Math.Cos(needleAngle) + m_NeedleWidth * 2 * Math.Cos(needleAngle + Math.PI / 2)), [Single])
points(1).Y = CType((Center.Y - m_NeedleRadius / 5 * Math.Sin(needleAngle) + m_NeedleWidth * 2 * Math.Sin(needleAngle + Math.PI / 2)), [Single])
points(2).X = CType((Center.X - m_NeedleRadius / 5 * Math.Cos(needleAngle) + m_NeedleWidth * 2 * Math.Cos(needleAngle - Math.PI / 2)), [Single])
points(2).Y = CType((Center.Y - m_NeedleRadius / 5 * Math.Sin(needleAngle) + m_NeedleWidth * 2 * Math.Sin(needleAngle - Math.PI / 2)), [Single])
pe.Graphics.FillPolygon(brush4, points)

points(0).X = CType((Center.X - m_NeedleRadius / 20 * Math.Cos(needleAngle)), [Single])
points(0).Y = CType((Center.Y - m_NeedleRadius / 20 * Math.Sin(needleAngle)), [Single])
points(1).X = CType((Center.X + m_NeedleRadius * Math.Cos(needleAngle)), [Single])
points(1).Y = CType((Center.Y + m_NeedleRadius * Math.Sin(needleAngle)), [Single])

pe.Graphics.DrawLine(New Pen(m_NeedleColor2), Center.X, Center.Y, points(0).X, points(0).Y)
pe.Graphics.DrawLine(New Pen(m_NeedleColor2), Center.X, Center.Y, points(1).X, points(1).Y)
Exit Select
Case 1
Dim startPoint As New Point(CType((Center.X - m_NeedleRadius / 8 * Math.Cos(needleAngle)), Int32), CType((Center.Y - m_NeedleRadius / 8 * Math.Sin(needleAngle)), Int32))
Dim endPoint As New Point(CType((Center.X + m_NeedleRadius * Math.Cos(needleAngle)), Int32), CType((Center.Y + m_NeedleRadius * Math.Sin(needleAngle)), Int32))

pe.Graphics.FillEllipse(New SolidBrush(m_NeedleColor2), Center.X - m_NeedleWidth * 3, Center.Y - m_NeedleWidth * 3, m_NeedleWidth * 6, m_NeedleWidth * 6)

Select Case m_NeedleColor1
Case NeedleColorEnum.Gray
pe.Graphics.DrawLine(New Pen(Color.DarkGray, m_NeedleWidth), Center.X, Center.Y, endPoint.X, endPoint.Y)
pe.Graphics.DrawLine(New Pen(Color.DarkGray, m_NeedleWidth), Center.X, Center.Y, startPoint.X, startPoint.Y)
Exit Select
Case NeedleColorEnum.Red
pe.Graphics.DrawLine(New Pen(Color.Red, m_NeedleWidth), Center.X, Center.Y, endPoint.X, endPoint.Y)
pe.Graphics.DrawLine(New Pen(Color.Red, m_NeedleWidth), Center.X, Center.Y, startPoint.X, startPoint.Y)
Exit Select
Case NeedleColorEnum.Green
pe.Graphics.DrawLine(New Pen(Color.Green, m_NeedleWidth), Center.X, Center.Y, endPoint.X, endPoint.Y)
pe.Graphics.DrawLine(New Pen(Color.Green, m_NeedleWidth), Center.X, Center.Y, startPoint.X, startPoint.Y)
Exit Select
Case NeedleColorEnum.Blue
pe.Graphics.DrawLine(New Pen(Color.Blue, m_NeedleWidth), Center.X, Center.Y, endPoint.X, endPoint.Y)
pe.Graphics.DrawLine(New Pen(Color.Blue, m_NeedleWidth), Center.X, Center.Y, startPoint.X, startPoint.Y)
Exit Select
Case NeedleColorEnum.Magenta
pe.Graphics.DrawLine(New Pen(Color.Magenta, m_NeedleWidth), Center.X, Center.Y, endPoint.X, endPoint.Y)
pe.Graphics.DrawLine(New Pen(Color.Magenta, m_NeedleWidth), Center.X, Center.Y, startPoint.X, startPoint.Y)
Exit Select
Case NeedleColorEnum.Violet
pe.Graphics.DrawLine(New Pen(Color.Violet, m_NeedleWidth), Center.X, Center.Y, endPoint.X, endPoint.Y)
pe.Graphics.DrawLine(New Pen(Color.Violet, m_NeedleWidth), Center.X, Center.Y, startPoint.X, startPoint.Y)
Exit Select
Case NeedleColorEnum.Yellow
pe.Graphics.DrawLine(New Pen(Color.Yellow, m_NeedleWidth), Center.X, Center.Y, endPoint.X, endPoint.Y)
pe.Graphics.DrawLine(New Pen(Color.Yellow, m_NeedleWidth), Center.X, Center.Y, startPoint.X, startPoint.Y)
Exit Select
End Select
Exit Select
End Select
End Sub

Protected Overloads Overrides Sub OnResize(ByVal e As EventArgs)
drawGaugeBackground = True
Refresh()
End Sub
#End Region

End Class
End Namespace
GeneralRe: VB Net version Pin
robjay30-Sep-10 14:04
robjay30-Sep-10 14:04 
QuestionHow would I use the DLL in VB .NET to add a dial on my form? Pin
ZiaUrRehman12-Jul-10 19:48
ZiaUrRehman12-Jul-10 19:48 
QuestionThese properties dont seem to work? Pin
Juwi_uk23-Apr-10 11:27
Juwi_uk23-Apr-10 11:27 
AnswerRe: These properties dont seem to work? Pin
A.J.Bauer25-Apr-10 21:05
A.J.Bauer25-Apr-10 21:05 
GeneralRe: These properties dont seem to work? Pin
Juwi_uk25-Apr-10 21:35
Juwi_uk25-Apr-10 21:35 
GeneralRe: These properties dont seem to work? Pin
A.J.Bauer25-Apr-10 21:59
A.J.Bauer25-Apr-10 21:59 
GeneralRe: These properties dont seem to work? Pin
Juwi_uk26-Apr-10 9:51
Juwi_uk26-Apr-10 9:51 
GeneralRe: These properties dont seem to work? Pin
Member 84690637-Dec-11 9:09
Member 84690637-Dec-11 9:09 
GeneralRe: These properties dont seem to work? Pin
A.J.Bauer8-Dec-11 5:00
A.J.Bauer8-Dec-11 5:00 
GeneralStrange pixel artifacts when drawing ranges Pin
Juwi_uk20-Apr-10 11:39
Juwi_uk20-Apr-10 11:39 
GeneralRe: Strange pixel artifacts when drawing ranges Pin
A.J.Bauer20-Apr-10 21:12
A.J.Bauer20-Apr-10 21:12 
GeneralRe: Strange pixel artifacts when drawing ranges Pin
Juwi_uk21-Apr-10 6:32
Juwi_uk21-Apr-10 6:32 
GeneralRe: Strange pixel artifacts when drawing ranges Pin
A.J.Bauer22-Apr-10 21:05
A.J.Bauer22-Apr-10 21:05 
GeneralRe: Strange pixel artifacts when drawing ranges Pin
Juwi_uk23-Apr-10 9:10
Juwi_uk23-Apr-10 9:10 
GeneralChanging Ranges Pin
Juwi_uk7-Apr-10 11:52
Juwi_uk7-Apr-10 11:52 
GeneralRe: Changing Ranges Pin
A.J.Bauer7-Apr-10 21:19
A.J.Bauer7-Apr-10 21:19 
GeneralRe: Changing Ranges Pin
Juwi_uk8-Apr-10 6:21
Juwi_uk8-Apr-10 6:21 
GeneralRe: Changing Ranges [modified] Pin
A.J.Bauer9-Apr-10 2:58
A.J.Bauer9-Apr-10 2:58 
QuestionHow to set own needle colors? Pin
inflames2k2-Feb-10 3:17
inflames2k2-Feb-10 3:17 
AnswerRe: How to set own needle colors? Pin
A.J.Bauer7-Apr-10 21:28
A.J.Bauer7-Apr-10 21:28 
QuestionHow do I add your controls to my application? [modified] Pin
danicoelho711-Nov-09 23:47
danicoelho711-Nov-09 23:47 
AnswerRe: How do I add your controls to my application? Pin
A.J.Bauer12-Nov-09 10:08
A.J.Bauer12-Nov-09 10:08 

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

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