Page 1 of 1

Custom User Controls: Knobs Rate Topic: -----

#1 modi123_1  Icon User is offline

  • Suitor #2
  • member icon



Reputation: 8939
  • View blog
  • Posts: 33,521
  • Joined: 12-June 08

Posted 26 August 2011 - 09:02 PM

*
POPULAR

User controls are well loved compartmentalized chunks GUI that you can slap down in a moment's notice. It was suggested by a new member that they wanted to know how to make a knob that turns. I've done it before for a previous job, but damned if I have that code anymore and thus birthed this tutorial.

The gist is pretty straight forward - even with this crude make up. There's a background image that's static and a secondary image (the knob) that sits on top and the user would rotate the knob through mouse or keyboard interactions and the Graphics name space.

There are a few tricky spots. The first tricky part of the code is in the "MyRotate" method. That's where the incoming image is take in, rotated with a graphics object and spit out.

The second hardest problem is lining up the images. I drew both of these in paint.net in about five minutes. Pretty simple. I would stick to nice round numbers for image sizes just so you are not stuck pushing and pulling the locations around.

The controls take a set value for how far (in degrees) you want to turn the knob when moved, how many times it can move to the right, and the current value of where the knob is.

With regards to the mouse a user can click and hold to move the knob right or left. There is a "sensitivity" option. Basically when the user holds down the mouse the user control saves the location... every time the mouse moves the user control determines the distance and if that is greater than the 'mouse sensitivity' it updates the knob in the appropriate direction. This 'knob moving' also fires an event to let any parent control or form know the value has changes. Pretty handy if you want to pull values in real time!

I ended up adding my images directly to my project's resource area and load from there. When I push this into my general library I'll do that to keep the images hand.

Right click on your project -> properties -> resources -> add existing files -> select images.

Topics covered:
  • User control
  • keyboard input
  • mouse input
  • properties
  • drawing images
  • user control events


Attached Image

