Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Creating a color selection palette control

0.00/5 (No votes)
2 Jan 2005 1  
Creating a user control for selecting a foreground and background color using a palette.

Sample Image - ColorPaletteControl.jpg

Introduction

My first attempt to create a color selection control (the ColorMap control) actually had two major drawbacks. First: where are the grays? This was the first remark I got. And to be honest with you, grays are not easily found in the ColorMap. Oh, they're there. If you count the number of colors with red-value equals green-value equals blue-value (by definition a gray value), you'll find approximately 400 values of grays. But that's theory and you still can't select them!

That brings me to the second major drawback. One I already explained in the ColorMap article. Selecting a color works fine, selecting the exact same color again is like a mission impossible.

So what we actually need is a palette of colors. Not thousands of colors, but rather a fixed number of distinct colors, so that we easily can remember which color it was we selected yesterday. However, we have to make sure all color types are available. Oh, and this time we want to see grays!

Creating the control

Before actually creating the control, let's summarize the functionality of the control:

  • display a palette of colors containing all types of colors and grays.
  • set the foreground color property by left clicking anywhere on the color map.
  • set the background color property by right clicking anywhere on the color map.

And since we have palette, we add two more requirements:

  • set the number of colors displayed per column.
  • set the size of area displaying a single color.

The ColorMap control is a good place to start. It contains most of the functionality listed. New are the values ColorsPerColumn and ColorSize. Also, the CreateColorMap method of the ColorMap control is renamed to CreateColorPalette. As in the ColorMap control, the method returns a bitmap.

The color palette

The color palette is nothing more than a list of colors displayed as colored squares. Each colored square being of size ColorSize. So, the problem is creating the list of colors to be displayed and have them ordered in a way that makes sense.

The first problem was not that hard to solve. We could write some logic to create a list of colors, but the .NET Color class contains all the colors we want to see. Getting these colors is a matter of using reflection to inspect the Color class and extract the static color properties. Here's how to do it.

PropertyInfo[]   properties;
ArrayList        colors;
Color            color;
SolidBrush       brush;

// get all colors defined as properties in the Color class

properties = 
    typeof (Color).GetProperties (BindingFlags.Public | BindingFlags.Static);
colors = new ArrayList ();
foreach (PropertyInfo prop in properties) {
    // get the value of this static property

    color = (Color) prop.GetValue (null, null);
    // skip colors that are not interesting

    if (color == Color.Transparent) continue;
    if (color == Color.Empty) continue;    
    // create a solid brush of this color

    brush = new SolidBrush (color);
    colors.Add (brush);
}

If you display this list of colors, you'll find that they're randomly scattered. That's because the colors are ordered alphabetically by name.

What we need is a good way to sort the colors. One method of ordering is using the RGB-value. Again, this will create an order in the colors that seems random to the human eye. Luckily, there is an alternative to RGB-ordering. It is called hue-saturation-brightness (or HSB). The value hue determines the location of the color in the color circle. For those of you who don't know what a color circle is, here's how it looks like:

Sample image

The saturation value determines how much grey is in the color. The brightness value determines the color's intensity.

The sorting algorithm first orders by hue, then by saturation, and finally by brightness. The logic is captured in the internal class _ColorSorter which implements the IComparer interface. Note that the sorting is identical to the sorting of colors in the property window in Visual Studio.

/// <SUMMARY>

/// The class _ColorSorter orders the colors based on the hue,

/// saturation and brightness. This is the

/// order that is also used by visual studio.

/// </SUMMARY>

internal class _ColorSorter: System.Collections.IComparer {

    #region IComparer Members

    public int Compare (object x, object y) {
        // local variables

        Color                    cx, cy;
        float                    hx, hy, sx, sy, bx, by;

        // get Color values

        cx = ((SolidBrush) x).Color;
        cy = ((SolidBrush) y).Color;
        // get saturation values

        sx = cx.GetSaturation ();
        sy = cy.GetSaturation ();
        // get hue values

        hx = cx.GetHue ();
        hy = cy.GetHue ();
        // get brightness values

        bx = cx.GetBrightness ();
        by = cy.GetBrightness ();

        // determine order

        // 1 : hue       

        if (hx < hy) return -1; 
        else if (hx > hy) return 1;
        else {
            // 2 : saturation

            if (sx < sy) return -1;
            else if (sx > sy) return 1;
            else {
                // 3 : brightness

                if (bx < by) return -1;
                else if (bx > by) return 1;
                else return 0;
            }
        }
    }

    #endregion
}

Points of Interest

The list of colors displayed contains what I call 'white gaps'. At first, I considered this to be a sorting problem, that is, until I took a good look at the colors shown in the Visual Studio property window and found that the same 'white gaps' were shown here also.

It would be interesting to see an algorithm that generates a list of colors that, when sorted by hue-saturation-brightness, doesn't create white gaps in the palette.

History

  • 2004-12-21 - Initial release of article.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here