Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / Visual Basic
Article

Intercepting ENTER and ESC keys in custom, drop-down UITypeEditors.

Rate me:
Please Sign up or sign in to vote.
4.11/5 (9 votes)
8 Mar 2004CPOL6 min read 69.1K   288   25   5
This article and the accompanying source code shows you how to intercept the ENTER and ESC keys in your own UITypeEditor implementation.

The dropped-down ResourceImageEditor.

(The article assumes that you're familiar with Visual Basic .NET and the Visual Studio .NET Windows Forms designer.)

Introduction

When developing Windows Forms controls, it is often useful to provide your own, drop-down type editors for some of the control's properties. Custom type editors provide for richer design-time experience and they might be the deciding factor whether your users like your controls or not.

If you decide to create your own drop-down type editor, it should, presumably, follow the same behavioral pattern exposed by the built-in drop-down editors. Let's take the Anchor property as an example. A typical design-time user interaction with the property is as follows:

  • The user selects the Anchor property in the property grid and clicks the down-arrow button to the right of the property's cell.
  • A nice graphical control is dropped down allowing the user to either click the edges by the mouse or use the arrow keys to highlight an edge and the SPACEBAR key to select / deselect it.
  • The user presses the ENTER key or clicks outside of the drop-down control for accepting the changes. In order to cancel the changes, the user presses the ESC key.

Pretty intuitive!

So what does it take to replicate the above-mentioned behavior?

The ResourceImageEditor type editor

In order to explore the implementation choices, let's build a ResourceImageEditor type editor that allows for picking an image file from the file system (just like the built-in ImageEditor class) or picking an image resource from an assembly's manifest. With regards to user experience, the ResourceImageEditor should behave like the built-in type editors. Here are our requirements in a nutshell:

  1. When the user selects a property in the property grid, the grid should display a down-arrow button indicating that the property will be edited with a drop-down UI.
  2. When the down-arrow button is clicked, a list of all image resources from the current assembly should be displayed.
  3. When the user selects an image resource item, the image will be loaded from the assembly.
  4. To allow for selecting an image file, the last item in the drop-down list will be labeled as "Browse...". When the user clicks the "Browse..." item, the "classic" open file dialog will be displayed and the user will be able to pick an image file from the file system.
  5. The drop-down list will allow the user to select an item by single-clicking it with the mouse, or by using the arrow keys to highlight an item and pressing the ENTER key to actually select it. The drop-down selection will also be accepted by clicking off of the list. The drop-down selection will be canceled by pressing the ESC key.

The ResourceImageEditor is a type editor so it should derive (directly or indirectly) from the System.Drawing.Design.UITypeEditor class. I've decided to inherit from the built-in System.Drawing.Design.ImageEditor, because it already implements the image file selection functionality specified in the fourth requirement above. That is, the ImageEditor.EditValue implementation displays a file open dialog allowing the user to select an image file from the file system. Invoking this functionality from within my derived class is then a simple call to MyBase.EditValue.

In order to satisfy the first requirement (displaying the down-arrow button in the property grid), I've had to override the GetEditStyle method to return the appropriate constant from the UITypeEditorEditStyle enumeration:

VB
Public Overloads Overrides Function GetEditStyle( _
  ByVal context As ITypeDescriptorContext) As UITypeEditorEditStyle

  Return UITypeEditorEditStyle.DropDown
End Function

To display the list of image resources, I've had to enumerate all resources in a given assembly and display only the image resources in the list. To keep things simple, I've decided to employ a simple convention - when a resource name ends with a valid image file extension (.bmp, .jpg, .gif...), it is considered an image resource and it will be included in the drop-down list. The collection of image resource names is used to populate the drop-down ListBox control as described later.

Initially, the assembly that is enumerated for image resources is the one containing the ResourceImageEditor class. However, it can be changed setting the ResourceImageEditor.ResourceAssembly property to any valid System.Reflection.Assembly reference.

When the user selects an image resource name in the list box, the image should be loaded from the given assembly's manifest. This is implemented within the LoadResourceImage method:

VB
Private Function LoadResourceImage(ByVal resourceName As String) As Image
  Debug.Assert(Not resourceName Is Nothing)

  Dim ImageStream As System.IO.Stream = _
    Me.ResourceAssembly.GetManifestResourceStream(resourceName)
  Return System.Drawing.Bitmap.FromStream(ImageStream)
End Function

The drop-down user interface is implemented by dynamically creating and populating a ListBox control inside the overridden EditValue method. The editor also handles the Click and KeyDown events generated by the ListBox, which is necessary to intercept the ENTER and ESC keys. Here is the pseudo code illustrating the logic inside the EditValue method:

VB
Public Overloads Overrides Function EditValue(...)
  ' Store the context information for use
  ' in the drop-down ListBox event handlers.
  ' Create and fill the ListBox with the names
  ' of the available image resources.
  ' Add our special "Browse..." item.
  ' Wire the ListBox events.
  ' Display the ListBox in a drop-down window.
End Function

That's it.

I've written the ResourceImageEditor code.

I've created a sample MyPictureBox (derived from System.Windows.Forms.PictureBox) overriding the Image property as to have the ResourceImageEditor specified as the property's type editor.

I've compiled the code, placed the MyPictureBox control onto a form and invoked the drop-down user interface...

The mouse interface worked well. However, when I've selected an item with the keyboard and then pressed the ENTER key, the drop-down list disappeared, but my selection has been lost (i.e. the previously selected image hasn't changed). I've quickly discovered that when the ENTER key is pressed, the ListBox doesn't generate the KeyDown event.