''' <summary>
''' A condensed custom knob control that is made out of two images and rotates with a fairly consistent manner.
''' </summary>
''' <remarks></remarks>
Public Class CustomKnobControl
    '-- our image objects
    Private _bitmapBack As Bitmap = Nothing
    Private _bitmapKnob As Bitmap = Nothing

    '-- the image top left location for easy reference.
    Private _pBack As Point
    Private _pknob As Point

    '-- how far to rotate teh image
    Private _sMaxAngle As Single = 15
    '-- the current angle rotoated so far.
    Private _sAngle As Single = 0

    '-- how many turns ot the right can the knob go?
    Private _lMax As Int32 = 11
    '-- the current knob
    Private _lCurrent As Int32 = 0

    '-- temp object track where the mouse location was when it was last polled
    Private _pointMouseDown As Point

    '-- increase or decreaase this to shorten the distance threshold to clicking the knob over one direction or the other.
    Private _lMouseSensitivity As Int32 = 0

    '-- if the knob rotates let the parent control know in case we want the value
    Public Event ValueChange()

    '-- Returns the current knob value
    Public Property Value As Int32
        Get
            Return _lCurrent
        End Get
        Set(value As Int32)

        End Set
    End Property

    '-- Property to the consistent angle to rotate.
    Public Property AngleMovement As Single
        Get
            Return _sMaxAngle
        End Get
        Set(value As Single)
            _sMaxAngle = value
        End Set
    End Property

    '-- When polling the mouse's location - how far must it have traveled before a knob is moved?
    Public Property MouseSensitivity As Int32
        Get
            Return _lMouseSensitivity
        End Get
        Set(value As Int32)
            _lMouseSensitivity = value
        End Set
    End Property

    '-- how many clicks can the knob go?
    Public Property MaxValue As Int32
        Get
            Return _lMax
        End Get
        Set(value As Int32)
            _lMax = value
        End Set
    End Property

    '-- constructor
    Public Sub New()

        ' This call is required by the designer.
        InitializeComponent()
        ' Add any initialization after the InitializeComponent() call.

        '-- I added the controls to my resource's image area.  Right click on your project -> properties -> resources -> add existing files.
        '_bitmapBack = New Bitmap("C:\Code\Test\sandbox\Tutorial\back.png")
        '_bitmapKnob = New Bitmap("C:\Code\Test\sandbox\Tutorial\knob2.png")
        _bitmapBack = New Bitmap(My.Resources.back)
        _bitmapKnob = New Bitmap(My.Resources.knob2)

        '-- default everything up
        _sMaxAngle = 15
        _sAngle = 0
        _lMax = 11
        _lCurrent = 0
        _lMouseSensitivity = 35

        '-- Determine how to center the images so they line up with each other.
        Dim x As Int32 = 0
        Dim y As Int32 = 0

        x = CInt(Me.Width / 2)
        x -= CInt(_bitmapBack.Width / 2)
        y = CInt(Me.Height / 2)
        y -= CInt(_bitmapBack.Height / 2)
        _pBack = New Point(x, y)

        x = CInt(Me.Width / 2)
        x -= CInt(_bitmapKnob.Width / 2)
        y = CInt(Me.Height / 2)
        y -= CInt(_bitmapKnob.Height / 2)
        _pknob = New Point(x, y)

        _sAngle = -75 '-- start the knob pointing off to the left
    End Sub

    ''' <summary>
    ''' Rotation routine.  
    ''' </summary>
    ''' <param name="incoming_bitmap"></param>
    ''' <param name="angle"></param>
    ''' <returns>Rotated bitmap</returns>
    ''' <remarks></remarks>
    Private Function MyRotate(ByVal incoming_bitmap As Bitmap, ByVal angle As Single) As Bitmap
        Dim bitmapReturn As Bitmap = New Bitmap(incoming_bitmap.Width, incoming_bitmap.Height) '-- rotated image to return.
        Dim tempGraphic As Graphics = Graphics.FromImage(bitmapReturn) '-- need hook to a graphics object.
        tempGraphic.TranslateTransform(CSng(incoming_bitmap.Width / 2), CSng(incoming_bitmap.Height / 2)) '-- get the center of the image-ish
        tempGraphic.RotateTransform(angle) '-- do the rotation.
        tempGraphic.TranslateTransform(-CSng(incoming_bitmap.Width / 2), -CSng(incoming_bitmap.Height / 2)) '-- make sure the image is back to where the center is
        tempGraphic.DrawImage(incoming_bitmap, New Point(0, 0)) '-- draw the translated image which is to the return bitmap.
        Return bitmapReturn
    End Function

    ''' <summary>
    ''' If a keyboard or mouse input is coming in move the knob to the appropriate angle.
    ''' </summary>
    ''' <param name="bRight"> true = right, false = left</param>
    ''' <remarks></remarks>
    Public Sub DoMove(ByVal bRight As Boolean)
        If bRight AndAlso _lCurrent < _lMax Then
            _lCurrent += 1
            _sAngle += _sMaxAngle
            Refresh()
            RaiseEvent ValueChange()
        ElseIf Not bRight AndAlso _lCurrent > 0 Then
            _lCurrent -= 1
            _sAngle -= _sMaxAngle
            Refresh()
            RaiseEvent ValueChange()
        End If
    End Sub

    ''' <summary>
    ''' Basic distance forumla
    ''' </summary>
    ''' <param name="point1"></param>
    ''' <param name="point2"></param>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Private Function Distance(ByVal point1 As Point, ByVal point2 As Point) As Int32
        Dim lReturn As Int32 = 0

        lReturn = CInt(Math.Sqrt((point2.X - point1.X) ^ 2 + (point2.Y - point1.Y) ^ 2))

        Return lReturn
    End Function

    '-- every refresh draw the base of the knob first, then rotate the picture as needed.
    Private Sub rotate_image_tutorial_Paint(sender As Object, e As System.Windows.Forms.PaintEventArgs) Handles Me.Paint
        e.Graphics.DrawImage(_bitmapBack, _pBack)
        e.Graphics.DrawImage(MyRotate(_bitmapKnob, _sAngle), _pknob.X, _pknob.Y)
    End Sub

    '--When the user clicks the mouse down snag the location.  Important to determining if the mouse moved far enough to click the knob over a notch.
    Private Sub rotate_image_tutorial_MouseDown(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseDown
        _pointMouseDown = e.Location
    End Sub

    '-- When the user is moving their mouse determine if the distance moved is great enough for the sensitivity to rotate the knob left or right.
    Private Sub rotate_image_tutorial_MouseMove(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseMove
        If _pointMouseDown <> Nothing AndAlso (Distance(e.Location, _pointMouseDown) > _lMouseSensitivity) Then
            If e.X > _pointMouseDown.X Then
                DoMove(True)
                _pointMouseDown = e.Location
            ElseIf e.X < _pointMouseDown.X AndAlso (Distance(_pointMouseDown, e.Location) > _lMouseSensitivity) Then
                DoMove(False)
                _pointMouseDown = e.Location
            End If
        End If
    End Sub

    '-- When the user let's go clear the holder for the mouse location.
    Private Sub rotate_image_tutorial_MouseUp(sender As Object, e As System.Windows.Forms.MouseEventArgs) Handles Me.MouseUp
        _pointMouseDown = Nothing
    End Sub

    '-- If the user uses the arrow keys or a/d keys move the knob in the appropriate direction.
    Private Sub rotate_image_tutorial_KeyDown(sender As Object, e As System.Windows.Forms.KeyEventArgs) Handles Me.KeyDown
        If e.KeyCode = Keys.Right OrElse e.KeyCode = Keys.D Then
            DoMove(True)
        ElseIf e.KeyCode = Keys.Left OrElse e.KeyCode = Keys.A Then
            DoMove(False)
        End If
    End Sub


End Class



Once the user control is created and compiles it will show up in your designer's toolbox. Then you can add it like you would normally add a button! The code's self contained and should work right out of the gate. My mainform is pretty vacant with the exception that every time the knob event fires the main form updates a label with the knobs value.

I will zip up the code, the images, and you can add the forms to a windows project. I don't believe the code should break, but it was built in 2010 and using 3.5.
Attached File  knob.zip (26.76K)
Number of downloads: 1269

Helpful links:
http://msdn.microsof...g.graphics.aspx


Advanced topics:
  • stop the image flickering
  • make the images stay centered but also response to resizing.
  • line up image with tick marks or make them on the fly.


Related tutorials
Simple drawing selection shape (or rubberband shape)

Smashing Magazine has a nice graphic tutorial if you want to make a more snazzy knob.
Turn Up the Volume With a Moody PSD Interface

Is This A Good Question/Topic? 6
  • +

Replies To: Custom User Controls: Knobs

#2 MATTtheSEAHAWK  Icon User is offline

  • D.I.C Addict
  • member icon

Reputation: 137
  • View blog
  • Posts: 782
  • Joined: 11-September 10

Posted 26 August 2011 - 10:00 PM

I don't use VB.Net but I have to admit. This is really nice!
Was This Post Helpful? 0
  • +
  • -

#3 modi123_1  Icon User is offline

  • Suitor #2
  • member icon



Reputation: 8939
  • View blog
  • Posts: 33,521
  • Joined: 12-June 08

Posted 04 September 2011 - 01:09 PM

Just an FYI - smoothing out and not flickering are obtained by setting some styles to the user control.

An example is highlighted in my form constructor from this tutorial.
http://www.dreaminco...-basics-part-1/

Me.SetStyle(ControlStyles.UserPaint, True)
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)

Was This Post Helpful? 1
  • +
  • -

Page 1 of 1