Creating your own cool Volume Control using GDI+


Figure 1 - The Volume Control Sample

It turns out, it's much less painful to create your own controls in .NET than it ever was with former Microsoft development products. You can even design an entire look and feel for the control.  Its also easy to add your own events!  In this article, I'll give you an example of creating your own control. The particular control described in this article is a volume dial control. This control allows you to grab the red dot on the control and turn the dial in either direction to set a volume. Below is the class design of the Volume control:

Figure 2 - Volume Control UML Diagram Reverse Engineered using WithClass 2000

The Volume control also contains an event VolumeChanged, which allows you to have the control on a form to respond to the event when the volume actually changes value.

I actually stole a lot of the code for this article from a previous article I wrote, a Virtual Clock in .NET for doing all the drawing of the numbers and the dial.  The behavior of the control, however, is a bit different.

The states diagram below shows the events that turn the control:

Figure 3 - UML State Chart Drawn using WithClass 2000

This state diagram is implemented in the VolumeControl_MouseDown, VolumeControl_MouseMove, and VolumeControl_MouseUp event handlers respectively.  The MouseMove event handler  is shown below:

private void VolumeControl_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e)
{
if (MovingDial)
{
// get mouse position
double xpos = (double)e.X;
double ypos = (double)e.Y;
// calculate the center of the dial
double xcenter = ClientRectangle.Width/2;
double ycenter = ClientRectangle.Width/2;
// subtract the center from the actual position to get the relative position to the midpoint
xpos -= xcenter;
ypos -= ycenter;
// now we can calculate the angle using the arctangent function. this arctangent function
// is nice because it takes the sign of x and y into account when figuring the angle
double angle = Math.Atan2(ypos, xpos);
// Set the property to the new angle
TheOffset = angle;
// Set the red dot position
TheDotPosition = TheOffset;
// Get the old volume to use to see if its been changed
int OldVolume = TheVolume;
// Calculate the the volume (number from 1-10) using the angle of the offset of the dial from
// its initial position
CalcVolume(TheOffset);
// if the old volume doesn't equal the new volume, then fire a VolumeChanged Event
if (OldVolume != TheVolume)
{
VolumeChanged(this, e);
}
// force the dial to redraw with the new position
Invalidate();
}
}

If you've gone through the code and comments above, you'll notice that the MouseMove event will also fire our custom event, the VolumeChanged Event if the Volume has changed value.  This event is set up using the following two lines at the beginning of the class:

// set up the event handler delegate and declare the event itself
public delegate void VolumeChangedEventHandler(object sender, EventArgs e);
public event VolumeChangedEventHandler VolumeChanged;

Once you've created an instance of the event, you can fire it wherever you feel is appropriate in your code. To use the event of the control in the form, you can either do so directly through the IDE by clicking on the lightening bolt in the properties window or you can add an event handler to your form and wire it up:

Here is the event handler for the VolumeChanged event in the form that causes the text of a label to change:

private void volumeControl1_VolumeChanged(object sender, System.EventArgs e)
{
label1.Text = volumeControl1.Volume.ToString();
}

Wiring is shown here and goes in the form initialization:

this.volumeControl1.VolumeChanged += new VolumeControl.VolumeControl.VolumeChangedEventHandler this.volumeControl1_VolumeChanged);

Improvements

The dial control can definitely stand for a few improvements.  One improvement  is to give the control color properties for numbers and background.  Another is to give the control more of a 3-d look.  (Too bad I'm not more of an artist  <g>) .  Also it would be nice if you could specify the range of numbers on the control.  Anyway,  I hope this control gets you started in creating your own custom GUI under .NET.