UI Type Editors


ExpandableObjectConverters help break down a complex multivalue property into a nested list of its atomic values. Although this technique simplifies editing of a complicated property, it may not be suitable for other properties that exhibit the following behavior:

  • Hard to construct, interpret, or validate, such as a regular expression

  • One of a list of values so large it would be difficult to remember all of them

  • A visual property, such as a ForeColor, that is not easily represented as a string

Actually, the ForeColor property satisfies all three points. First, it would be hard to find the color you wanted by typing comma-separated integers like 33, 86, 24 or guessing a named color , like PapayaWhip. Second, there are a lot of colors to choose from. Finally, colors are just plain visual.

In addition to supporting in-place editing in the Property Browser, properties such as ForeColor help the developer by providing an alternative UI-based property-editing mechanism. You access this tool, shown in Figure 9.27, from a drop-down arrow in the Property Browser.

Figure 9.27. Color Property Drop-Down UI Editor

The result is a prettier, more intuitive way to select a property value. This style of visual editing is supported by the UI type editor , a design-time feature that you can leverage to similar effect. There are two types of "editor" you can choose from: modal or drop-down. Drop-down editors support single-click property selection from a drop-down UI attached to the Property Browser. This UI might be a nice way to enhance the clock control's Face property, allowing developers to visualize the clock face style as they make their selection, shown in Figure 9.28.

Figure 9.28. Custom View Drop-Down UI Editor

You begin implementing a custom UI editor by deriving from the UITypeEditor class (from the System.Drawing.Design namespace):

 
 Public Class FaceEditor   Inherits UITypeEditor   ... End Class 

The next step requires you to override the GetEditStyle and EditValue methods from the UITypeEditor base class:

 
 Public Class FaceEditor   Inherits UITypeEditor   Public Overrides Function GetEditStyle( _       context As ITypeDescriptorContext) As UITypeEditorEditStyle       ...   End Function   Public Overrides Function EditValue( _       context As ITypeDescriptorContext, _         provider As IServiceProvider, _           value As Object) As Object       ...   End Function End Class 

As with type converters, the appropriate UI type editor, provided by the GetEditor method of the TypeDescription class, is stored with each property. When the Property Browser updates itself to reflect a control selection in the Designer, it queries GetEditStyle to determine whether it should show a drop-down button, an open dialog button, or nothing in the property value box when the property is selected. This behavior is determined by a value from the UITypeEditorEditStyle enumeration:

 
 Enum UITypeEditorEditStyle   DropDown ' Display drop-down UI   Modal ' Display modal dialog UI   None ' Don't display a UI End Enum 

Not overriding GetEditStyle is the same as returning UITypeEditorEditStyle.None, which is the default edit style. To show the drop-down UI editor, the clock control returns UITypeEditorEditStyle.DropDown:

 
 Public Class FaceEditor   Inherits UITypeEditor   Public Overrides Function GetEditStyle( _       context As ITypeDescriptorContext) As UITypeEditorEditStyle       If Not (context Is Nothing) Then _         Return UITypeEditorEditStyle.DropDown       Return MyBase.GetEditStyle(context)   End Function   ... End Class 

ITypeDescriptorContext is passed to GetEditStyle to provide contextual information regarding the execution of this method, including the following:

  • The container and, subsequently, the designer host and its components

  • The component design-time instance being shown in the Property Browser

  • A PropertyDescriptor type describing the property, including the TypeConverter and UITypeEditor assigned to the component

  • A PropertyDescriptorGridEntry type, which is a composite of the PropertyDescriptor and the property's associated grid entry in the Property Browser

Whereas GetEditStyle is used to initialize the way the property behaves, EditValue actually implements the defined behavior. Whether the UI editor is drop-down or modal, you follow the same basic steps to edit the value:

  1. Access the Property Browser's UI display service, IWindowsForms-EditorService.

  2. Create an instance of the editor UI implementation, which is a control that the Property Browser will display.

  3. Pass the current property value to the UI editor control.

  4. Ask the Property Browser to display the UI editor control.

  5. Choose the value and close the UI editor control.

  6. Return the new property value from the editor.