The ESC key didn't generate the KeyDown event either, but it wasn't a problem because the drop-down list was "automagically" closed and I didn't have to process the currently selected item.

The property grid has apparently "stolen" the ENTER and ESC keys before the ListBox control could get a chance to process them. Or did it?

To make a long story short, the solution that worked was to employ the ProcessDialogKey method. The method is called during message preprocessing to handle dialog characters, such as TAB, RETURN, ESCAPE and also the arrow keys. The method is declared within the System.Windows.Forms.Control class in such a way that it simply delegates the call to the control's parent (if any). I've subclassed the ListBox control and I've overridden the ProcessDialogKey method to intercept the ENTER key like this:

VB
Protected Overrides Function _ 
      ProcessDialogKey(ByVal keyData As Keys) As Boolean
  If keyData = System.Windows.Forms.Keys.Return Then
    RaiseEvent EnterPressed(Me, EventArgs.Empty)
    Return True  ' True means we've processed the key
  Else
    Return MyBase.ProcessDialogKey(keyData)
  End If
End Function

Instead of generating the KeyDown event from within the ProcessDialogKey implementation, I've decided that a more straightforward approach would be to generate a new, distinguished event - the EnterPressed event. The ResourceImageEditor.EditValue implementation has been changed to handle this event (instead of the KeyDown event) and everything finally worked correctly.

You can use this technique to intercept the ENTER key in any Control-derived class that you use for implementing the drop-down UI inside your type editor. For implementation details, please have a look at the source code in the solution accompanying this article.

I've discovered this technique while implementing type editors for my AutoComplete WinForms component. It improves usability of your WinForms applications simply by putting the component onto a Form. And it comes with full VB.NET source code - Dynamic AutoComplete Tool

History

  • February 23, 2004 - Published.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Web Developer
Slovakia Slovakia
I live in Slovakia with my wife, two sons and two daughters. I've been doing Microsoft Windows development since 1988; primarily in VB. I'm a big fan of the MS .NET framework, publisher of the www.vbinfozine.com ezine and the author of components found at www.lamarvin.com.

Comments and Discussions

 
QuestionError opening in VS 2005 Pin
DavidAtDisplayData8-Nov-06 9:54
DavidAtDisplayData8-Nov-06 9:54 
AnswerRe: Error opening in VS 2005 Pin
palomraz8-Nov-06 19:22
palomraz8-Nov-06 19:22 
GeneralThat's really a GREAT piece of code, but... Pin
Luca Crisi, MCP5-Aug-05 4:11
Luca Crisi, MCP5-Aug-05 4:11 
GeneralRe: That's really a GREAT piece of code, but... Pin
palomraz5-Aug-05 4:57
palomraz5-Aug-05 4:57 
GeneralRe: That's really a GREAT piece of code, but... Pin
Holy Mother Of God27-Jul-07 5:48
Holy Mother Of God27-Jul-07 5:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.