Cancellable In-Place Edit Popups

How to create in-place edit windows for controls easily, including an Editable ListBox implementation.

Editing a ListBox item in the Demonstration Project.

Controls like the ListView and the Outlook ListBar allow you to edit items in place by either double-clicking them or through a context UI feature such as a menu. This article provides a class which helps with the tricky parts of implementing in-place editing, and demonstrates how to use it to create an editable ListBox. VB.NET and C# code provided.

Creating a Cancellable Edit Popup

Creating a TextBox which appears on top of an item in a control isn't hard at all: you can do this just by adding a TextBox control to your form, bringing it to the front and making it visible at the desired location. But that's the easy part: the more tricky aspect is determining when you should make the TextBox go away. There are normally three things which cause an edit popup to be dismissed:

  1. Typing Return or Escape into the edit box.
  2. Clicking out of the text box on some other part of the application.
  3. Switching to another application on the system by alt-tab or using the mouse.

The first aspect of the behaviour is easy to implement using the KeyDown or KeyCode event of the text box. However, the other two parts are rather more tricky. The LostFocus is the only realistic candidate for detecting these events. If you try that, though, you'll find that it only occurs if you click on an control capable of receiving the input focus. Clicking on a non-client area of a control or form (such as scroll bars, or just a control that isn't set up to receive input focus like a label) doesn't shift the input focus. In addition, there is no event whatsover when someone alt-tabs away from your application.

To fix these two problems, the techniques described in the article Using .NET Forms as Popup Windows can be used. Full details of the techniques are provided there, but to recap you can detect mouse clicks anywhere in an application using an IMessageFilter implementation and switching to another application can be detected by looking for the WM_ACTIVATEAPP message sent to the main form.

Helping Hands

This functionality is wrapped into a reusable class, PopupCancelNotifier. The class has two methods and one event which give you all you need to simplify showing a popup edit control:

  • StartTracking(Control ctl)
    Once you've shown your edit control in the right location, call this method and the class will attach the subclass and message filter to check for cancellation events. If one is detected, the PopupCancel event is raised.
  • StopTracking()
    This method allows tracking to be cancelled programmatically and should be called whenever the popup edit control is hidden (note that it will be called automatically if the class is told through the PopupCancel event that tracking should be cancelled, but it can be called multiple times, which makes coding easier).
  • PopupCancel
    This event provides details about the popup control that is about to be cancelled. You can set the Cancel property of the associated arguments to true to, erm, cancel the cancellation of the control (sorry, I really should have given this a proper name!)

Note that the code is not limited to edit controls; you can popup any Framework control. Other ideas are to use a RichEdit or even something like a ListView to pick from one of many items.

Using It

To use PopupCancelNotifier you will want to do four things:

  1. Create an instance of the class and hook up the PopupCancel event.
  2. Detect when you want to show the popup in the control that you want to display the popup editor for.
  3. Position the popup control at the right place, make if visible, bring it to the foreground and call StartTracking.
  4. Hide the control when you either get a PopupCancel event or an event from the popup control that means using the popup is over (e.g. a Return or Escape keypress in an edit control. Then call StopTracking

These steps are demonstrated in the sample application in two ways: firstly, the simplest part of the demo shows how to allow the text of a label control to be edited when it is clicked. Secondly, the same technique is applied to a ListBox control to allow the any item to be edited place.

Conclusion

This article provides a class which you can use to add in-place editing to new or existing controls and wraps up some of the trickier details of making the editing and cancellation to work correctly. Users intuitively understand editing in-place and have come to expect it in many cases. Add the feature!

Note that at the time of writing the Outlook Style ListBar control implements this technique using a Win32 API MouseHook rather than a IMessageFilter as shown here. However, fundamentally the technique is exactly the same and that control will be updated to use this technique at the next release.