Introduction
Subclassing is a technique that allows an application to intercept and process messages sent or posted to a particular window before the window has a chance to process them. This is typically done by replacing the Window Procedure for a window with application-defined window procedure. I will devide this article into 3:
- Subclassing in SDK programs,
- Subclassing in MFC programs,
- Reflected messages
Why subclass?
Sometimes you want to take the functionality of a control and modify it slightly. One example is replacing the menu in an edit control, Another is adding a context menu when you press on a button. One of the most common questions I encounter is "How do I screen out characters from an edit control?". I will show the solution to this from an MFC approach and from an SDK approach, while I try to explain Subclassing.
The need for subclassing comes from the fact that the code for the Windows controls is within Windows, meaning you cannot edit the code. Although you cannot edit the code of the control itself, you intercept the messages sent to it, and handle them your self. You do so by subclassing the control. Subclassing involves replacing the Message Handlers of the control, and passing any unprocessed message to the controls Message Handler.
SDK Subclassing
Although the Message Procedure for the control is located within windows, you can retrieve a pointer to it by using the GetWindowLong
function with the GWL_WNDPROC
identifier. Likewise, you can call SetWindowLong
and specify a new Window Procedure for the control. This process is called Subclassing, and allows you to hook into a window/control and intercept any message it gets. Subclassing is the Windows term for replacing the Window Procedure of a window with a different Window Procedure and calling the old Window Procedure for default (superclass) functionality. Remember DefWindowProc()
? instead of calling DefWindowProc
for default Message Handling you use the old Window Procedure as the default Message Handler.
Implementing SDK Subclassing
So, lets try to solve the classic question "How do I screen out characters from an edit control?", or "How do I create a letter-only edit control?"
First lets analyze how an edit control works:
An edit control is a window. It's window procedure lies within windows. Among other things, whenever it gets a WM_CHAR
message it adds the character to the text it contains. Now that we know that, we can simply subclass the edit control, and intercept the WM_CHAR
messages. Whenever the WM_CHAR
message is a letter or a key like space bar or backspace we'll pass the message to the edit control. If it isn't one of the above, we'll just "Swallow" the message, blocking it from reaching the Edit Control.
The first step to subclassing is to add a global/static WNDPROC
variable that will store the address of the edit control's Window Procedure.
WNDPROC g_OldEdit;
The second step is to create a new Window Procedure for the edit control:
LRESULT CALLBACK NewEditProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
TCHAR chCharCode;
switch (message)
{
case WM_CHAR:
chCharCode = (TCHAR) wParam;
if(chCharCode > 0x20 && !IsCharAlpha(chCharCode))
return 0;
break;
}
return CallWindowProc (g_OldEdit, hwnd, message, wParam, lParam);
}
The IsCharAlpha
function determines whether a character is an alphabetic character. This determination is based on the semantics of the language selected by the user during setup or by using Control Panel.
More interesting is the CallWindowProc
function. The CallWindowProc
function passes message information to the specified Window Procedure. A call to CallWindowProc
will allow you to call the old Window Procedure with any message you receive, thus providing default message handling
The third step is to replace the Window Procedure for the edit control, and to store the old one in g_OldEdit. For example, if you want to subclass an edit control that resides in a dialog (hDlg) and has the ID of IDC_EDIT1
you would use the following code:
hwnd hWndEdit = GetDlgItem(hDlg, IDC_EDIT1);
//Replace the Window Procedure and Store the Old Window Procedure
g_OldEdit = (WNDPROC)SetWindowLong(hWndEdit, GWL_WNDPROC, (LONG)NewEditProc);
The control is now subclassed. Any message to the edit control will first go through the NewEditProc
Window Procedure, which will decide if the message should go to the edit control's Window Procedure .
MFC Subclassing
Subclassing in both MFC and SDK programs is done by replacing the message handlers of a control. It is rather easy to subclass in a MFC program. First you inherit your class from a class that encapsulates the functionality of a the control. In ClassWizard, click on "Add Class", then "New". For the base class, choose the MFC Control class you are deriving from, in our case, CEdit
.
Using MFC relieves you from having to call the old Message Handlers, since MFC will take care of it for you.
The second step is to add Message Handlers to your new class. If you handle a message and you want the control's message handler to get a shot at it, you should call the base class member function the corresponds with the message. this is the subclassed WM_CHAR
handler:
void CLetterEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
if(nChar <= 0x20 || IsCharAlpha((TCHAR)nChar))
{
//Call base class member, which will call
//the control's message handler
CEdit::OnChar(nChar, nRepCnt, nFlags);
//If not, the message is blocked
}
}
The third and final stage is to associate the window with an instance of our new class. In a dialog this is done simply by using ClassWizard to create a control member variable of your class in the window's parent, and associate it with the control.
In a non-dialog parent you should add a member variable instance of your control to it, and call one of the two CWnd
Subclassing functions: CWnd::SubclassWindow or CWnd::SubclassDlgItem
. Both routines attach a CWnd
object to an existing Windows HWND
. SubclassWindow
takes the HWND
directly, and SubclassDlgItem
is a helper that takes a control ID and the parent window (usually a dialog). SubclassDlgItem
is designed for attaching C++ objects to dialog controls created from a dialog template.
Further Reading
For a more in depth treatment of MFC subclassing see Chris Maunder's article: "Create your own controls - the art of subclassing".
Reflected Messages - MFC 4.0+
When you subclass a control, besides handling the message it receives, in MFC you can also handle the notifications it sends to it's parent window. This technique is called Message Reflecting. Windows controls often send notifications to their parents, for example, a Button will send a WM_COMMAND
message telling it's parent it has been clicked. Usually it is the parent's job to handle these messages, but MFC will also allow you to handle them in the control itself. In ClassWizard these messages appear with an "=" sign before them, indicating they are Reflected Messages. You handle them just like any other message. The macros for these messages are similar to the regular messages, but have _REFLECT
added to the end of the macro. For example, ON_NOTIFY()
becomes ON_NOTIFY_REFLECT()
.
Further Reading
For a more in depth treatment of MFC reflected messages see the MFC technical notes:
- TN014: Custom Controls
- TN062: Message Reflection for Windows Controls
License
This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.