Tooltip control for .NET CF

I went to see a company in the UK (ATOS Origin) in December and they need a tooltip class that works binary compatible on desktop and device - they are sharing all code in the project between both environments. Should they be using a tooltip on Windows Mobile? Microsoft omit this control for the OS because it is deemed to have little UI benefit, but there are always exceptions to the rule, so if ATOS wants a tooltip and can clearly understand the usability aspects of adding this then that’s fine with me. Today they have their own custom control implementations for button and text box and these are pretty much the only controls used in the app. These control track mouse movement and when a tooltip event is detected they create a new Form instance and popup it up, very small and just containing the tip text. This is mostly ok for them but has some nasty focus issues for the main form, and their controls don’t operate quite the same way as the windows controls.

So I took on the task of trying to build something better for them …

What I set out to do was create a VS designer hosted control that would capture mouse moves on all standard controls and simulate the real tooltip control functionality on the device - and of course it needed to work on the desktop as well. Initially I committed just one day of work on this, but I’m up to four and still counting (lesson 1: never trust developer’s estimates for anything!).

Here is my code (disclaimer applies) please feel free to take a look. To build this you will need EVC 4.0 SP2 or later and Visual Studio .NET 2003. The native component TooltipHelper needs to be manually deployed to \Windows or the project directory. The managed component needs to be built in two steps:

1> Build interactively through Visual Studio to get the runtime component built. Build the debug and release versions.

2> Run a Visual Studio command prompt and navigate to the project directory. Run the BuildDesignTime.cmd to enable the NETCFDESIGNTIME compile time directive and build the design time version of the component. This also copies the component into the default design directory under VS.NET and copies the Release component into the Compact Framework directory which enables Visual Studio to automatically deploy that component with a development project.

These are my conclusions (so far):

 

Controls in the Compact Framework offer the MouseMove event but don’t (in general) forward the events correctly so there is no way to track stylus movement inside a child control from the parent form or from another control on the screen. The Enter and Leave events work fine but that requires the user to tap into the control in order to show tip text which just isn’t right.

 

I did look over the OpenNetCF controls as well, and found these are a bit of a mixture: some are derived directly from Control and offer mouse move events correctly, whereas others are specializations of existing classes like ButtonEx deriving from Button and suffer from the underlying control’s deficiency in this area.

 

Creating a designer aware control is a bit more complicated for CF than for desktop WinForms designer. What I needed was a “Component” derived class rather than “Control” derived as the tooltip has no visible area at design time – to understand the difference here consider the designer experience when placing a Button control (derived from Control) and placing a Menu or SIP control (derived from Component) that sits in the application tray. The CF designer supports “Control” derived custom controls with no fuss, but for Component controls a special attribute needs to be specified for the class in order to get the custom control displayed: ToolboxItemFiler( <type string> ,<type enum>) . This tells the design environment that the control supports | requires | denies | custom (etc) a specific parent type in the control being designed. For this to work in the Smart Client design surface I used: [ToolboxItemFilter(“System.CF.Windows.Forms”,ToolboxItemFilterType.Require)] – this makes the control visible but stops it showing in the desktop designer. Using “Allow” would support both.

 

Setting the tooltip text: the value needs to be set per control on the form so I used the [ProvideProperty( <property name string>, <control type>)] attribute on the class which causes the designer to add a property to each control placed on the form and allow the user to specify the text value for each one – this feature is just really cool J. The attribute I used was this: [ProvideProperty( “TooltipText”, typeof(Control))] and the generated code for a button on the form looks something like this: this.tooltipCF1.SetTooltipText(this.button1, “Tooltip Text for Button1”);

 

I tried just about everything to get the mouse moves on CF, but in the end I had to resort to a native C++ DLL that is used to subclass the controls on the form and capture the WM_MOUSEMOVE, WM_LBUTTONDOWN and WM_LBUTTONUP messages, then forward them back to the listening tooltip control using a Microsoft.WindowsCE.Forms.MessageWindow class. All sounds nice and easy so far but the problem I hit is when to subclass the controls? It turns out the earliest point this will work is a Form.Load, but there is no easy way of attaching to that event from a child control on the form! In the end what I did was to capture the ParentChange event for each control and on that event register for the Form.Load event – it needed a little thought to get this to fire only once, otherwise I would have had a few too many subclasses going on.

 

The next challenge is that CF doesn’t offer the windows handles of the controls so I used the WindowsFromPointXY Win32 API to get each windows handle, then fire it off to the C++ to subclass. In theory CF could change the windows handle underneath my code, but I haven’t see it happen yet (at least not after the Form.Load has fired).

 

To show the tip text I use a background timer that is created in the constructor and started when a mouse down or mouse move is detected. The timer is stopped if a mouse up is detected or the move takes the mouse outside the control. If the mouse remains stationary for the required duration then the timer event fires and the timer is reset to wait for the ‘display’ period of the tip. The text is displayed by using a Label control that is moved to the location of the mouse (with a bit of frigging to fit as much text on the screen as possible) and then set to visible. Using a control works really well as there are no focus issues showing and hiding the label. If the mouse stays stationary until the tip text display time has expired then the tip is hidden again.

 

Couple of problems outstanding:

This control doesn’t yet work on the desktop and device and its certainly not binary compatible. It’s possible to make it work but requires a tweak to the designer and will require some twiddling runtime information to avoid missing method exceptions. The code already sets up the mouse move message handler for child controls and this should work fine on the desktop controls.

Controls inside containers don’t work yet – e.g. button inside a panel. To fix this probably requires changing “.Parent” to “.TopLevelControl” and that should be about it.

The native DLL doesn’t ship with the managed components at this point; it requires a manual copy step to get the dll on the device. I don’t know the right way to solve this problem at the moment.

I couldn’t get the designer icon to work properly even after spending ½ day on it! Very frustrating! Docs are reasonably clear, but it wouldn’t work for me

Resources:

This is where I got all my information:

Custom Design-time Control Features in Visual Studio .NET

This article is a pretty good overview of all the different features and steps for building control designers and includes information about ProvidePropertyAttribute.

Building Windows Forms Controls and Components with Rich Design-Time Features

This is a cracking article that covers most of the details I couldn’t find elsewhere including ToolboxBitmapAttribute for the icon settings.

Windows Forms Designer Sample

This is the HelpLabel sample that gave me my starting point as it’s quite close to what I needed.

Looking to the future we have tooltip support coming in CF 2.0 for the menu bar on Pocket PC to match the underlying OS control capability, but not for general form controls.

If I get time to work on this code some more I will post updates. But right now it’s onto the next thing as alwaysJ

Marcus