Using the UITypeEditor Class

Using the UITypeEditor Class

If you have started wondering how to display list boxes, NumericUpDown controls, little boxes that contain colors, or dialog boxes for editing properties in the IDE, you have come to the right place. By creating a custom UITypeEditor instance you can add enhanced property editing to your custom control. To demonstrate I will show you how to display a list of states to choose from ”a custom type editor ”and how to associate that type editor and provide a default value for the State combobox in the AddressUserControl .

To implement a drop-down list of states in the AddressUserControl 's State combobox, we need to define a custom type editor and provide an overridden implementation for the methods EditValue and GetEditStyle . EditValue is invoked when the consumer attempts to modify the property in the IDE at design time, and GetEditStyle returns an enumerated UIEditorEditStyle value. Listing 9.11 contains the code for the custom type editor, dubbed StateEditor , followed by a synopsis.

Listing 9.11 A Custom Type Editor for Editing State Abbreviations in the Properties Window
 1:  Imports System.Drawing.Design 2:  Imports System.Windows.Forms 3:  Imports System.Windows.Forms.Design 4:  Imports System.ComponentModel 5: 6:  Public Class StateEditor 7:    Inherits UITypeEditor 8: 9:    Private editorService As _ 10:     IWindowsFormsEditorService = Nothing 11: 12:   Private States() As String = _ 13:     New String() {"AL", "AR", "AK", "AZ", "CA", _ 14:       "DE", "FL", "GA", "HI", "IN", "MI", "OR", "PA"} 15: 16:   Private Function IsValidContext( _ 17:     ByVal context As ITypeDescriptorContext) As Boolean 18: 19:     Return (context Is Nothing = False) _ 20:       And (context.Instance Is Nothing) = False 21: 22:   End Function 23: 24:   Private Function IsValidProvider( _ 25:     ByVal provider As IServiceProvider) As Boolean 26: 27:     Return provider Is Nothing = False 28:   End Function 29: 30:   Private Function GetService( _ 31:     ByVal provider As IServiceProvider) _ 32:     As IWindowsFormsEditorService 33: 34:     Return CType(provider.GetService( _ 35:       GetType(IWindowsFormsEditorService)), _ 36:       IWindowsFormsEditorService) 37: 38:   End Function 39: 40:   Public Overloads Overrides Function EditValue( _ 41:     ByVal context As ITypeDescriptorContext, _ 42:     ByVal provider As IServiceProvider, _ 43:     ByVal value As Object) As Object 44: 45:     If (IsValidContext(context) = False Or _ 46:       IsValidProvider(provider)) = False Then 47:       Return value 48:     End If 49: 50: 51:     editorService = GetService(provider) 52:     If (editorService Is Nothing) Then Return value 53: 54:     Dim control As ListBox = New ListBox() 55:     control.Items.AddRange(States) 56:     AddHandler control.SelectedValueChanged, _ 57:       AddressOf SelectedValueChanged 58: 59:     editorService.DropDownControl(control) 60:     If Not (control.Text = String.Empty) Then 61:       Return control.Text 62:     Else 63:       Dim Attribute As Attribute = _ 64:         context.PropertyDescriptor.Attributes( _ 65:           GetType(DefaultValueAttribute)) 66: 67:       If (Attribute Is Nothing = False) Then 68:         Return CType(Attribute, DefaultValueAttribute).Value 69:       Else 70:         Return value 71:       End If 72:     End If 73:   End Function 74: 75:   Public Overloads Overrides Function GetEditStyle( _ 76:     ByVal context As ITypeDescriptorContext) _ 77:       As UITypeEditorEditStyle 78: 79:     If (IsValidContext(context)) Then 80:       Return UITypeEditorEditStyle.DropDown 81:     Else 82:       Return MyBase.GetEditStyle(context) 83:     End If 84: 85:   End Function 86: 87:   Private Sub SelectedValueChanged(ByVal sender As Object, _ 88:     ByVal e As EventArgs) 89: 90:     If (editorService Is Nothing) Then Return 91:     editorService.CloseDropDown() 92:   End Sub 93: 94: End Class 

Lines 1 through 4 contain the namespaces used by the custom type editor. System.Drawing.Design contains the base class UITypeEditor . System.Windows.Forms contains the definition for the list box used as the control for the type editor beginning on line 54. System.Windows.Forms.Design contains the definition for the IWindowsFormsEditorService interface declared in lines 9 and 10. System.ComponentModel contains the definition for the DefaultValueAttribute and the ITypeDescriptorContext interface.

Line 7 is understood to mean that StateEditor is-a UITypeEditor , expressed as an inheritance relationship using the Inherits keyword in Visual Basic .NET.

The private methods are "worker bee" methods. IsValidContext , IsValidProvider , and GetService are all used to help simplify the EditValue and GetEditStyle methods. For example, IsValidContext is used to make sure that the context ”that is, the component whose properties we are editing ”is valid. The ITypeDescriptorContext interface refers to the component and the property we will be modifying. IsValidProvider is used to test whether IServiceProvider is valid. Our service provider is IWindowsFormsEditorService , which provides an interface for displaying DropDownList or dialog controls. We are using the DropDownList support provided by classes that implement IWindowsFormsEditorService . Finally, GetService is used to cast the generic IServiceProvider to an IWindowsFormsServiceProvider , which is the actual interface we know IServiceProvider to be returning in the context of UITypeEditor .

