Click here to Skip to main content
Click here to Skip to main content

Why Develop Custom Controls? Just Customize Generic Controls. Part 3: ColorComboBox

, 31 Jan 2007
Rate this:
Please Sign up or sign in to vote.
ColorComboBox, no need to derive from ComboBox

Introduction

If you search for Color Picker in CodeProject, you will easily get more than one dozen of them. The one I wanted was a color picker of combo box. There are already a few in CodeProject. However, I am not going to develop a derived class of ComboBox, instead, I would use the design pattern introduced in this series of articles. You may compare this implementation with others.

Using the Code

The code was constructed with Visual Studio 2005, and it shouldn't be difficult to copy the code to other C# development environments.

/// <summary>
/// Turn a normal Combobox into a color picker.
/// </summary>
public class ColorComboAgent
{
    private ComboBox box;
    private Color[] colorArray;
    private Color currentColor;
    private bool useCustomColor;

    private bool SelectedIndexChangedEventEnabled;

    public ColorComboAgent(ComboBox comboBox)
    {
        ColorConverter cc = new ColorConverter();
        // I might use KnownColor alternatively if it is really cool
        ICollection colorCollection = cc.GetStandardValues();  
        // extra item for custom color
	    colorArray = new Color[colorCollection.Count + 1];  

        colorArray[0] = Color.White; // for custom ... trigger

        colorCollection.CopyTo(colorArray, 1);
            
        InitializeList(comboBox);

        SelectedIndexChangedEventEnabled = true;
    }

    private void InitializeList(ComboBox comboBox)
    {
        this.box = comboBox;
        box.DropDownStyle = ComboBoxStyle.DropDownList;
        box.DrawMode = System.Windows.Forms.DrawMode.OwnerDrawVariable;

        // Hook up the MeasureItem and DrawItem events
        box.DrawItem += new DrawItemEventHandler(DrawItemHandler);

        box.SelectedIndexChanged += new EventHandler(box_SelectedIndexChanged);

        LoadColorsToBox();
    }

    private void LoadColorsToBox()
    {
        if (box.Items.Count < 2)
            box.DataSource = colorArray; 
    }

    private void box_SelectedIndexChanged(object sender, EventArgs e)
    {
        if (!SelectedIndexChangedEventEnabled)
            return;

        if (box.SelectedIndex > 0)
            currentColor = colorArray[box.SelectedIndex];
        else
        {
            using (ColorDialog fm = new ColorDialog())
            {
                fm.Color = Color;
                if (fm.ShowDialog() == DialogResult.OK)
                    Color = fm.Color;
            }
        }
    }

    /// <summary>
    /// Get and set color.
    /// </summary>
    public Color Color
    {
        get { return currentColor; }
        set
        {
            int dx = -1;

            currentColor = value;
            for (int i = 0; i < colorArray.Length; i++)
            {
                if (colorArray[i] == value)
                {
                    dx = i;
                    break;
                }
            }

            useCustomColor = dx == -1;
            if (useCustomColor)
            {
                colorArray[0] = currentColor;
                dx = 0;
            }

            SelectedIndexChangedEventEnabled = false;
            box.SelectedIndex = dx;
            SelectedIndexChangedEventEnabled = true;

            box.Refresh();  //sometime there's delay, so force it to refresh
        }
    }

    private void DrawItemHandler(object sender,
        System.Windows.Forms.DrawItemEventArgs e)
    {
        // Draw the background of the item.
        e.DrawBackground();

        // Draw color hint
        Rectangle colorRectangle = new Rectangle(2, e.Bounds.Top + 1,
                e.Bounds.Height * 2, e.Bounds.Height - 2);
        e.Graphics.FillRectangle(new SolidBrush(colorArray[e.Index]), 
        		colorRectangle);

        // Draw string
        if (e.Index > 0)  // for named color
        {
            e.Graphics.DrawString(colorArray[e.Index].Name, 
			box.Font, System.Drawing.Brushes.Black,
             	new RectangleF(e.Bounds.X + colorRectangle.Width, 
			e.Bounds.Y, e.Bounds.Width, e.Bounds.Height));
        }
        else // for custom color
        {
            if (useCustomColor) // for assigned custom color
                e.Graphics.DrawString("Custom", box.Font, 
				System.Drawing.Brushes.Black,
                    new RectangleF(e.Bounds.X + colorRectangle.Width, 
				e.Bounds.Y, e.Bounds.Width, e.Bounds.Height));
            else  // for not-yet assigned custom color
                e.Graphics.DrawString("Custom...", box.Font, 
					System.Drawing.Brushes.Black,
                        new RectangleF(e.Bounds.X + 2, e.Bounds.Y, 
					e.Bounds.Width, e.Bounds.Height));
        }
    }
}

In the client code of a Form, you may change the properties through the agent.

// To setup, 
 colorComboAgent agent = new ColorComboAgent(comboBox1))
 agent.Color = Color.Red;
 
// To read
  Color buttonColor = agent.Color;

Unlike in InTextboxLabel which provides only visual effects, you will need to access ColorComboAgent to access data, so you need to keep an explicit reference in the client code.

Points of Interest

In the first 2 articles of this series, I asked a question as to what this design pattern is, as I could not exactly map the common things of these examples with a particular pattern described in "Design Patterns - Elements of Reusable Object Oriented Software" by GOF. Reminded by one of the readers of these articles, I agreed that the design pattern is more like the Decorator Pattern rather than the Builder Pattern.

I would try to illustrate common things of these examples in the next article, to discuss whether it is worthy of giving it a distinguishing name of design pattern.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Zijian
Software Developer
Australia Australia
I started my IT career in programming on different embedded devices since 1992, such as credit card readers, smart card readers and Palm Pilot. Programming on the hardware was really fun, feeling like driving the hardware directly.
 
Beside technical works, I enjoy reading literatures, playing balls, cooking and gardening.

Comments and Discussions

 
GeneralNice PinmemberNinjaCross24-Jan-07 5:49 
GeneralDesign Pattern PinmemberSteve Hansen23-Jan-07 21:09 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140827.1 | Last Updated 31 Jan 2007
Article Copyright 2007 by Zijian
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid