Hosting a Native Windows Control – The desktop approach

Following Neil’s recent post on my control hosting article, I realised that the article had not gone into detail on the differences between the Control class in the full framework and Compact Framework. The Control class in the full .NET framework is incredibly powerful, and when I built the ControlEx class to enable hosting I tried to work to the same model where possible. Generally the Compact Framework follows the design of it’s larger cousin and therefore if the functionality of the Control class is enhanced in future revisions of the Compact Framework it will likely follow a subset of the desktop functionality.

There is a key difference with the Control class on the desktop, than any custom Control derived class on the Compact Framework. On the desktop you can override the protected CreateParams property to force the control to create any window class (remember that in Win32 terms any control is considered a window with it’s own unique handle and based on a specific window class). It’s possible to create an intrinsic windows control like the SysAnimate32 with almost no need to resort to Platform Invoke. If you view the running program with a tool like Spy++ you’ll see the SysAnimate32 window is a direct child of the form it sits on. We can pass it messages entirely in managed code by simply calling it’s WndProc method. The Handle property for the control is the handle for the SysAnimate itself.

On the Compact Framework this behaviour is only true for the standard set of Controls in System.Windows.Forms. For all other controls a standard window class is created (and this we have no control over), any native window class we wish to use must be added as a child to this window. However the next main hurdle with the .NETCF Control class is that there is no WndProc method we can override, so if we create a window as a child of this control, we have no way of capturing it’s window messages (Unless we capture them in the application message loop itself using ApplicationEx). Therefore to implement a WndProc for the control we use an additional layer.

The MessageWindow class is specific to the Compact Framework and serves only one purpose, it allows us to receive messages within managed code. By default the MessageWindow is a hidden 0x0 pixel window. With some P/Invoke magic it is possible to alter the size and position of the MessageWindow to make it a visible control – this is our empty canvas. We now create the native window as a child of this window and we have a native control, housed within a managed control with the ability to both send messages to the control and, most importantly, receive notification messages and process them.

There are a few house-keeping chores required to keep this three-layer solution working. Whenever the control is resized we must resize both the child (MessageWindow) and grandchild (native control) to fit. In order to hide as much of this plumbing as possible, and to avoid re-inventing the wheel, the ControlEx class was built to do all this work for you. Like the Control class on the desktop it exposes a CreateParams property which you can override with your class type. Notification messages received from the native control by the MessageWindow are passed to the OnNotifyMessage method of the ControlEx. It’s not quite as powerful as the full WndProc of the desktop but it allows us to receive events such as item selections within the native control. The WebBrowser sample within the article download uses these to expose events when the user taps a link.

The HtmlViewer, MonthCalendar and InkX controls within SDF v1.2 are all built using this technique, however they use a less transparent approach to the new ControlEx class. I’ll be upgrading these to use the ControlEx class, and of course you can use the class yourself to wrap other native windows controls of your choice.

You can download a sample “Trash It!” project here which is a desktop .NET project which wraps the SysAnimate32 control to display a delete animation. The only P/Invoke required is LoadLibrary / FreeLibrary to load the dll containing the AVI animation resource. This should provide some contrast with the WebBrowser class in the article.