The core requirement for the ColorPicker
control was to display the same drop-down color selector that is used within the WinForms' PropertyGrid
control. 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 Color
structure:
Namespace System.Drawing
<EditorAttribute("System.Drawing.Design.ColorEditor"), _
GetType(System.Drawing.Design.UITypeEditor))> _
Public Structure Color
Unfortunately, the 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 the 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 editors are supported - the ones that derive (directly or indirectly) from the System.Drawing.Design.UITypeEditor
class.) 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 ColorEditor.GetEditStyle
method:
Namespace System.Drawing.Design
Public Class UITypeEditor
Public Overridable Function GetEditStyle( _
ByVal context As ITypeDescriptorContext _
) As UITypeEditorEditStyle
The 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 - ColorEditor.EditValue
:
Namespace System.Drawing.Design
Public Class UITypeEditor
Public Overridable Function EditValue( _
ByVal context As ITypeDescriptorContext, _
ByVal provider As IServiceProvider, _
ByVal value As Object _
) As Object
The ColorEditor
implementation of this method does the following:
- Queries the passed in
IServiceProvider
instance for anIWindowsFormEditorService
implementation. - Stores the
IWindowsFormEditorService
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.
ColorEditor
doesn't use the
ITypeDescriptorContext
arguments, all I had to do to host it was to implement just two interfaces:
Namespace System Public Interface IServiceProvider Public Function GetService( _ ByVal serviceType As Type) As Object End Interface ... Namespace System.Windows.Forms.Design Public Interface IWindowsFormsEditorService Public Sub CloseDropDown() Public Sub DropDownControl(ByVal control As Control) Public Function ShowDialog( _ ByVal dialog As Form) As DialogResult End Interface
Because the 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.
Comments