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.
- User control
- keyboard input
- mouse input
- drawing images
- user control events
''' <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.
Number of downloads: 2112
- 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.
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