|Provide design time support for a custom control in Visual Basic .NET
|This example shows how to provide design time support for a custom control in Visual Basic .NET.
|custom control, design time, support, icon, smart tag, properties, dropdown, editor, UITypeEditor, VB.NET
|ActiveX, VB.NET, Software Engineering
This is a huge example and unfortunately I don't have time to do a thorough job of documenting it right now. I'll give some hints but you'll probably need to do some exploration in the code to uncover all of the details.
This example provides design time support for a custom control. ScribbleControl displays a picture drawn freehand with the mouse. It has BackColor, LineColor, and LineWidth properties. LineColor is an enumerated type allowing only nine colors.
The PolyPolyline property is an array of arrays of points. Each array of points represents a series of lines drawn for the picture.
At design time, it represents the LineColor and LineWidth properties graphically in the Properties window. Those properties provide dropdowns to let you graphically select colors and line widths.
The PolyPolyline property displays a tiny image of the drawing in the Properties window. If you click the ellipsis to the right, a modal dialog appears where you can scribble a new drawing.
Below the Properties window, the control provides the verb Draw Spiral. It looks like a link. If you click this, the control draws a spiral on itself.
When you place a ScribbleControl on a form, the control displays a smart tag. It looks like a tiny box with a little arrow in it. If you click this, a smart tag dialog appears. This dialog contains a standard color selection box for BackColor, dropdown controls for LineColor and LineWidth, two commands Draw Star and Draw Spiral, and some text telling how many polylines the drawing contains.
Finally, if you right-click the control and select Properties, a property sheet dialog appears. The dialog has two pages. The first page lets you set basic properties such as BackColor, LineWidth, and LineColor. The second page lets you draw a picture. The dialog has OK, Cancel, and Apply buttons that work as they do in standard property sheet dialogs.
Here are the main ideas behind this example.
Displaying and Editing Properties
To display a property in a special format and to allow custom editing in the Properties window, you need to associate the property with a property editor class. For example, the PolyPolyline property is declared like this:
Public Property PolyPolyline() As PolyPolyline
The PolyPolylineEditor class inherits from UITypeEditor. It overrides the GetEditStyle method to indicate the style of editing it provides. In this example, the PolyPolylineEditor provides a modal dialog so this function returns UITypeEditorEditStyle.Modal.
The EditValue subroutine edits a PolyPolyline value. It displays a dialog and, if the user clicks OK, it sets the value to the new value the user drew on the dialog.
To display a value in a special format on the Properties window, the program overrides the GetPaintValueSupported method to return True, indicating that the editor can display a value. The PaintValue subroutine actually draws the value. In this case, it draws a tiny image of the control's drawing.
The LineWidthEditor class works similarly but is a bit simpler (actually it's a better place to start exploring because it is simpler). Its GetEditStyle function returns UITypeEditorEditStyle.DropDown to indicate that it lets you edit the LineWidth property by using a dropdown in the Properies window. When you click the dropdown arrow to the right of this property, the editor displays a LineWidthListBox control. This control is a ListBox that displays a series of images of lines with different widths.
To display a property value, the LineWidthEditor draws a line of the appropriate width in the Properties window.
The ColorIndexEditor is vey similar to LineWidthEditor except it displays samples of colors rather than samples of lines.
To display a smart tag, you need to associate the control with a designer. This example uses the following attribute on the ScribbleControl class's Class statement to associate the control with the ScribbleControlDesigner class.
The designer class inherits from ControlDesigner and overrides the ActionLists method to return a DesignerActionListCollection that contains a list of classes that represent the actions that the smart tag should contain. This example uses the single action list class named ScribbleActionList.
The ScribbleActionList class provides public properties and methods that handle the smart tag's items. For example, it has a LineColor property to let the smart tag modify the ScribbleControl's LineColor property. It also has methods DrawStar and DrawSpiral.
The class uses EditorAttribute attributes to associate its LineColor and LineWidth properties with the same editor classes used in the Properties window so the smart tag can display the custom dropdowns for those properties.
The GetSortedActionItems function returns a DesignerActionItemCollection to tell the smart tag what items to display and the order in which it should display them. It lists the properties and the DrawStar and DrawSpiral routines. The list gives each item's name, the name the smart tag should display, and the item's category. This example uses the categories Appearance and Information.
The list entries for the DrawStar and DrawSpiral routines also include a Boolean indicating whether the routine should also be used as a command verb as well as appearing in the smart tag. If this value is True, the routine appears as a link below the Properties window. This example makes DrawSpiral a verb but does not make DrawStar a verb so both appear in the smart tag but only DrawSpiral appears as a link below the Properties window.
To display property sheets, you need to associate the control class with an editor class. This example adds the EditorAttribute attribute to the ScribbleControl's class declaration to associate it with the ScribbleControlEditor class as shown in the following code.
ScribbleControlEditor inherits from WindowsFormsComponentEditor. Its GetComponentEditorPages method returns an array of types giving the classes used for the property pages. This example uses two property page classes: ScribbleControlEditorBasicsPage and ScribbleControlEditorPolyPolylinePage.
The editor's GetInitialComponentEditorPageIndex method returns the index of the property page that should initially be selected, in this example 0.
The property page classes inherit from ComponentEditorPage. Their constructors size the pages and add controls to them in code. (Hint: build the page as a Windows Form and then copy and paste the designer-generated code that builds the controls into the property page class's constructor.) The property pages use the same controls as before to allow the user to pick BackColor, LineColor, and LineColor.
The LoadComponent method initializes the page's controls for the ScribbleControl that it is editing. For example, it makes the ColorIndexListBox control display the ScribbleControl's LineColor.
The SaveComponent method does the revese: it saves the values from the controls into the selected ScribbleControl.
Finally, whenever the user modifies a value on a property page the page class calls its SetDirty method. This tells the property page dialog that the data has been modified so the dialog enables its Apply button. If the user clicks this button or clicks OK whilc there have been changes, the dialog automatically calls the page's SaveComponent method.
Even though I glossed over a lot of details, this is still a lot of information. Take a look at the code to see how it all fits together. You should probably focus on one concept at a time: display in the Properties window, editing in the Properties window, smart tags, and property pages.
One final hint: The editors seem to get cached by the IDE. Often when you change an editor and rebuild, the IDE still uses the old editor. To fix this, close the IDE, delete the bin and obj directories, re-open Visual Basic, and rebuild. This is easier if you don't have any instances of the control on a form because Visual Basic will be unable to reload its properties until after you rebuild the control.