Introduction
Colors are the breath of the Internet. This article discusses some color palettes and then describes a tool that may be used to choose appropriate colors for a web page.
Background
Color History
In the early history of computing, color was not an important attribute of a successful application. Most computer terminals had a green or amber display. Furthermore, most terminals were text oriented, displaying a fixed number of characters per row and a fixed number of rows per screen. In large part, most of the applications that ran on computer terminals were command line interpreters, text editors, and modem control applications. Although there were graphical terminals available, they were very expensive and used only in specialized environments.
In 1973, the Xerox Palo Alto Research Center (PARC) developed the personal workstation, the forerunner of the modern personal computer. In 1975, PARC introduced the Graphical User Interface, with its familiar desktop and icons. To support these Parc developed bit-mapped graphics.
In 1981, with the introduction of the personal computer, IBM also introduced the Computer Graphics Adapter (CGA), sixteen colors became available to the application developer.
In 1984, IBM introduced the Enhanced Graphics Adapter (EGA) with 64 colors.
Then, in 1987, IBM introduced the Video Graphics Array (VGA). The VGA palette provided 256 colors.
With the introduction of X windows, a palette, known as the X11 color names became available. The most important distinction between the X11 color names and the earlier color palettes was the ability to choose a color by specifying either its name or its RGB triplet.
Finally, with the introduction of .NET Framework 1.1, the Known Colors Palette was formalized by Microsoft. Although this palette includes some colors used by Microsoft to display various system entities (e.g., ActiveBorder
, AppWorkspace
, Control Dark, etc.), during the enumeration of the known colors, we can exclude these Microsoft system colors.
Need
While testing the Color Hit Testing User Control, I required a test image. I decided that a display of the US Time Zones would suffice. Because I was preparing the article for viewing on the Internet, I decided to color the image with the known colors.
I was using Microsoft Paint to create the image. During the image preparation, I stumbled across a couple of limitations in that tool's Colors?Edit Colors... dialog:
- The colors that are displayed are not the known colors.
- The colors that are initially displayed are not the known colors.
- The dialog does not display a color name when the mouse hovers over the color.
To solve these two problems, I built the Known Colors Palette Tool. The requirements levied against the tool included:
- The colors that are displayed are to be the known colors.
- When the mouse hovers over a color swatch, the color name is to be displayed.
- The colors may be sorted either by known color name or by the known colors' ARGB values.
- When the mouse clicks on a color swatch, the ARGB value of the known color shall be displayed along with the individual ARGB component values.
- A context menu shall be provided that allows the user to perform the sorts, keep the tool on top, and allow the appropriate color information to be copied to the Clipboard.
Known Colors Palette Tool
The Known Colors Palette Tool is based upon the KnownColor Enumeration that is provided by Microsoft as part of the System.Drawing
assembly. Microsoft's enumeration, with the exception of the system colors, is equivalent to the CSS color names. During the enumeration of the Microsoft known color names, those known colors that are Microsoft system colors are excluded.
private List < Color > get_known_colors_from_Microsoft ( )
{
List < Color > colors = new List<Color> ( );
string [ ] color_names =
Enum.GetNames ( typeof (
KnownColor ) );
foreach ( string color_name in color_names )
{
KnownColor known_color = ( KnownColor ) Enum.Parse (
typeof ( KnownColor ),
color_name );
if ( ( known_color > KnownColor.Transparent ) &&
( known_color < KnownColor.ButtonFace ) )
{
colors.Add ( Color.FromName ( color_name ) );
}
}
return ( colors );
}
The first task, after the colors are enumerated, is to create two panels, one to display the colors by name, the other to display the colors by ARGB. Both panels are created and then hidden. I create two separate panels to improve performance when switching between the two displays.
private void create_colors_by_name_panel ( )
{
NameComparer nc = new NameComparer ( );
known_colors.Sort ( nc );
colors_by_name_panel = populate_panel ( );
this.Controls.Add ( colors_by_name_panel );
colors_by_name_panel.Visible = false;
}
private void create_colors_by_ARGB_panel ( )
{
ARGBComparer ac = new ARGBComparer ( );
known_colors.Sort ( ac );
colors_by_ARGB_panel = populate_panel ( );
this.Controls.Add ( colors_by_ARGB_panel );
colors_by_ARGB_panel.Visible = false;
}
Two classes, NameComparer
and ARGBComparer
, were implemented to perform the comparisons.
public class NameComparer : IComparer < Color >
{
public int Compare ( Color color_1,
Color color_2 )
{
if ( color_1 == null )
{
if ( color_2 == null )
{
return ( 0 );
}
else
{
return ( -1 );
}
}
else
{
if ( color_2 == null )
{
return ( 1 );
}
else
{
return ( color_1.Name.CompareTo (
color_2.Name ) );
}
}
}
}
public class ARGBComparer : IComparer < Color >
{
public int Compare ( Color color_1,
Color color_2 )
{
if ( color_1 == null )
{
if ( color_2 == null )
{
return ( 0 );
}
else
{
return ( -1 );
}
}
else
{
if ( color_2 == null )
{
return ( 1 );
}
else
{
uint ARGB_1;
uint ARGB_2;
ARGB_1 = Utilities.ColorToUIntARGB ( color_1 );
ARGB_2 = Utilities.ColorToUIntARGB ( color_2 );
return ( ARGB_1.CompareTo ( ARGB_2 ) );
}
}
}
}
When the known colors list is sorted, it is used to populate the panel.
private Panel populate_panel ( )
{
Point color_square_location;
Size color_square_size;
int column = 0;
Panel panel = new Panel ( );
panel.Location = new Point ( PANEL_LEFT, PANEL_TOP );
panel.Size = new Size ( PANEL_WIDTH, PANEL_HEIGHT );
color_square_location = new Point ( INITIAL_LEFT,
INITIAL_TOP );
color_square_size = new Size ( COLOR_SQUARE_EDGE,
COLOR_SQUARE_EDGE );
foreach ( Color color in known_colors )
{
Button color_square = new Button ( );
ToolTip tooltip = new ToolTip ( );
color_square.Location = color_square_location;
color_square.Size = color_square_size;
color_square.BackColor = color;
color_square.Click += new System.EventHandler (
color_square_BUT_Click );
tooltip.SetToolTip ( color_square, color.Name );
tooltip.AutomaticDelay = TOOLTIP_DELAY;
panel.Controls.Add ( color_square );
column++;
if ( column >= COLOR_SQUARES_PER_ROW )
{
column = 0;
color_square_location.X = INITIAL_LEFT;
color_square_location.Y +=
COLOR_SQUARE_EDGE + COLOR_SQUARE_SEPARATION;
}
else
{
color_square_location.X +=
COLOR_SQUARE_EDGE + COLOR_SQUARE_SEPARATION;
}
}
return ( panel );
}
On initialization, the variable sort_by_name
is true
and the colors by name panel is displayed.
private void initialize_GUI ( )
{
if ( sort_by_name )
{
byNameToolStripMenuItem.Checked = true;
sortByNameToolStripMenuItem.Checked = true;
byARGBToolStripMenuItem.Checked = false;
sortByARGBToolStripMenuItem.Checked = false;
colors_by_name_panel.Visible = true;
colors_by_ARGB_panel.Visible = false;
}
else
{
byNameToolStripMenuItem.Checked = false;
sortByNameToolStripMenuItem.Checked = false;
byARGBToolStripMenuItem.Checked = true;
sortByARGBToolStripMenuItem.Checked = true;
colors_by_name_panel.Visible = false;
colors_by_ARGB_panel.Visible = true;
}
}
If the user toggles the display, the variable sort_by_name
becomes false
and the colors by ARGB panel is displayed.
When a color is selected, the information about the color is displayed.
Once a color is selected, either clicking on the color swatch or pressing Ctrl-S causes the color information to be copied to the Clipboard. For the known color DarkRed
, the information copied to the Clipboard is:
{Name=DarkRed,ARGB=(255,139,0,0)=#FF8B0000=(FF,8B,00,00)}
Although the transfer to the Clipboard is not the same as setting the color in Microsoft Paint, it does provide a means by which the data can be captured and copied into Microsoft Paint. If I can figure out how to subclass Microsoft Paint, I will revise the Know Colors Palette Tool to interact directly with Microsoft Paint. Any suggestions for this upgrade would be appreciated.
Revision
As pointed out by a reader, the screen resolution at tool development may not be the same as the screen resolution at tool execution. What looks fine at 1024x768 may not look very well at other resolutions. What is worse is that the tool may become useless as the tool shrinks at higher resolutions.
To address this issue, I redeveloped the tool at a screen resolution of 800x600. Once that was completed, I added code to initialize two screen resolution multipliers, one for horizontal, the other for vertical adjustment.
private void initialize_resolution_multipliers ( )
{
if ( ( SystemInformation.PrimaryMonitorSize.Height >
CREATED_IN_SCREEN_HEIGHT ) ||
( SystemInformation.PrimaryMonitorSize.Width >
CREATED_IN_SCREEN_WIDTH ) )
{
screen_height_multiplier =
( float ) SystemInformation.
PrimaryMonitorSize.Height /
( float ) CREATED_IN_SCREEN_HEIGHT;
screen_height_multiplier *= 4.0F;
screen_height_multiplier /= 5.0F;
screen_width_multiplier =
( float ) SystemInformation.
PrimaryMonitorSize.Width /
( float ) CREATED_IN_SCREEN_WIDTH;
screen_width_multiplier *= 4.0F;
screen_width_multiplier /= 5.0F;
color_square_edge =
( int ) ( ( float ) color_square_edge *
screen_width_multiplier );
color_square_separation =
( int ) ( ( float ) color_square_separation *
screen_width_multiplier );
resolution_changed = true;
}
}
In addition to the multipliers, two global variables, color_square_edge
and color_square_separation
are assigned adjusted values. These two variables replace the constants COLOR_SQUARE_EDGE
and COLOR_SQUARE_SEPARATION
in the populate_panel
method (see above).
Once these multipliers are initialized (note both will be 1.0F if the execution resolution is 800x600), the actual adjustment waits for the Load
event to fire, that, in turn, executes the adjust_GUI_to_resolution
method.
private void KnownColorsPalette_Load ( object sender,
EventArgs e )
{
adjust_GUI_to_resolution ( );
}
If the execution resolution is different from the execution resolution (i.e., the variable resolution_changed
is true
), the adjust_GUI_to_resolution
method modifies the location, size, and font size of each control.
private void adjust_GUI_to_resolution ( )
{
if ( resolution_changed )
{
float font_size;
int height;
int width;
float x;
float y;
font_size = this.Font.Size *
screen_height_multiplier;
height = ( int ) ( ( float ) this.Height *
screen_height_multiplier );
width = ( int ) ( ( float ) this.Width *
screen_height_multiplier );
x = this.Location.X * screen_width_multiplier;
y = this.Location.Y * screen_height_multiplier;
this.Height = height;
this.Width = width;
this.Font = new Font ( this.Font.FontFamily,
font_size,
this.Font.Style );
foreach ( Control control in this.Controls )
{
font_size = control.Font.Size *
screen_height_multiplier;
height = ( int ) ( ( float ) control.Height *
screen_height_multiplier );
width = ( int ) ( ( float ) control.Width *
screen_height_multiplier );
x = control.Location.X * screen_width_multiplier;
y = control.Location.Y * screen_height_multiplier;
control.Width = ( int ) Math.Ceiling ( ( double ) width );
control.Height = ( int ) Math.Ceiling ( ( double ) height );
control.Location = new Point ( ( int ) x,
( int ) y );
control.Font = new Font ( control.Font.FontFamily,
( int ) font_size,
control.Font.Style );
}
}
}
There is a problem with the font size of tooltips and menu items that I am still researching. When I have a solution, I will again revise this article.
References
History
- 11/26/2010 - Revised article to address reader's comments and correct typographic and logic errors
- 11/29/2010 - Revised the tool such that it reacts to execution screen resolutions that differ from the development resolution