Using PreSubclassWindow |
|
One of the lesser-understood handlers is the PreSubclassWindow handler. It turns out this is a very nice handler to use for certain effects in dialogs.
There are a couple problems in interfacing MFC and dialogs. I have often stated that MFC is a triumph of brute force over common sense. What I mean by this is that the object model of C++ is so completely different from the object model of Windows that the two are almost incompatible. In spite of this, some very clever programming makes the integration almost seamless. But there is a substantial difference between "seamless" and "almost seamless".
In normal window handling, you will have an OnCreate handler that is invoked just as the window is created. In normal MFC window handling, you have have the PreCreateWindow virtual method which lets you change the CREATESTRUCT values, such as style parameters.
These are not accessible in dialogs. The reason is that the members of your subclass can be invoked only when the window is mapped into MFC, that is, its handler has been changed to be the AfxWnd handler. But dialog controls are created long before the subclassing, which takes place on the first DoDataExchange handler, which happens during the OnInitDialog processing. This is far too late.
I have found that the PreSubclassWindow is an ideal place to make certain modifications, such as changing styles. This applies only to those styles which have an effect after the window has been created (many styles cannot be changed once the window is created. You can change the style bits, but the window itself is oblivious to these changes). This is useful when the styles you want to change are not part of the styles presented by the dialog editor.
Another place I use it is when I want to set a font. For example, in this particular class, I needed to set a font that was 80% of the height of the window. The code is shown below. This creates a font which is a member variable of my class which uses the font that will be set during the OnPaint handler. I want to use the same font as the parent window, but 80% of the size of the current window.
void CMyControl::PreSubclassWindow() { CRect r; GetClientRect(&r); LOGFONT lf; CFont * f = GetParent()->GetFont(); f->GetLogFont(&lf); lf.lfHeight = -(int)((double)r.Height() * 0.8); font.CreateFontIndirect(&lf); CMySuperclass::PreSubclassWindow(); }
There are some things you cannot do in a PreSubclassWindow handler; for example, I found that doing a ShowWindow would cause NT 4.0 SP6 to bluescreen immediately. I have not tried the experiment on Win2K.
Note that in MFC there are serious problems if you attempt to create another window while in PreSubclassWindow. It does not work, but the error reporting is flawed and produces incomprehensible diagnostic messages, such as a statement that there is an unhandled user breakpoint deep in ntdll.dll (it is actually the DebugBreak API being handled).
However, PreSubclassWindow is a much-neglected method, having little documentation to show its utility. In writing this article, I found precisely one useful reference in the MSDN, to an article Paul DiLascia wrote in the Microsoft System Journal in December, 1999, where he actually says pretty much the same thing. But having written this article, I might as well post it to my Web site.
Several people have pointed out that "You aren't doing anything in PreSubclassWindow that could not be done in OnInitDialog. They are absolutely right. The difference is one of encapsulation. If you force the user of your class (which is often yourself) to do the initialization in OnInitDialog, you must remember what to initialize, even if it is independent of the dialog. And if you change what needs to be done in the initialization, everyone who uses the class (and in every place they use the class), must add or modify the initialization in OnInitDialog. By putting it in PreSubclassWindow, the initialization "follows" the changes you make, and the clients of the class do not need to be concerned about this at all. This is good modularization.
You might also want to check out my essay on self-registering windows, another useful technique to know about if you are doing custom controls and want to put them in a dialog.
The views expressed in these essays are those of the author, and in no way represent, nor are they endorsed by, Microsoft.