C/C++ Users Journal March, 2005
Whenever you implement an event handler for a C++ window class, when such a window (or window that extends it) gets created on-screen, "subclassing" occurs behind the scenes. If you've programmed nontrivial GUI apps, you're probably familiar with the term. Simply put, if you subclass a window, you get to handle its events first. As Listing 1 shows, whenever you say create_wnd<hyper_link>(par), the hyperlink's event handler gets created and subclassing occurs.
Event handlers are inherited. If you extend a window class, you automatically inherit all its event handler(s)its base class(es), event handler(s), and the like. This makes sense because base class(es) need to implement their logic. In Listing 2, the sample_dlg class automatically inherits the event handler(s) from the dialog class (and its base class(es)) and from the resizable_wnd class (and its base class(es)).
When an on-screen window is created, its corresponding C++ window class gets created. Then the event handler(s) corresponding to this C++ window class are created. If there is at least one event handler, the window is subclassed; if there are no event handlers, no subclassing is done. In particular, a C++ window class is not required to have any event handler, it is optional.
Again, the C++ window class decides which are its event handler(s). In addition to those, you can add your own event handlers to:
Any of these can be solved by extending existing window classes (that is, derived from wnd_extend<>), but they are not as powerful. For instance, you could create a window class that simply logs all events. But what if you want to use it throughout your application? All of your existing window classes must extend from iteasily turning into a maintenance nightmare. For instance, what if you miss a class? Or what if you'll want to turn this logging off later? How easy will it be? What if you want to reuse this logging in another application?
For that matter, what if you want to implement persistence? Some controls/ windows need to implement it, and some do not. It should, of course, be flexible (you should easily add/remove controls to be persisted). Which controls should implement this behavior? All or only some? What if it proves expensive? You can implement all of these with advanced subclassing. Better yet, you can easily turn them off or reuse them in other applications.
Advanced subclassing is very loosely coupled with the rest of the application. Some of these classes are simply transparent to the rest of the application (for example, logging all events), thus making your application highly flexible.
There are two types of advanced subclassingautomatic and manual. Automatic subclassing occurs, well, automatically. Its syntax is:
template< class self, class impl, h_type = events_before, int idx = 0, ...> struct subclass::auto_event_handler;
while its parameters are:
In addition, you can implement the static void matches_wnd(window_base*); function (which, by default, returns true).
Whenever an on-screen window gets created:
For instance, say you have an auto_event_handler class. When automatic subclassing takes place, an instance of its type gets created if:
After automatic subclassing event handlers have been created, two arrays are formed:
Both BeforeHs and AfterHs are now partially ordered, according to each handler's idx (if handler H1 has a lower index than H2, H1 will be called before H2). Finally, they will be added to DefHs (DefHs = BeforeHs + DefHs + AfterHs).
Say your boss tells you to implement this: If there's no movement (keyboard or mouse) within your application for three minutes, minimize the app. It's easy; see Listing 3.
Logging all GUI events is easy; see Listing 4(a). If you want to be able to enable/disable it, you can add Listing 4(b).
Besides automatic subclassing, you can also do it manually. You can choose this when you know which windows you want to subclass, only at a later time than their creation.
The simplest example is the tooltip_parent class (which manages tooltips in a dialog). Every time you set a control's tooltip, that control must be subclassed (you need to add one more event handler for that control) [1]. Its syntax is:
template< class self, class impl, ...> struct subclass::manual_event_handler;
Both self and impl have the same meaning as for automatic subclassing. To add such a handler, you use:
your_handler_class * subclass::add_manual_event_handler<your_handler_class>( some_wnd, h_type, int idx = 0);
which returns a pointer to the newly created handler. Later on, you can use this pointer to manually remove the handler, in case it's not needed anymore [2]. Also, h_type and idx have the same meaning as for automatic subclassing. Listing 5 shows you how you can implement a reusable dialog class whose children edit controls don't show any context menu.
When it is all said and done, however, I recommend automatic subclassing over manual subclassing. Manual subclassing can happen at any time (as opposed to automatic subclassing, which always happens at window creation), which brings some further complications:
A manual event handler can also be removed, so that when an event happens, it won't be called anymore. Just call subclass::del_manual_event_handler(some_wnd, ptr_to_handler). As with adding, removing an event handler must happen in the same thread that created the window.
Until now, you had to be a C++ window class to be able to listen/respond to events. However, with advanced subclassing, anyone can be notified of events. This is a useful feature: Any time you need to be notified when certain events occur, simply plug in an advanced event handler. That's it! For instance, Listing 6 is a straightforward way to be notified when mouse movement occurs.
If you've used other frameworks before (MFC/ATL), you're probably familiar with the PreTranslateMessage function, which gives you a first look at the event and lets you consider the event as "translated," thus stopping further processing.
However, PreTranslateMessage is error prone and unreliable because it works only for events that are posted into a thread's message queue. But there are events that get sent to windows directly (for instance, WM_KILLFOCUS) that you can't catch in PreTranslateMessage.
I thought about providing a pre_translate event handler, but I chose not to. Since advanced subclassing lets me catch all events, why have only a piece of cake, when you can have it all, and eat it, too?
Advanced subclassing can give you a very tidy way to create yet another Spy [4]. Logging all your events is simple. In fact, Listing 4 shows an easy way to do that. However, because you're operating from inside an application, you can do so much more. For example, you can do a simple profiler that shows you how much time your application spends processing each event. Even more, it can show the events chain (for instance, clicking on a checkbox can trigger other events, such as disabling an edit box and changing another label's text).
Profiling is easy. For each window, add two event handlersone at the beginning and one at the end. For each event, record the time its first handler gets called, when the last event handler gets called, and compute the difference (Listing 7). After you download the code (http://www.cuj.com/code/ and http://www.torjo.com/win32gui/), you can check out the win32gui/spy/spy.hpp and win32gui/spy/spy.cpp files, which log all gathered information into a file (win32gui_spy.txt).
If you want to use this spy:
The Win32GUI Spy, an application I wrote using this library, monitors the aforementioned log (win32gui_spy.txt), and shows it to you in a user-friendly form. It features advanced filtering, and information is shown in a tree-like view (see Figure 1). After downloading the latest win32gui, you can find the code for Win32GUI Spy in examples/applications/spy.
Since you can spy from inside your application, you can create many pluggable behaviors that you can reuse. For example, you might want to turn off some timer messages if they make your application slower in debugging mode. Or you could implement skinning, using advanced subclassing, and see whether it's too CPU consuming. You could even make it possible to break into debug mode, when a certain combination of events happens. You could break on "selecting a checkbox, which triggers the text on an edit box to change, which triggers the text of another label to change"such breakpoints are very hard to set directly in the debugger.
Documentation is available as of Version 1.6. You should check it out. I have written an extensive document about event handling. I've also documented a lot of classes that I did not talk about here due to space considerations.
In the upcoming issues, I'll tackle resources, DLLs, device contexts, and making drawing much easier.
[1] In particular, whenever you want to add a tooltip to a control, you need to intercept all its mouse events and relay them to a tooltip_ctrl control.
[2] Internally, a smart pointer to this object is kept. Thus, the event handler (that is, the C++ instance) is automatically destroyed when its reference count is zero.
[3] For example, say a window has seven event handlers. While handling, say, WM_MOUSEMOVE event, it calls each event handler. While executing the third, a new event handler is added. The remaining four event handlers will not be called.
[4] Be sure to check out Microsoft Spy++. It's a very useful tool.