Drop-Down UI Type Editors

Here's how the clock control implements these steps to show a drop-down editor for the Face property:

 
 Public Class FaceEditor   Inherites UITypeEditor   ...   Public Overloads Overrides Function EditValue( _       context As ITypeDescriptorContext, provider As IServiceProvider, _       value As Object) As Object       If Not(context Is Nothing) And Not(provider Is Nothing) Then           ' Access the property browser's UI display service           Dim editorService As IWindowsFormsEditorService = _ CType(provider.GetService(GetType(IWindowsFormsEditorService)), _                   IWindowsFormsEditorService)           If Not(editorService Is Nothing) Then               ' Create an instance of the UI editor control               Dim dropDownEditor As FaceEditorControl = _                   New FaceEditorControl(editorService)               ' Pass the UI editor control the current property value               dropDownEditor.Face = CType(value, ClockFace)               ' Display the UI editor control               editorService.DropDownControl(dropDownEditor)               ' Return the new property value from the UI editor control               Return dropDownEditor.Face           End If       End If       Return MyBase.EditValue(context, provider, value)   End Function End Class 

When it comes to displaying the UI editor control, you must play nicely in the design-time environment, particularly regarding UI positioning in relation to the Property Browser. Specifically, drop-down UI editors must appear flush against the bottom of the property entry and must be sized to the width of the property entry.

To facilitate this, the Property Browser exposes a service, an implementation of the IWindowsFormsEditorService interface, to manage the loading and unloading of UI editor controls as well as their positioning inside the development environment. The FaceEditor type references this service and calls its DropDownControl method to display the FaceEditorControl, relative to Property's Browser edit box. When displayed, FaceEditorControl has the responsibility of capturing the user selection and returning control to EditValue with the new value. This requires a call to IWindowsFormsEditorService.CloseDropDown from FaceEditorControl, something you do by passing to FaceEditorControl a reference to the IWindowsForms- EditorService interface:

 
 Public Class FaceEditorControl   Inherits UserControl   Dim _face As ClockFace = ClockFace.Both   Dim editorService As IWindowsFormsEditorService = Nothing   ...   Public Sub New(editorService As IWindowsFormsEditorService)       ...       Me.editorService = editorService   End Sub   Public Property Face As ClockFace       ...   End Property   Sub picBoth_Click(sender As Object, e As System.EventArgs)       _face = ClockFace.Both       ' Close the UI editor control upon value selection       editorService.CloseDropDown()   End Sub   Sub picAnalog_Click(sender As Object, e As System.EventArgs)       _face = ClockFace.Analog       ' Close the UI editor control upon value selection       editorService.CloseDropDown()   End Sub   Sub picDigital_Click(sender As Object, e As System.EventArgs)       _face = ClockFace.Digital       ' Close the UI editor control upon value selection       editorService.CloseDropDown()   End Sub   ... End Class 

The final step is to associate FaceEditor with the Face property by adorning the property with EditorAttribute:

 
 <Category("Appearance"), Description("Which style of clock face?"), _   DefaultValue(ClockFace.Both), _   Editor(GetType(FaceEditor), GetType(UITypeEditor))> _ Public Property Face() As ClockFace 

Now FaceEditor is in place for the Face property. When a developer edits that property in Propery Browser, it will show a drop-down arrow and the FaceEditorControl as the UI for the developer to use to choose a value of the ClockFace enumeration.

Modal UI Type Editors

Although drop-down editors are suitable for single-click selection, there are times when unrestricted editing is required. In such situations, you would use a modal UITypeEditor implemented as a modal form. For example, the clock control has a digital time format sufficiently complex to edit with a separate dialog outside the Property Browser:

 
 Public Class ClockControl   Inherits Control   ...   Dim myDigitalTimeFormat As String = "dd/MM/yyyy hh:mm:ss tt"   ...   <Category("Appearance"), Description("The digital time format, ..."), _   DefaultValue("dd/MM/yyyy hh:mm:ss tt")> _   Public Property DigitalTimeFormat() As String       Get           Return myDigitalTimeFormat       End Get       Set           myDigitalTimeFormat = value           Me.Invalidate()       End Set   End Property End Class 

