Create a Custom UI Value Editor
Leverage inheritance to add developer-friendly features to a popup button control.
by Bill Storage

August 2003 Issue

Technology Toolbox: VB.NET

In last month's Desktop Developer column, I showed you a simple approach to building a popup button control with inheritance and GDI+, and emphasized its ease-of-use features for other developers who use the control. Now, you'll use the same control to advance this theme another step—by adding a UI value editor to last month's code. You'll tap the power of inheritance in .NET again, without using any Windows API or black-belt techniques.

The ImageButton control I showed you how to design last month uses three different images, obtained from an associated .NET ImageList control. You gave the ImageButton a design-time–only CurrentImageIndex to allow control users to cycle through the three images at design time. This feature is necessary because ImageIndex properties have integer values—the ordinal position of the related image. The integer value isn't particularly friendly at design time if the ImageList includes many images; a visual representation of the selected value would be better. So, I'll show you how to change the design-time display of the Up, Down, and HoverImageIndex properties to show both the ordinal value and the actual ImageList image at that position in the Properties browser ( download the sample code here).

This task involves informing VS.NET that your ImageIndex properties supply their own design-time editor, which appears in the Properties browser. In pre-.NET days, this required implementing IPerPropertyBrowsing, which was essentially impossible in VB. The technique you'll use requires no tricks, API calls, or anything of the sort.

The first task is to code a means of showing all the images from an ImageList in a vertical strip when the user selects ImageButton's Up, Down, or HoverImageIndex in the Properties browser. Do this by building a quick ImageBar control (see Figure 1). It doesn't need a slick programming interface, because you're the only person who ever uses the ImageBar directly. ImageBar simply draws an array of vertically arranged images, along with a number indicating each one's position. The drawing technique—a few lines of GDI+ in the OnPaint procedure—is similar to the one you used in the ImageButton itself, without the concern for mouse-button state (see Listing 1). ImageBar has a single event—IndexChanged. You must supply this event for the UI value editor, so that the Properties dropdown list closes after the user makes an ImageIndex selection.

You must make one small change to the ImageButton control code. You need to add an <Editor> attribute to the Up, Down, and HoverImageIndex properties to tell .NET that these properties use a custom editor. This causes a dropdown to appear for these properties in the Properties browser. The Editor constructor takes two arguments: your custom editor's type, and the .NET editor type from which your editor class derives—UITypeEditor, in this case:

<Editor(GetType(ImageButtonEditor), _
   GetType(UITypeEditor))

The inheritance introduction/refresher in last month's column will serve you here too. Your editor must override two methods of the UITypeEditor base class from which your editor derives—GetEditStyle and EditValue—in order to cooperate with the Properties browser (see Listing 2). This code isn't particularly difficult, but you probably haven't seen anything like it yet. The GetEditStyle and EditValue procedures you write for this project can serve as a template for all future UI value editors you build.

Inherit From UITypeEditor
Start with a new class that inherits from UITypeEditor and implements IDisposable. You need project references to the .NET ComponentModel and Drawing libraries for this. The GetEditStyle override uses three parameters, two of which require some explanation. The first—context—is of type ITypeDescriptorContext. At design time, the context value holds information about the instance of the control being edited and its container. Surprisingly, it also contains a PropertyDescriptor—a complete set of descriptive details about the property of the control that's being edited. This is important for the ImageButton, because three of its properties use the same UIEditor, and the editor must know which property it should update. The .NET Platform SDK instructs you not to depend on the existence of the context object or context.Instance when EditValue is called, so your EditValue code must check for null values.

EditValue's second parameter—provider—is of type IServiceProvider, and in this case is a service provider that implements IWindowsFormsEditorService. IServiceProvider has a single method—GetService—that returns the service being provided. For VS.NET design time, this means the particular property line a user has selected in the Properties browser. Again, you must ensure that the GetService return value is valid before you proceed.

If all these checks succeed, then you can be sure EditValue's third parameter—value—contains the current value of the property being edited. Use this to set the initial value-editor displays. If your editor applies to multiple properties, as with ImageButton, get the current property from context.PropertyDescriptor.Name. Store the return value of GetService in a class-level variable for when you need to close the Properties browser dropdown later. EditValue can use it directly to show the dropdown by calling IWindowsFormsEditorService.DropDownControl.

EditValue needs to complete one more task. It must provide for the editor to close the Properties browser dropdown after the user picks an Up, Down, or HoverImageIndex value. To do this, the editor needs to know when the ImageBar control's ImageIndex values change. Note this applies to the ImageBar displaying an ImageButton's property value, not the ImageButton itself. Get this notification by listening to the ImageBar's IndexChanged event—the event you wrote for the tiny ImageBar control, solely for this purpose. Subscribe to the ImageBar's event by providing a callback function—ValueChanged—and by using AddHandler:

AddHandler _ImageBar.IndexChanged, _
   AddressOf Me.ValueChanged

Overriding the GetEditStyle method is much easier. You merely tell the editing context what to do when a user wants to edit a property. If the context and instance are valid objects, simply return UITypeEditorEditStyle.DropDown. Modal is also a valid style if you need to show a complete separate editing window instead of a dropdown, unlike the case with ImageButton properties.

The rest of the ImageButtonEditor class consists of two support functions. The private ValueChanged procedure listens to the ImageBar's IndexChanged event and closes the Properties browser dropdown. Clean up your editor by unhooking the ValueChanged event listener by implementing IDisposable.Dispose and calling RemoveHandler in it.

The entire ImageButtonEditor class contains about 40 lines of executable code, and compiling it, the ImageBar, and your original ImageButton class yields a DLL weighing in at a whopping 12K—not bad, given the level of sophistication you're providing. I'll show you in a future article how to build a similar ASP.NET image-button control with both server- and client-side behavior.

About the Author
Bill Storage is the president of Nerve Net Inc., the founding author of VBPJ's Black Belt Programming column, and a member of the Visual Studio Magazine advisory board. Reach him at .