Introduction
I am a GIS developer, mainly use ArcEngine, which contains a useful color selector control, as shown below:
But ArcEngine is too big and complicated, so I'd like to build my own color select control, written in pure C#, then it can be used in any C# programs.
Background
The color selector control is used to display and select colors, always used with a button with menu, like this:
so the menu button should also be built.
Basic Requirement
The ColorSelector control is displayed via MenuButton, which contains the following requirements:
- Display a rectangle and triangle on a button.
- Display the
ColorSelector
control as a contextmenu
when clicked. - Change the filling color of rectangle after user picked a color in
ColorSelector
.
For the ColorSelector control, these are the requirements:
- Drawing the default colors.
- Display the input color, output the picked color.
- Enhance the color when the mouse hovered.
- For the colors not contained in default list, display custom color box for user.
Implementation
MenuButton
MenuButton can be extended from a button, followed by this article, the button with drop down menu can easily be built via GDI+, here are the painting codes:
protected override void OnPaint(PaintEventArgs pevent)
{
this.Text = "";
base.OnPaint(pevent);
int arrowX = ClientRectangle.Width - 14;
int arrowY = ClientRectangle.Height / 2 - 1;
Point[] arrows = new Point[] { new Point(arrowX, arrowY),
new Point(arrowX + 7, arrowY), new Point(arrowX + 3, arrowY + 4) };
pevent.Graphics.FillPolygon(m_TriBrush, arrows);
int linex = ClientRectangle.Width - 18;
int liney = 2;
pevent.Graphics.DrawLine(m_LinePen1, linex, liney, linex,
liney + ClientRectangle.Height - 6);
pevent.Graphics.DrawLine(m_LinePen2, linex + 1, liney, linex + 1,
liney + ClientRectangle.Height - 6);
pevent.Graphics.FillRectangle(m_FillBrush, 4, 4,
linex - 7, liney + ClientRectangle.Height - 11);
pevent.Graphics.DrawRectangle(m_OutLinePen, 3, 3,
linex - 6, liney + ClientRectangle.Height - 10);
}
Please pay attention to the triangle and split line, which are drawn at the absolute position, so the width of the MenuButton
should not be too small, or there will be an exception.
ColorSelector Control
None of the default controls can be used to build the ColorSelector
, and since there are so many colors to display, I decided to paint them directly on a UserControl
with GDI+.
First, paint the borders:
g.DrawLine(m_OutlineBorderPen, new Point(0, 239), new Point(219, 239));
g.DrawLine(m_OutlineBorderPen, new Point(219, 0), new Point(219, 239));
g.DrawLine(m_OutlinePen, new Point(1, 1), new Point(1, 238));
g.DrawLine(m_OutlinePen, new Point(1, 1), new Point(217, 1));
g.DrawLine(m_EnhancePen, new Point(218, 2), new Point(218, 238));
g.DrawLine(m_EnhancePen, new Point(1, 238), new Point(218, 238));
Then, the colors:
g.DrawRectangle(m_EnhancePen, m_TopRect);
g.DrawString("No Color", m_Font, m_FontBrush, 86, 8);
for (int i = 0; i < ROWCOUNT; i++)
{
for (int j = 0; j < COLUMNCOUNT; j++)
{
ColorUnit cu = m_ColorArray[i, j];
g.FillRectangle(cu.Brush, new Rectangle(cu.InnerPos, cu.InnerSize));
g.DrawRectangle(m_EnhancePen, new Rectangle(cu.InnerPos, cu.InnerSize));
}
}
g.DrawLine(m_EnhancePen, new Point(6, 212), new Point(213, 212));
g.DrawLine(m_OutlinePen, new Point(6, 213), new Point(213, 213));
g.DrawString("More Colors...", m_Font, m_FontBrush, 75, 220);
Enhance the initial color:
if (m_InitUnit != null)
{
EnhanceRectangle(g, m_InitUnit.OutterPos, m_InitUnit.OutterSize.Width,
m_InitUnit.OutterSize.Height, true);
}
When the mouse moved over a color, it should be displayed as hovered:
if (m_HoverUnit != null)
{
EnhanceRectangle(g, m_HoverUnit.OutterPos,
m_HoverUnit.OutterSize.Width, m_HoverUnit.OutterSize.Height);
}
Here is the code for judging a color to be hovered, in mousemove
event:
void ucColorPicker_MouseMove(object sender, MouseEventArgs e)
{
m_HoverUnit = null;
int i = (e.X - 5) / 17, j = (e.Y - 31) / 17;
if (i >= 0 && i < COLUMNCOUNT && j >= 0 && j < ROWCOUNT)
{
m_HoverUnit = m_ColorArray[j, i];
this.Refresh();
}
}
The default colors are drawn in fixed size and position, which make it easy to get the right color.
When a color was picked by the user, it should be outputted, here is the custom event for output:
public delegate void ColorSelectedHandler(object sender, Color color);
public event ColorSelectedHandler ColorSelected;
Fires in the click event:
void ucColorPicker_MouseClick(object sender, MouseEventArgs e)
{
if (ColorSelected != null)
{
ColorSelected(this, SelectedColor);
}
}
If the user clicks on the other place, the ColorSelector
should be hidden as the contextmenu
, so the LostFocus
event was used:
this.LostFocus += new EventHandler(FormColorPicker_LostFocus);
void FormColorPicker_LostFocus(object sender, EventArgs e)
{
this.Hide();
}
The System.Windows.Forms.ColorDialog
is used to implement the custom colors, shown when the "More Colors..." was Clicked:
void ucColorPicker_MouseClick(object sender, MouseEventArgs e)
{
Color SelectedColor = Color.FromArgb(0);
else if (m_BottomSelected)
{
this.Hide();
if (m_ColorDlg.ShowDialog() == DialogResult.OK)
{
SelectedColor = m_ColorDlg.Color;
ok = true;
}
}
}
The final result looks like this:
Using the Code
Add reference for GISColorSelector.dll, drag the MenuButton
to Form
, run the program and the MenuButton
will show, click the MenuButton
, the ColorSelector
displays. The source code and demo are in the TestMenuButton_src.zip.