Where do TCommandEnabler objects come from?

Back Up

In the accompanying article, Enabling and disabling commands in an ObjectWindows application we show you how to enable and disable menu commands with the ObjectWindows Library (OWL) version 2.0. To enable or disable commands this way, you have to provide a command-enabler function that accepts a reference to a TCommandEnabler object as its only parameter.

If you place your command-enabler function in a message-response table, OWL will call this function when it needs to know the enabled/disabled status of the function's corresponding command. When OWL calls your command-enabler function, OWL passes the address of an object derived from the TCommandEnabler class that will perform the actual enabling or disabling of the command control for you.

Origin of a TCommandEnabler object

OWL derives two classes from the TCommandEnabler class for you: the TMenuItemEnabler class and the TButtonGadgetEnabler class. Both classes overload the Enable(), SetText(), and SetCheck() member functions to set the appropriate visual state for the corresponding TMenu or TButtonGadget object. Since the behavior of these two classes is the same in all other ways, we'll look only at the TMenuItemEnabler class.

Enabling and disabling TMenu commands

When an OWL application receives the message EV_WM_INITMENUPOPUP from Windows, the application calls the TFrameWindow or TFrameWindow-derived class's EvInitMenuPopup() member function to handle the message. In turn, this function calls the TFrameWindow class's EvCommandEnable() function to begin searching for corresponding command-enabler functions for each command in the menu. (Page 420 of the ObjectWindows 2.0 Reference Guide contains a diagram that illustrates how the TWindow class's DefaultProcessing() function processes messages. The TFrameWindow class inherits this public member function.)

If you've provided an appropriate command-enabler function for a given command ID, the EvCommandEnable() function will eventually locate and call your command-enabler function and pass it the current TMenuItemEnabler object. When your command-enabler function calls the Enable() member function of the TMenuItemEnabler object with TRUE or FALSE, the TMenuItemEnabler object sets the menu item's MF_GRAYED flag to the appropriate value by using the standard Windows function EnableMenuItem().

The command-enabler chain

You don't have to put all your command-enabler functions in your application class. You can put command-enabler functions for individual commands in separate window classes. This is particularly useful when you implement certain commands only when a specific type of client window is open.

When OWL is about to display a command object, the EvCommandEnable() function searches the classes of the objects in the current view hierarchy, or chain, beginning with the command object's parent window. The EvCommandEnable() function continues searching the class of each object in the chain until it finds a command-enabler function or until it reaches the end of the chain (the application object).

For example, when an OWL application is about to display a menu in a client window, OWL first searches in the client window object's class for command-enabler functions for the menu commands. If there isn't a corresponding handler for a command in the client window object's class, OWL will search the class of the frame window object and then finally the class of the application object.

If OWL finds a command-enabler function for a given command, it stops searching the command-enabler chain. However, if OWL doesn't find an appropriate command-enabler by the time it finishes searching the application object's class, it will disable the corresponding command automatically.

This means that OWL will always disable your new menu commands unless you provide a command-enabler function for them. This explains why you can't use the EnableMenuItem() API function to enable a menu item directly. As soon as the function begins displaying the menu, OWL creates a TMenuItemEnabler object whose default behavior disables menu commands.

Conclusion

It may seem strange to create command-enabler functions that use a TCommandEnabler object you don't need to create yourself. However, by doing so, you allow OWL to take the responsibility for calling your command-enabler functions when necessary.