Subclassing Controls with a Managed Window Procedure
The .NET Compact Framework version 2.0 provides the capability for native code to call managed code by using a callback delegate. By subclassing a managed control to receive a callback from its corresponding native control, you can create controls with functionality not directly available in the .NET Compact Framework.
This is an advanced topic for developers knowledgeable about Windows programming and control subclassing. To subclass a control, you will need to know the inner details of the native control and how it maps to the functionality you want to provide in the extension to the managed control. You will need to know which Windows messages to monitor and native Windows CE functions to invoke to provide the desired functionality.
This topic describes subclassing the TreeView and Button controls, and the following topics provide the code examples and instructions for building the applications.
How-to topic | Demonstrates |
---|---|
Subclassing the TreeView control to create an implementation of the NodeMouseClick event. The .NET Compact Framework does not directly support this method because of the need to limit its size for constrained resources. |
|
Subclassing the Button control to display a colorful gradient fill. Note that this button is provided primarily for demonstrating how to use subclassing and callbacks. For an easier way to create a gradient filled button, see How to: Display a Gradient Fill. |
Both of these subclassing programs include the WndProcHooker
class and a helper class of native Win32 structures, platform invoke declarations, and a WndProc
delegate. For code listings, see How to: Use a Class for Hooking Windows Procedures and How to: Use a Helper Class for Platform Invokes.
WndProcHooker Class
The WndProcHooker
class provides the ability to have a native control, or window, invoke a callback to managed code when a particular Windows message is received for that control. This is achieved by substituting a native control's window procedure (WndProc) with a generic window procedure, WindowProc
, that performs a look-up to determine if the control is in a list of controls associated with callback methods to be invoked. If so, the control is considered to be hooked.
If the control is hooked, WindowProc
determines if the control is mapped to respond to a particular Windows message. This is the message map, which maps a Windows message with a WndProcCallback
delegate that calls your managed method containing the desired functionality. If the message map contains the message, the WndProcCallback
delegate invokes your code using the message parameters that were supplied to WindowProc
.
Hooking Controls
The HookWndProc
method associates the control's handle with a message map to be used by the generic window procedure WindowProc
. This is referred to as hooking a control.
The HookWndProc
method determines whether a control has already been hooked. If not, it creates a HookedProcInformation
object for that control. This object contains a reference to the control and the message map. If the handle to the control has already been created, it hooks the window by creating a pointer to the window's original window procedure for later restoration. If the handle has not been created, it will get hooked by the ctrl_HandleCreated
method that handles the HandleCreated event.
Next, the HookWndProc
method adds the HookedProcInformation
object to one of two generic dictionary collections:
The hwindDict
dictionary, which contains a global list of all the hooked window handles. The key is an hwnd
. Controls whose handles have been created go into this dictionary. Controls in this dictionary are evaluated by WindowProc
for any mapped messages.
Unhooking Controls
The UnhookWndProc
method provides two ways to unhook a control:
Remove a message from the message map for a control but still keep the control in the
hwndDict
dictionary of hooked windows. This method also restores the original window procedure for the control by using a pointer kept in theHookedProcInformation
object.Remove the control from the
hwndDict
dictionary of hooked controls and either remove its handle and place it in thectrlDict
dictionary or dispose of the control entirely. This method also restores the original window procedure for the control by using a handle kept in theHookedProcInformation
object.
Subclassing the TreeView Control
The sample program TreeViewBonus
class, listed in How to: Subclass a TreeView by Using Native Callbacks, extends the TreeView control to include the NodeMouseClick event, which is not directly available in the .NET Compact Framework.
The NodeMouseClick event is obtained by adding the WM_NOTIFY message to the message map for the control, as performed by the WndProcHooker
class. The managed callback method WM_Notify_Handler
invokes the native GetMessagePos function to get coordinates of the mouse cursor at the time the Windows messages were sent.
Note that these coordinates are relative to the visible client area of the screen, not relative to the TreeView control. The TreeViewBonus
class converts screen coordinates to client coordinates with the PointToClient method for the control. Then these client coordinates are sent along with the TVM_HITTEST message to determine if and where the TreeViewBonus
object was clicked.
The TreeViewBonus
class contains code to get the coordinates relative to the control by using the native control's TVM_HITTEST message.
If the click occurred on one of the tree view nodes, the native TVHITTESTINFO structure contains the handle to that node. The final step is to recursively traverse through the managed TreeView nodes, performed by the FindTreeNodeFromHandle
method, to find the matching handle and raise the NodeMouseClick event. The TreeNodeMouseClickEventArgs
class provides the following data:
The node that was clicked.
The button clicked.
The number of clicks, set at 1.
The x-coordinate where the click occurred.
The y-coordinate where the click occurred.
The TreeViewBonus
class hooks the native tree view control's parent to a managed window procedure as performed by the WndProcHooker
class. It responds to the OnParentChanged event by hooking the parent control, thereby accommodating the possibility that the TreeView was moved to a new parent, such as from the Form and into a Panel.
Subclassing the Button Control
The GradientFilledButton
and GradientFill
classes listed in How to: Subclass a Button by Using Native Callbacks extend the Button control to display a gradient fill between two colors. This program is primarily meant to demonstrate subclassing. However, an easier way to display a gradient fill in a button is to create a custom control derived from Control, as described in How to: Display a Gradient Fill.
The constructor for the GradientFilledButton
class creates instances of the WndProcHooker
class to map Windows messages to managed callbacks. These callback methods draw the button in the appropriate state depending on the Windows message and the state of the Capture property for the control. The following table lists the mapped Windows messages and their corresponding callbacks.
Windows message | Managed callback method and description |
---|---|
WM_KEYDOWN |
|
WM_KEYUP |
|
WM_LBUTTONDOWN |
|
WM_LBUTTONUP |
|
WM_MOUSEMOVE |
|
WM_PAINT |
|
These managed callback methods use the DrawButton
method to draw the button in the appropriate state. This method has two overloads for drawing the button either on a Window, used by this example, or on a Graphics object. Both overloads take a Boolean value that is true if the button was pressed.
The GradientFilledButton
class uses the GradientFill
class to perform the platform invoke calls to native code to do the fill. The GradientFill
class provides properties to set the start and ending colors and to specify the fill direction as left-to-right or top-to-bottom.
See Also
Tasks
How to: Use a Class for Hooking Windows Procedures
How to: Use a Helper Class for Platform Invokes
How to: Subclass a TreeView by Using Native Callbacks
How to: Display a Gradient Fill
Concepts
.NET Compact Framework How-To Topics