Although the .NET framework provides quite a rich collection of UI controls and components for WinForms development, there is one particular control that's been missing. I'm talking about a color-picker control with drop-down color selection capabilities, just like the one used within the Visual Studio .NET property browser for editing Color-typed properties.
Of course, there is the standard
ColorDialog component, but the drop-down color selector is more user-friendly, IMHO. (Should I say cool?)
The ColorPicker control
Based on the above observations, I've decided to implement a
ColorPicker control with the following features:
- The control should look and act like a button.
- The button should be colored with the currently selected color and, optionally, it should display the currently selected color's name.
- Clicking the button should drop-down the built-in WinForms color selector and the control should change its appearance to a "pushed down" look.
The main design goals were implementation simplicity and the ability to reuse the control in either binary, or in source code form.
The following is the important part of the
ColorPicker's public contract (the semantics should be self-explanatory):
Public Class ColorPicker
Public Event ColorChanged As EventHandler
Public Property Color() As Color
The core requirement for the
ColorPicker control was to display the same drop-down color selector that is used within the WinForms'
For more information about the
PropertyGrid control and its use of attributes, see the Shawn Burke's article at MSDN.
The built-in color selector is implemented inside a
ColorEditor class, which is designated as the
EditorAttribute for the
Public Structure Color
ColorEditor class is currently undocumented. The only thing one can learn from the official documentation is the infamous sentence - "This type supports the .NET Framework infrastructure and is not intended to be used directly from your code".
Nevertheless, with some help of ILDASM and Lutz Roeder's .NET Reflector, I was able to find out how a
ColorEditor instance is hosted within the
PropertyGrid. Moreover, I was also able to emulate the hosting within the
ColorPicker control, which is the focus of the remainder of this article.
To better understand the process of hosting the
ColorEditor, let's quickly recap how the
PropertyGrid control uses the
EditorAttribute when editing properties of a given object:
When a row within the
PropertyGrid gets focus, the grid looks first at the property itself, then at the property's type in order to see, if there is an
EditorAttribute applied to one of them. The
EditorAttribute specifies the
System.Type that should be used as the editor for the given property.
In theory, a type can have more than one editor. However, currently only one 'type' of editor is supported - the ones that derive (directly or indirectly) from the
In the case of a
Color-typed property, the
PropertyGrid finds the
System.Drawing.Design.ColorEditor class to be used as the editor. The grid then calls the overridden
Public Class UITypeEditor
Public Overridable Function GetEditStyle( _
ByVal context As ITypeDescriptorContext _
) As UITypeEditorEditStyle
ColorEditor implementation of this method returns always
UITypeEditorEditStyle.DropDown. This causes the
PropertyGrid to display a drop-down button on the right side of the property row.
When the user clicks the drop-down button, the
PropertyGrid calls another overridden method -
Public Class UITypeEditor
Public Overridable Function EditValue( _
ByVal context As ITypeDescriptorContext, _
ByVal provider As IServiceProvider, _
ByVal value As Object _
) As Object
ColorEditor implementation of this method does the following:
- Queries the passed in
IServiceProvider instance for an
- Stores the
IWindowsFormEditorService reference in a member variable.
- Creates an instance of a private
ColorUI class, which implements the actual user interface and interacts with the user.
- Calls the
IWindowsFormEditorService.DropDownControl method passing it the custom control instance.
- When the user selects a new color, the
ColorEditor calls the
IWindowsFormEditorService.CloseDropDown method, which (you guessed that) closes the drop-down UI and causes the
IWindowsFormEditorService.DropDownControl method to return.
In fact, this implementation of
UITypeEditor.EditValue method is common. If you've ever implemented your own drop-down
UITypeEditor, it is highly likely that you've implemented it according to the above-described pattern.
ColorEditor doesn't use the
ITypeDescriptorContext arguments, all I had to do to host it was to implement just two interfaces:
Public Interface IServiceProvider
Public Function GetService( _
ByVal serviceType As Type) As Object
Public Interface IWindowsFormsEditorService
Public Sub CloseDropDown()
Public Sub DropDownControl(ByVal control As Control)
Public Function ShowDialog( _
ByVal dialog As Form) As DialogResult
ColorEditor queries the passed in
IServiceProvider just for the
IWindowsFormsEditorService, I've implemented both interfaces in one class - the
EditorService class, which is nested within the
ColorPicker control class.
There were several other minor issues that I had to solve and I've described them as comments in the ColorPicker.vb source file.
There was one issue, however, that I'd like to discuss here in more detail.
When the drop-down color selector is displayed by calling the
IWindowsFormsEditorService.DropDownControl method, it is expected not to return only until after the user either selects a new color or she cancels the selection. The cancellation can be performed in a variety of ways: by pressing the Esc key, by clicking outside of the drop-down box, by pressing the Ctrl+Esc system key combination or by clicking the
ColorPicker button once again.
In other words, the
DropDownControl method implementation should block while dispatching Windows messages caused by the user interaction with Windows (including the drop-down color selector).
Do you remember the old pal,
This time, however, it is exposed as the
DoEvents method of the
System.Windows.Forms.Application class. Calling the method causes processing of all the Windows messages in the current thread's message queue.
Here is a pseudo code for my first
DropDownControl implementation, which uses the
Public Class ColorPicker
Private Class EditorService
Public Sub DropDownControl(ByVal control As Control) _
Do While _DropDownHolder.Visible
The code shows the drop-down UI and then it enters a loop calling
Application.DoEvents until the drop-down form is closed. This way, the
DropDownControl method blocks (ignoring the possible reentrancy issues for the moment) while Windows messages are still being dispatched.
It worked fine this way until I realized that when the drop-down color selector is displayed, the process hosting the
ColorPicker control eats 100% of the CPU.
Once again, I've turned to the .NET Reflector tool in order to see how the
DropDownControl method is implemented within the
PropertyGrid itself (which, obviously, doesn't eat 100% CPU while displaying the color selector).
Here is what I found out:
Private Class PropertyGridView
Private Class DropDownHolder
Public Sub DoModalLoop()
Do While MyBase.Visible
UnsafeNativeMethods.MsgWaitForMultipleObjects(1, 0, 1, 250, 255)
The same code as above plus the
MsgWaitForMultipleObjects function call. The function is a standard part of the Win32 API. Here is the function's prototype taken from the MSDN documentation (comments mine):
DWORD nCount, const HANDLE* pHandles, BOOL bWaitAll, DWORD dwMilliseconds, DWORD dwWakeMask );
The purpose of the function is to suspend the calling thread until one or all of the object handles become signaled, OR, until a message appears in the thread's input queue according to the
The weird thing is that .NET framework calls the function with the number of object handles equal to ONE, while the pointer to the array of handles is ZERO (
Nothing in VB nomenclature).
This way of calling the function is not documented (AFAIK). One can only guess that such a call is used to suspend the calling thread until a message has to be processed without checking the signaled state of any object handle.
Nevertheless, because the call is used within WinForms implementation itself, I find it quite safe to use it within your own applications.
I've used it with
ColorPicker and the problem with the drop-down form consuming 100% CPU cycles disappeared.
Using the code
You can use the provided
ColorPicker control in binary form by referencing the assembly LaMarvin.Windows.Forms.ColorPicker.dll in your project (This will work for any .NET language, not just VB.NET). Or, you can include just the ColorPicker.vb source code file in your VB.NET project.
When you download the zipped solution, be sure to:
- Extract the files with the WinZip's "Use Folder Names" option checked.
- Rebuild the solution the first time you open it in VS.NET (otherwise, the
ColorPicker's reference in the demo application might not be resolved correctly).
- October 26, 2003