Environment: Any flavour of MFC (Doc/View) including VC6 and VC7 (demo is VC7)
Introduction
I had a project in the company where I work 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. To do that, I needed a way to control all the Controls on a formview or on a dialog; I needed to resize the controls, move them, or hide them. I wanted to subclass the Controls on a dialog/formview 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.
It works like this: The messages first go into a function called PreWndProc and then they continue to the original WndProc of the Control. After it is finished, 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 easily move the controls on the form while you are in edit mode.
Using the Class
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. The CDblSubclassWnd class will call these to pass by the messages of the subclassed controls. The methods signature is:
static void SetDblSubclassWndProc(DBLSUBCLASS_WNDPROC *pPREPointer = NULL, DBLSUBCLASS_WNDPROC *pPOSTPointer = NULL);
The first parameter is 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 is 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 and LPARAM, are the two parameters that are passed with every Window Message; their meaning depends 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 or pPOSTPointer. To explain this point more by an example, if you have a button and you clicked 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 of 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, BLSUBCLASS_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 pointer to the second parameter of the function, BLSUBCLASS_RECURSIVECALLBACKPROC. This function will be called by the engine for each control that is going to be subclassed; its job is 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 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, you unsubclass the doubly subclassed controls by using UnSubclassChildsRecurs. Its use is straightforward: 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.