by Kent Reisdorph
If you're not using the ObjectWindows
Library (OWL) to create your Windows applications, you
should be. Here are three quick tips that you can use to make
sure you're getting the most out of OWL.
A good Windows application follows certain conventions. When a menu item is not available or not applicable for the current situation, it should be grayed out. This prevents the user from clicking on a menu item and wondering why nothing is happening.
In an OWL application, you can create a TMenu object for your menu and do all sorts of nifty things. But you don't often need all the power of TMenu.
The TCommandEnabler class offers a simple and elegant solution for basic menu manipulation. You need only add a response table entry, EV_COMMAND_ENABLE, for the menu item you want to manipulate and then provide a simple function to do the enabling. OWL takes care of calling the enabler function for you.
In addition to enabling and disabling menu items, the TCommandEnabler class also allows you to set a check mark on or remove it from the menu item, and to change the menu text. The most commonly used TCommandEnabler functions are Enable(), SetCheck(), and SetText().
You nearly always see a command enabler used with an application's Save command, for example. You don't want to enable the Save option if there is no data to save, or if the current data is unchanged. You'd typically use a boolean value to determine whether the file needs to be saved. (By the way, the OWL Doc/View architecture automatically handles enabling and disabling the standard commands using the technique we're describing.) The designated value will be zero if the file doesn't need saving and non-zero if it does.
Part of your response table would look like this:
EV_COMMAND(CM_FILESAVE, CmFileSave), EV_COMMAND_ENABLE(CM_FILESAVE, FileSaveEnabler)
Note that we use the same ID in both entries. Next, you'll
need to define the FileSaveEnabler() function:
void MyWindow::FileSaveEnabler(TCommandEnabler& ce) { ce.Enable(isFileDirty); }
That's it! Now the Save menu item will automatically be enabled when isFileDirty is True and disabled when isFileDirty is False.
To set a check mark or to change the text, you need only call
the appropriate TCommandEnabler functions. You can see
that command enablers are both easy to use and effective!
You're probably aware that OWL provides fly-by status bar hints nearly automatically if you use the TControlBar and TStatusBar classes in your applications. OWL accomplishes this task by determining what control the cursor is over, loading a string resource with the same ID as the control, and displaying that string in the status bar.
This process works like a charm, but at times you may need to accomplish the same type of action to add some flexibility that OWL doesn't provide. In order to do this you'll need to determine what control the cursor is over. EvSetCursor() provides you with all the information you need.
The function signature of EvSetCursor() looks like this:
bool EvSetCursor(HWND hWndCursor, uint hitTest, uint mouseMsg)
As you can see, you get a lot of information in the EvSetCursor() function: You get the handle of the window that the cursor is over (remember, all controls are windows), the hitTest code, and the mouse message. Using this information, you can do a lot of things!
For example, let's say you want hint text for buttons in
a dialog box. Assuming that you had a pointer to a static control
to display the text, called Hint, you could use the following:
bool TMainDlg::EvSetCursor(HWND hwnd, UINT, UINT msg) { if (msg == WM_MOUSEMOVE) { if (hwnd == *this) { Hint->SetText(""); } else { char text[50]; int id = ::GetDlgCtrlID(hwnd); HINSTANCE hInstance = GetModule()->GetInstance(); LoadString(hInstance, id, text, sizeof(text)); Hint->SetText(text); } } return false; }
This code sets the text to blank if the cursor is positioned over the dialog box itself. If the cursor isn't over the dialog box, then we first get the control's ID based on its HWND. Next, we load a string resource with the same ID as the control. Finally, we display the string by setting the text of the static control.
In reality, this code would include some modifications to prevent
flicker in the static control, but you get the idea. As you can
see, EvSetCursor() can be useful for actions other than
just setting the cursor.
Frequently, you'll need to access members of one class
from within another class. For instance, if your application has
a control bar, your control bar will typically be set up at the
application level, as a member of your TApplication derived
class. If you try to get at the control bar using
GetApplication()->cb->Insert( . . . );
you'll get a compiler error telling you that cb is not a member of TApplication. This error appears because the GetApplication() function returns a TApplication pointer, and your control bar isn't a member of TApplication but rather a member of your class derived from TApplication.
To get at the control bar, you'll need to cast the pointer
returned from GetApplication() to a pointer of your derived
class. C++ provides an operator called dynamic_cast
to cast from a base class to a derived class. In our example,
the cast would look like this:
MyApp* app = dynamic_cast<MyApp*>(GetApplication()); if (app) app->cb->Insert( . . . );
Note that we test the pointer before we use it. If the case fails, dynamic_cast will then return NULL.
In addition to the obvious, dynamic_cast is also useful
for other operations. For instance, you could use dynamic_cast
to determine the type of a derived class, given the base class
pointer:
Fruit* fruit = dynamic_cast<Fruit*>(ptrObject); if (dynamic_cast<Apple*>(fruit)) // fruit must be an Apple fruit->MakeApplePie(); else if (dynamic_cast<Cherry*>(fruit)) // fruit must be a Cherry fruit->MakeCherryPie();
We've stepped through just one of the many uses for dynamic_cast.
With this basic introduction, you can now experiment to find other
ways to use it in your applications.
Copyright (c) 1996 The Cobb Group, a division of Ziff-Davis Publishing Company. All rights reserved. Reproduction in whole or in part in any form or medium without express written permission of Ziff-Davis Publishing Company is prohibited. The Cobb Group and The Cobb Group logo are trademarks of Ziff-Davis Publishing Company.