Overview
This article describes a simple Windows control written in C# that simulates a wheel knob like that on your walkman used to change volume. The control is drawn using animation technique. I use a bitmap that shows the wheel in various position that will be displayed sequentially when the control is dragged. Some properties give the user the possibility to change range of possible values returned by the control. More over sensitivity property act like a gain on wheel movements.
Description
The solution consists of two projects: TestContainer and WheelCtrl. The first project is a simple container for the WheelCtrl control that we want to test. To see the control in action simple set the TestConteiner project as the startup project by right click on TestContainer Project and selecting Set as StartUp Project on pull-up menu.
I would spend two more words on WheelCtrl properties and event. The control can be drawing on X or Y axes depending on WheelAxe property. Drawing the wheel on both axes it is simple obtained by rotating the bitmap of 90 degrees. The property Loop let values to start from Min value once the values get over the Max Value and vice versa. Last but not least Value property returns the current value. This control has only one event: DragWeel. This event is triggered when the wheel is dragged and returns the coordinate of the mouse pointer.
Source Code
I think that most of the source code is quite simple. I will explain the draw procedure that is the hart of the control.
protected override void OnPaint(PaintEventArgs e)
{
Graphics grfx = e.Graphics;
Rectangle destRect;
Rectangle srcRect;
if(wheelAxe == Orientation.YAxe)
{
destRect = new Rectangle(0, 0, 8, aImage.Height);
srcRect = new Rectangle(nPos*(8 - 1), 0, 8 ,aImage.Height);
}
else
{
destRect = new Rectangle(0, 0, aImage.Width, 8);
srcRect = new Rectangle(0, nPos*(8 - 1), aImage.Width, 8);
}
grfx.DrawImage(aImage, destRect, srcRect, GraphicsUnit.Pixel);
}
To draw the wheel, I used one of the 30 different versions of the DrawImage function.
This function takes as first parameter the bitmap of the various wheel draw in all the different position that will be displayed in sequence. The two Rectangle variables are use to set destination client area of the control to draw (destRect) with one of the nine slice, eight bit wide, of the source bitmap to display. The nPos variable is used to point to one of this slice. To calculate nPos I use a little math formula that convert mouse movements in a suitable integer value between 0 and 8.
private void DragWheel(int dx)
{
double ddx = (dx != 0) ? Math.Abs(dx) : 1.0;
double xx = dx / ddx * Math.Log(Math.Abs(dx) + 2.0);
nPos = (int) ((xx != 0 && (nPos+xx <= 0 || nPos+xx >= 8 )) ? 4 : nPos +xx);
ddx = dValue + xx + ((xx - xx/ddx) * dSensitivity);
. . . . .
}
Finally this is the routine that rotate the bitmap according to the axes that we choose to draw the wheel. The bitmap that I use infect, show the wheel lay on the X axe. If we want the control draw on the Y axe we must rotate the bitmap of 90 degrees and flip on the X to get the right image to display when we drag mouse trough up or down direction. Try to change RotateFlipType.Rotate90FlipX with RotateFlipType.Rotate90FlipNone to see what I mean.
private void RotateComponent()
{
aImage = new Bitmap(GetType(), "wheelh.bmp");
if(wheelAxe == Orientation.YAxe)
{
aImage.RotateFlip(RotateFlipType.Rotate90FlipX);
this.Size = new Size(8, aImage.Height);
}
else
{
this.Size = new Size(aImage.Width, 8);
}
this.Invalidate();
}
Technique used to draw a control can lead to nice result but it became little convenient when the bitmap size are large. The first version of this component was written by me for Visual C++ with ATL so it was quite naturally for me use bitmap image format. Now, with .NET we can use also jpeg images, so there is the possibility to represent more complex image sequence. I will be glad if someone of you will try to write a control using this technique.