Introduction
This article explains how a to subclass already subclassed controls. It
includes a simple Runtime form editor.
Introduction
I had a project in the company I work in that needed different forms for
different customers, so the team decided to create a runtime form editor that
can enable editing the forms for each customer. In order to do that I needed a
way to control all the Controls on a formview or on a dialog; enabling me to
resize the controls, move them, or hide them. I wanted to subclass the Controls
on a dialog/formview in order to control them, however the major problem was
that most controls were already subclassed. I wrote a class
(CDblSubclassWnd
) that can subclass controls even if they are
already subclassed.
Explanation
The way it works is, the messages first go into a function called
PreWndProc
and then they continue to the original
WndProc
of the Control, and after it finishes from there it goes
into a function called PostWndProc
.
The class is simple to use. The demo in the article contains a very simple
form editor that allows you to simply move the controls on the form while you
are in edit mode. Using the class is very simple. You have three methods of the
class CDblSubclassWnd
that you will work with. All methods are
static
- The first thing to do is give to the class two pointers to functions, that
the
CDblSubclassWnd
class will call to pass by the messages of the
subclassed controls.
The method signature is:
static void SetDblSubclassWndProc(
DBLSUBCLASS_WNDPROC *pPREPointer = NULL,
DBLSUBCLASS_WNDPROC *pPOSTPointer = NULL
);
The first parameter would be a pointer to the function
(PreWndProc
) that will be called prior to passing on the message to
the main WndProc
of the control (or another Proc if it is
subclassed). This function would give you the option of changing messages before
they reach their control. The second parameter would be a pointer to the
function that will be called after the control processed the message
(PostWndProc
) You can use this to inspect the values changed (or
returned) by the Control.
The DBLSUBCLASS_WNDPROC
function pointer is defined as such:
typedef LRESULT CALLBACK DBLSUBCLASS_WNDPROC(
CWnd*,
HWND,
UINT,
WPARAM,
LPARAM,
bool &
);
The first parameter is a pointer to the Control Window class that the current
message belongs to. The second parameter is the Handle to the Window. The third
parameter (UINT
) is the Message Number itself, and the following
two parameters (WPARAM, LPARAM
) are the two parameters that are
passed with every Window Message, their meaning depend on the actual message.
The last parameter (a reference to a bool) is used to indicate whether you want
the DblSubclassed Engine to discontinue routing the message and return the
return value returned by one of your two functions (pPREPointer,
pPOSTPointer
). To explain this point more by an example, if you have a
button and you clicked on this button, the clicking message
BN_CLICKED
would first go to the PRE function pointer you assigned,
if you do not want this message to continue to the WndProc
of the
control, you can stop this by setting the the boolean (last parameter to the
function DBLSUBCLASS_WNDPROC
) to false, this way the message will
be dropped and will not reach the control.
- Now that you have your
PreWndProc
and PostWndProc
set, it is time to actually call the methods to subclass the Controls. void CDblSubclassWnd::SubclassChildsRecurs(
HWND hWnd,
DBLSUBCLASS_RECURSIVECALLBACKPROC *pFunc
)
You call this method giving it (for example) the handle to the dialog you
are working in. The function will traverse all the Controls on the dialog and
for each control will call the function pointed to by the second parameter of
the function, DBLSUBCLASS_RECURSIVECALLBACKPROC
. This function will
be called by the engine for each control that is going to be subclassed, its job
is to decide whether the control is to be subclassed or not.
For example, if you want to subclass all the Buttons on your dialog and not
all the controls, in your RecursiveCallBack
function, you would
test for the control for being a button or not (control's class) and you would
return true only for the button controls. The RecursiveCallBack
simply takes a HWND
parameter (to do your testing on the control)
and returns a boolean value to indicate whether to subclass the specified
control or not.
- After you subclass the controls the messages will start flowing into your
PRE
and POST WindowProc
assigned in the call to
SetDblSubclassWndProc
(in the first step). When you want to end
this effect, then you unsubclass the doubly subclassed controls by using
UnSubclassChildsRecurs
. Its use is straight forward, you give it
the Handle to the main window (the dialog in our example) and it will go through
the Controls belonging to it and unsubclass all the Controls that were
subclassed before.
I hope you will find this article useful to you.
History
Date Posted: May 22, 2003