Lines 9 and 10 declare a local field that we use to store a reference to IServiceProvider , rather than continually requesting and typecasting it.

Lines 12 through 14 contain a partial list of U.S. state abbreviations. (Note that we can always go back and verify the data after we get the code working correctly.)

Lines 75 through 85 simply indicate the kind of UITypeEditorEditStyle this type editor will be displaying. If we have a valid context, we want to return a UITypeEditorEditStyle.DropDown ; otherwise we defer to the inherited method (line 82).

The most challenging method to implement correctly is the EditValue method (see lines 40 through 73). The purpose of EditValue is to display the control used to edit the property value and retrieve the edited value. We are using a ListBox control, so we need to be able to both display and close it. Lines 45 through 48 ensure that the context is correct. If not, the passed-in value is returned. Lines 51 and 52 make sure that IServiceProvider exists; again, if not, the passed-in value is returned. Assuming we have a valid context (the component) and a valid provider (the IDE), we can proceed. Lines 54 through 57 declare and initialize a ListBox control. In line 55 we add the list of states to the ListBox control. In lines 56 and 57 we assign an event handler to the SelectedValueChanged event. Defined in lines 87 through 92, SelectedValueChanged is used to close the ListBox control after the user selects a value. IServiceProvider is used to display the control used to edit the property. If the control ”the ListBox in our example ”contains a value for its Text property in lines 60 and 61, the selected value is returned to the Properties window and consequently the related States combobox. If the user escaped the program or failed to select a state, Reflection is used to get the default value (indicated by a DefaultValueAttribute that we will supply) from the property itself.

Lines 63 through 65 request the DefaultValueAttribute from the context's property descriptor. If a DefaultAttribute is applied to the property ”in this case the State property ”that default value is returned (see lines 67 and 68).

The work left to do is to associate our type editor with the member we want to edit in the IDE.

Associating a Type Editor with a Control

We defined StateEditor as a UITypeEditor instance for editing the value of our AddressUserControl 's State property. We associate StateEditor with the State property by using the EditorAttribute . Listing 9.12 demonstrates an application of the EditorAttribute in conjunction with the DefaultValueAttribute and CategoryAttribute , which I describe in the remaining two subsections.

Listing 9.12 Associating a Type Editor with the Member to Edit
 1:    <Editor(GetType(StateEditor), GetType(UITypeEditor)), _ 2:    DefaultValue("CA"), _ 3:    Category("Address Fields"), _ 4:    Description("Partial list of U.S. state abbreviations.")> _ 5:    Public Property State() As String 6:    Get 7:      Return ComboBoxState.Text 8:    End Get 9:    Set(ByVal Value As String) 10:     ComboBoxState.Text = Value 11:   End Set 12:   End Property 

The EditorAttribute needs the type of our custom editor and the type of the base editor. We satisfy this requirement in line 1 by passing the type object of StateEditor (our custom type editor) and the base editor (the UITypeEditor instance itself) as arguments to the EditorAttribute . (We talked about the DescriptionAttribute earlier in this chapter, so we'll skip that attribute in this discussion.)

Supplying a Default Value

The DefaultValueAttribute can be used to associate a default value with a property. In our case we know that StateEditor is used with a specific field ”the State field ”but we have no way to know what future consumers may do with StateEditor . As a result it is safe to dynamically determine the default value by using the ITypeDescriptorContext interface's PropertyDescriptor and Reflection rather than supposing that there is some predetermined default value. (Keep in mind that in real life the producer of StateEditor may not be the same or only consumer.) Line 2 of Listing 9.12 above demonstrates how to use the DefaultValueAttribute .

Categorizing Control Properties

Properties in the Properties window can be ordered alphabetically or categorically. Suppose a consumer elects to modify all the properties we added representing parts of the address. By providing a CategoryAttribute (see line 3 of Listing 9.12 above) we can provide a means by which consumers can quickly access the properties they are most interested in. Figure 9.11 shows the result of using the CategoryAttribute in the Properties window.

Figure 9.11. Categorical organization of properties in a custom control.


Clearly there are other kinds of editors you may want to use in the Properties window. However, if we are ever to finish this chapter I have to leave some things for you to explore on your own. For example, if you want to display an icon or some other custom- painted graphic in the Properties window, you will need to define a UITypeEditor instance that overrides GetPaintValueSupport and PaintValue . PaintValue receives a Graphics object that represents the region of the editor in the Properties window. You can use this Graphics object to paint boxes with colors, gradient effects, or whatever you can squeeze into the small space provided. If you need to display a complex editor, such as one that requires a dialog form, you can use code similar to that found in Listing 9.11. The difference is that UITypeEditorEditStyle will become UITypeEditorStyle.Modal , and you will need to call the IWindowsFormsEditorService.ShowDialog method, converting the results in the dialog form into a value that is suitable for the property being edited. (My book Advanced C# Programming [2002] has an example of a dialog UITypeEditor in C#. You can download the source code for the dialog UITypeEditor from for free or, better yet, pick up a copy of the book.)

Visual Basic. NET Power Coding
Visual Basic(R) .NET Power Coding
ISBN: 0672324075
EAN: 2147483647
Year: 2005
Pages: 215
Authors: Paul Kimmel © 2008-2017.
If you may any questions please contact us: