Google
WWW Yariv Hammer's Code Site

Monday, January 09, 2006

DegreesUpDown Control

Introduction
The code I will post here is for a custom Control called DegreesUpDown. The control is similar to a NumericUpDown only it shows degrees (from 0 to 359). The nice thing about it is that the degrees are shown in 3 digits (000, 050, 180, etc...).

The Code
You might have thought that the control inherits from NumericUpDown. Well, it does not. It inherits from DomainUpDown which is much more flexible. I cannot format the way numbers look in NumericUpDown, so the trick is to convert strings into numeric values.

------------------------------------------------
Imports System.ComponentModel
Imports CommonControls.CGraphicsCommon
Imports System.Drawing

Public Class DegreesUpDown
   Inherits System.Windows.Forms.DomainUpDown

#Region " Windows Form Designer generated code "
Public Sub New()
   MyBase.New()
'This call is required by the Windows Form Designer.
   InitializeComponent()
   InitializeGraphics()
End Sub

'UserControl overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
   If disposing Then
      If Not (components Is Nothing) Then
         components.Dispose()
      End If
   End If
   MyBase.Dispose(disposing)
End Sub

'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
   '
   'DegreesUpDown
   '
   Me.Wrap = True
End Sub
#End Region

Private Sub InitializeGraphics()
   Me.Font = CommonFont
   Me.BackColor = Color.FromArgb(238, 238, 238)
   Me.BorderStyle = BorderStyle.FixedSingle
End Sub

Private Sub InitValues()
   Me.Items.Clear()
   Dim vals As ArrayList = New ArrayList(360)
   For i As Integer = 359 To 0 Step -1
      vals.Add(ConvertTo3Digits(i))
   Next
   Me.Items.AddRange(vals)
End Sub

Public Shared Function ConvertTo3Digits(ByVal i As Integer) As String
   If (i >= 100) Then Return i.ToString()
   If (i >= 10) Then Return "0" + i.ToString()
   If (i >= 0) Then Return "00" + i.ToString()
   Return "000"
End Function

<Browsable(False)> _
Public Shadows ReadOnly Property Items() As DomainUpDownItemCollection
   Get
      Return MyBase.Items
   End Get
End Property

<Browsable(False), _
Bindable(True)> _
Public Property Value() As Long
   Get
      If (IsNothing(Me.SelectedItem)) Then Return 0
      Return Long.Parse(Me.SelectedItem)
   End Get
   Set(ByVal Value As Long)
      If IsNothing(Value) Then
         Me.SelectedItem = "000"
         RaiseEvent ValueChanged(Me, EventArgs.Empty)
         Exit Property
      End If
      If (Value >= 360 Or Value < 0) Then
         Me.Text = Value.ToString
         FixHeading()
      Else
         Me.SelectedItem = ConvertTo3Digits(Value)
      End If
      RaiseEvent ValueChanged(Me, EventArgs.Empty)
   End Set
End Property

<Category("Action"), _
Description("Raised when the value is changed")> _
Public Event ValueChanged As EventHandler

Private Sub DegreesUpDown_SelectedItemChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.SelectedItemChanged
   RaiseEvent ValueChanged(Me, EventArgs.Empty)
End Sub

Protected Overrides Sub OnCreateControl()
   Me.Items.Clear()
   If Not DesignMode Then
      Dim vals As ArrayList = New ArrayList(360)
      For i As Integer = 359 To 0 Step -1
         vals.Add(ConvertTo3Digits(i))
      Next
      Me.Items.AddRange(vals)
      Me.SelectedIndex = 359
   Else
      Me.Text = "000"
   End If
End Sub

Private Sub DegreesUpDown_Leave(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Leave
   FixHeading()
End Sub

Private Sub FixHeading()
   Try
      Dim i As Integer = Integer.Parse(Me.Text)
      If i < 0 Then
         Me.SelectedIndex = ((-i) Mod 360) - 1
      End If
      If i > 359 Then
         Me.SelectedIndex = 359 - (i Mod 360)
      End If
      If i < 359 And i > 0 Then _
         Me.SelectedIndex = 359 - i
   Catch ex As Exception
      Me.SelectedIndex = 359
   End Try
End Sub

Protected Overrides Sub UpdateEditText()
Try
   Dim i As Integer = Integer.Parse(Me.Text)
   If (i > 359 Or i < 0) Then
      FixHeading()
   Else
      If Me.Text.Length <> 3 Then _
         FixHeading()
   End If
   Catch ex As Exception
      FixHeading()
   Finally
      MyBase.UpdateEditText()
   End Try
End Sub

<Browsable(False)> _
Public Shadows Property Text() As String
   Get
      Return MyBase.Text
   End Get
   Set(ByVal Value As String)
      MyBase.Text = Value
   End Set
End Property

End Class
------------------------------------------------

Explanations
On initialization, in method InitValues, I create 360 strings (000,001,...,359), and add them to the items collection of the DomainUpDown base. The method ConvertTo3Digits does the convertion from int to 3 digits string.
In order to make the control fill more like a NumericUpDown, I added a Value property, which is numeric. Convertion to string and vice versa is done. The event ValueChanged also emulate the corresponding event of NumericUpDown control.
The Items and Text properties is no longer needed for use, so they are shadowed and marked as Browsable false.

FixHeading method is a nice feature - when the user enters a number out of range 0..359, the control automatically convert the number to the appropriate degree (For example: -90 => 270).

Extensions
You can extend the control by adding an Increment property (pressing on the UpDown buttons as of now only increase or decrease by one).
Also, you can add Maximum and Minimum properties.
In addition, the range -180..180 can be useful.

0 Comments:

Post a Comment

<< Home

Feel free to use everything here. Add links to my site if you wish.

Do not copy anything to other sites without adding link to here.

All the contents of the site belong to Yariv Hammer.