Window Subclassing for Enhanced Data-Entry Control

What if you want an edit control (in a dialog or a form view) that accepts only numeric characters? That's easy. You just set the Number style in the control's property sheet. If, however, you want to exclude numeric characters or change the case of alphabetic characters, you must do some programming.

The MFC library provides a convenient way to change the behavior of any standard control, including the edit control. Actually, there are several ways. You can derive your own classes from CEdit, CListBox, and so forth (with their own message handler functions) and then create control objects at runtime. Or you can register a special window class, as a Win32 programmer would do, and integrate it into the project's resource file with a text editor. Neither of these methods, however, allows you to use the dialog editor to position controls in the dialog resource.

The easy way to modify a control's behavior is to use the MFC library's window subclassing feature. You use the dialog editor to position a normal control in a dialog resource, and then you write a new C++ class that contains message handlers for the events that you want to handle yourself. Here are the steps for subclassing an edit control:

  1. With the dialog editor, position an edit control in your dialog resource. Assume that it has the child window ID IDC_EDIT1.

  2. Write a new class—for example, CNonNumericEdit—derived from CEdit. Map the WM_CHAR message and write a handler like this:

    void CNonNumericEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
    {
        if (!isdigit(nChar)) {
            CEdit::OnChar(nChar, nRepCnt, nFlags);
        }
    }

  3. In your derived dialog or form view class header, declare a data member of class CNonNumericEdit in this way:

    private:
        CNonNumericEdit m_nonNumericEdit;

  4. If you're working with a dialog class, add the following line to your OnInitDialog override function:

    m_nonNumericEdit.SubclassDlgItem(IDC_EDIT1, this);
  5. If you're working with a form view class, add the following code to your OnInitialUpdate override function:

    if (m_nonNumericEdit.m_hWnd == NULL) { m_nonNumericEdit.SubclassDlgItem(IDC_EDIT1, this); }

The CWnd::SubclassDlgItem member function ensures that all messages are routed through the application framework's message dispatch system before being sent to the control's built-in window procedure. This technique is called dynamic subclassing and is explained in more detail in Technical Note #14 in the online documentation.

The code in the preceding steps only accepts or rejects a character. If you want to change the value of a character, your handler must call CWnd::DefWindowProc, which bypasses some MFC logic that stores parameter values in thread object data members. Here's a sample handler that converts lowercase characters to uppercase:

void CUpperEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags)
{
    if (islower(nChar)) {
        nChar = toupper(nChar);
    }
    DefWindowProc(WM_CHAR, (WPARAM) nChar,
                  (LPARAM) (nRepCnt | (nFlags << 16)));
}

You can also use window subclassing to handle reflected messages, which were mentioned in Chapter 6. If an MFC window class doesn't map a message from one of its child controls, the framework reflects the message back to the control. Technical Note #62 in the online documentation explains the details.

If you need an edit control with a yellow background, for example, you can derive a class CYellowEdit from CEdit and use ClassWizard to map the =WM_CTLCOLOR message in CYellowEdit. (ClassWizard lists the message name with an equal sign in front to indicate that it is reflected.) The handler code, shown below, is substantially the same as the nonreflected WM_CTLCOLOR handler. (Member variable m_hYellowBrush is defined in the control class's constructor.)

HBRUSH CYellowEdit::CtlColor(CDC* pDC, UINT nCtlColor)
{
    pDC->SetBkColor(RGB(255, 255, 0)); // yellow
    return m_hYellowBrush;
}
posted @ 2007-01-03 23:29  shipfi  阅读(472)  评论(0编辑  收藏  举报