Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

Derived Control Classes in Dialog Boxes

You can extend the functional ity of any of the standard control classes by deriving your own classes and then use these extended controls in your dialog box.

This technique can let you subtly (or drastically) modify the behavior of the standard Windows controls. Many third-party software vendors supply libraries full of these extended classes to greatly enhance the control capabilities.

Creating a Derived Control

You can use ClassWizard to create a new class derived from an existing MFC control class by clicking the Add Class button and selecting New to display the New Class dialog box, as shown in Figure 5.7. After entering a name for your new control class, you can set the base class to one of the existing MFC control classes.

05fig07.gif

Figure 5.7 Deriving your own control class.

After you click OK, a new control class appears in your ClassView. This new class just consists of constructor/destructor functions and a message map derived from the existing MFC class.

You don't have to use ClassWizard to generate this derived control class, but it certainly makes things easier, especially when you incorporate the new control in a dialog box, as you'll see later, in the section, "Using the Derived Control in a Dialog Box."

Instead of deriving your custom control from another MFC control class, you can derive it from CWnd directly and write a new control from scratch. This is a fairly laborious job, however, and you probably would find it more beneficial to create the control as an ActiveX control.

Customizing the Derived Control

You change or extend the default functionality of your new derived class by adding message-handler functions to intercept the Windows messages before they are passed to the base class.

You could create a custom Edit control that converts uppercase letters to lowercase letters, for example. You could add a handler to catch the WM_CHAR messages and change any uppercase characters to lowercase, like this:

void CCustomEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if (nChar>='A' && nChar<='Z')
        nChar+=32; // Make it lowercase

    DefWindowProc(WM_CHAR,nChar,MAKELONG(nRepCnt,nFlags));
}

If you examine the source code for the MFC control base classes, you'll notice that most call a CWnd function called Default(). This function just calls the DefWindowProc() function passing the details of the last message sent to the window. The DefWindowProc() function then implements the default processing for the Windows message.

Instead of calling the base class CEdit::OnChar() function, the preceding example calls DefWindowProc(), directly passing the modified nChar variable. You'll notice that a corresponding ON_WM_CHAR message-handler macro was added to your derived control's message map, like this:

BEGIN_MESSAGE_MAP(CCustomEdit, CEdit)
    //{{AFX_MSG_MAP(CCustomEdit)
    ON_WM_CHAR()
    //}}AFX_MSG_MAP
END_MESSAGE_MAP()

This macro is responsible for separating the nRepCnt and nFlags word values from the original LPARAM parameter sent in the WM_CHAR message. The MAKELONG macro merely recombines these values back into a single LPARAM parameter for the DefWindowProc() function.

You now have a simple customized CEdit-derived class that converts all uppercase characters to lowercase. Obviously, this is quite a simple customization, but by catching other Windows messages in this way, you can customize the standard controls beyond recognition.

Using the Derived Control in a Dialog Box

After you derive a custom control, you can use it in a dialog box as a mapped member variable (as you would any standard MFC control). If you used ClassWizard to create the derived control class, you'll find that your new class is available from the list of control variable types in the Add Member Variable dialog box, Figure 5.8 shows.

05fig08.gif

Figure 5.8 Mapping your derived class to a dialog box control.

After clicking OK to map the new member variable to your derived class, you'll notice that ClassWizard adds the new member variable to the dialog box's class definition as normal:

CCustomEdit    m_ceditCustomEdit;

ClassWizard prompts you with a message box to remind you that you must manually add an #include for the custom control's class definition before the new member variable declaration. If your new derived class is defined in the CustomEdit.h module, for ex ample, you should add the #include before your custom dialog box definition:

#include "CustomEdit.h"

You now should find that when your new dialog box is displayed, the behavior of your new customized control is changed accordingly.

You may not always want to use (or be able to) ClassWizard to map your derived control class to a particular control. If not, you should manually add the member variable declaration for your derived class into the dialog box class definition. You then can call SubclassDlgItem() from OnInitDialog() to hook your derived control class's message map into the specific Windows control.

The term subclassing in this circumstance is different from the object-oriented subclass term and really means use this class to handle the Windows messages for this control. This subclassing normally is performed during the first pass of DoDataExchange() from inside a DDX_Control() routine. You must pass the control ID and a valid parent dialog box pointer to SubclassDlgItem().

To manually subclass the new custom edit box (m_ceditCustomEdit), for example, your OnInitDialog() function may look like this:

BOOL CCustomDlg::OnInitDialog()
{
    CDialog::OnInitDialog();

    m_ceditCustomEdit.SubclassDlgItem(IDC_CUSTOM_EDIT,this);

    return TRUE;
}

If the subclassing succeeds, SubclassDlgItem() returns TRUE. If you already know the HWND handle of the control, you can call SubclassWindow() from the dialog box instead, passing the control's window handle. SubclassWindow() also returns TRUE if the subclassing was successful.

You can add an override for the PreSubclassWindow() virtual function in your derived control class. Your PreSubclassWindow() override is called just before the control's messages are hooked into your derived class's message map. This lets you perform dynamic changes to the subclassing procedure or just some last-minute initialization of your new control-handler class.

You can call UnsubclassWindow() to make the control revert to using the original default (CWnd) handler object.

Share ThisShare This

Informit Network