Date and Time format strings are composed of a complex array of format specifiers that are not easy to remember and certainly aren't intuitive in a property browser, as shown in Figure 9.29.

Figure 9.29. The DigitalTimeFormat Property

Modal UITypeEditors are an ideal way to provide a more intuitive way to construct hard-to-format property values. By providing a custom form, you give developers whatever editing experience is the most conducive for that property type. Figure 9.30 illustrates how the Digital Time Format Editor dialog makes it easier to edit the clock control's DigitTimeFormat property.

Figure 9.30. Custom DigitalTimeFormat Modal UI Editor

A modal UITypeEditor actually requires slightly different code from that of its drop-down counterpart . You follow the same logical steps as with a drop-down editor, with three minor implementation differences:

  • Returning UITypeEditorEditStyle.Modal from UITypeEditor.GetEditStyle

  • Calling IWindowsFormsEditorService.ShowDialog from EditValue to open the UI editor dialog

  • Not requiring an editor service reference to be passed to the dialog, because a Windows Form can close itself

The clock control's modal UITypeEditor is shown here:

 
 Public Class DigitalTimeFormatEditor   Inherits UITypeEditor   Public Overrides Function GetEditStyle( _       context As ITypeDescriptorContext) As UITypeEditorEditStyle       If Not(context Is Nothing) Then           Return UITypeEditorEditStyle.Modal       End If       Return MyBase.GetEditStyle(context)   End Function   Public Overrides Function EditValue( _       context As ITypeDescriptorContext, _         provider As IServiceProvider, _       value As Object) As Object       If Not(context Is Nothing) And Not(provider Is Nothing) Then           ' Access the property browser's UI display service           Dim editorService As IWindowsFormsEditorService = _             CType(provider.GetService(GetType(IWindowsFormsEditorService)), _               IWindowsFormsEditorService)           If editorService <> Nothing Then               ' Create an instance of the UI editor dialog               Dim modalEditor As DigitalTimeFormatEditorForm = _                   New DigitalTimeFormatEditorForm()               ' Pass the UI editor dialog the current property value               modalEditor.DigitalTimeFormat = CStr(value)               ' Display the UI editor dialog               If editorService.ShowDialog(modalEditor) = _                 DialogResult.OK Then                   ' Return the new property value from the UI editor dialog                   Return modalEditor.DigitalTimeFormat               End If           End If       End If       Return MyBase.EditValue(context, provider, value)   End Function End Class 

At this point, normal dialog activities (as covered in Chapter 3: Dialogs) apply for the UI editor's modal form:

 
 Public Class DigitalTimeFormatEditorForm   Inherits Form   ...   Dim myDigitalTimeFormat As String = "dd/MM/yyyy hh:mm:ss tt"   Public Property DigitalTimeFormat() As String       Get           Return myDigitalTimeFormat       End Get       Set           myDigitalTimeFormat = value       End Set   End Property   ...   Sub btnOK_Click(sender As Object, e As System.EventArgs)       DialogResult = DialogResult.OK       myDigitalTimeFormat = txtFormat.Text   End Sub   ... End Class 

Again, to associate the new UITypeEditor with the property requires applying the EditorAttribute:

 
 <Category("Appearance"), Description("The digital time format, ..."), _   DefaultValue("dd/MM/yyyy hh:mm:ss tt"), _   Editor(GetType(DigitalTimeFormatEditor), GetType(UITypeEditor))> _ Public Property DigitalTimeFormat() As String 

After EditorAttribute is applied, the modal UITypeEditor is accessed via an ellipsis-style button displayed in the Property Browser, as shown in Figure 9.31.

Figure 9.31. Accessing a Modal UITypeEditor

UITypeEditors allow you to provide a customized editing environment for the developer on a per-property basis, whether it's a drop-down UI to select from a list of possible values or a modal dialog to provide an entire editing environment outside the Property Browser.



Windows Forms Programming in Visual Basic .NET
Windows Forms Programming in Visual Basic .NET
ISBN: 0321125193
EAN: 2147483647
Year: 2003
Pages: 139

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net