Now that we have the XAML UI complete on our color picker (live demo here), it's time to wire up the control with some logic. To spare you the boring details, this post is really just a summary of the key pieces of logic. Obviously you can download the complete source code, and if you have any questions, please feel free to send me feedback!
As a first step, we need to make the selectors responsive to the various mouse actions. The hue selector on the left side of the control responds to mouse dragging and clicks in the Y direction. In the ColorPicker constructor, we can wire up the various mouse events. The handlers for these events look like the following:
1: void rectHueMonitor_MouseLeftButtonDown(object sender, MouseEventArgs e)
2: {
3: m_sliderMouseDown = true;
4: int yPos = (int)e.GetPosition((UIElement)sender).Y;
5: UpdateSelection(yPos);
6: }
7:
8: void rectHueMonitor_MouseLeftButtonUp(object sender, MouseEventArgs e)
9: {
10: m_sliderMouseDown = false;
11: }
12:
13: void rectHueMonitor_MouseMove(object sender, MouseEventArgs e)
14: {
15: if (m_sliderMouseDown)
16: {
17: int yPos = (int)e.GetPosition((UIElement)sender).Y;
18: UpdateSelection(yPos);
19: }
20: }
21:
22: void rectHueMonitor_MouseLeave(object sender, EventArgs e)
23: {
24: m_sliderMouseDown = false;
25: }
As you can see from the above code, the left mouse button state is tracked using an instance level boolean called m_sliderMouseDown. This member allows the user to drag the mouse and have the selector update as the mouse moves. The Saturation/Brightness selector uses the same general logic, the only difference is that both X and Y coordinates are updated for the selector.
The Hue is calculated from the y-position of the selector. A factored version of the y-position is passed to the function below:
1: public Color GetColorFromPosition(int position)
2: {
3: byte mod = (byte)(position % MAX);
4: byte diff = (byte)(MAX - mod);
5: byte alpha = 255;
6:
7: switch (position / MAX)
8: {
9: case 0: return Color.FromArgb(alpha, MAX, mod, MIN);
10: case 1: return Color.FromArgb(alpha, diff, MAX, MIN);
11: case 2: return Color.FromArgb(alpha, MIN, MAX, mod);
12: case 3: return Color.FromArgb(alpha, MIN, diff, MAX);
13: case 4: return Color.FromArgb(alpha, mod, MIN, MAX);
14: case 5: return Color.FromArgb(alpha, MAX, MIN, diff);
15: default: return Colors.Black;
16: }
17: }
The ultimate color extrapolated from Saturation/Brightness area is determined using an HSV to RGB algorithm that I ported from some JavaScript code in a DHTML color picker found here. This algorithm (ConvertHsvToRgb) accepts the selected hue, saturation, and brightness components as arguments and returns the corresponding color from the RGB color space. The hue is a value between 0 and 360 and saturation and brightness components are really just percentages.
Once you have the actual color, it's really easy to obtain the hex code for the color:
1: public string GetHexCode(Color c)
2: {
3: return string.Format("#{0}{1}{2}",
4: c.R.ToString("X2"),
5: c.G.ToString("X2"),
6: c.B.ToString("X2"));
7: }
That's really about all there is to it. Of course, I've left out a few plumbing details, but you can download the code if you are interested in that. Enjoy!