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.
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.