15.3 Deriving Controls from a Standard Control

As described in the previous section, the Microsoft Foundation control classes provides classes that correspond to the predefined control windows supported by Windows, such as buttons, edit controls, and scroll bars. For the most part, you will be able to use these control classes just as they are defined since their default behavior conforms to the normal user-interface guidelines for Windows programs.

There are some situations, however, where you may want to modify the behavior of a control. You can often modify behavior of a control by deriving a new class from an existing control class, as shown by the following example.

Suppose you wanted to modify the standard edit control, CEdit, so that it only accepted digits. You could call this derived control CNumEdit, since it would be good for user-entered numeric values. In addition to modifying its behavior so that it only accepted digits, you could add member functions to get and set its contents by numeric value, thus encapsulating the conversion between text and numeric values.

There are three steps to creating a derived control class, as listed here:

1.Derive your class from an existing control class and override the Create function so that it provides the necessary arguments to the base class Create function.

2.Provide message-handler functions and message-map entries to modify the control's behavior in response to specific window messages.

3.Provide new member functions to extend the functionality of the control (optional).

These three steps are described in the following three sections.

·To override the Create function class:

1.Override the Create function for your derived class that takes the same arguments as the base class Create and pass the arguments on to the base class and let it do most of the default initialization of the control.

2.Perform other initialization tasks specific to your derived class. For the CNumEdit example, call the LimitText member function to limit the number of characters that the derived control will accept.

class CNumEdit : public CEdit

{

public:

BOOL Create( DWORD dwStyle, const RECT& rect,

CWnd* pParentWnd, UINT nID )

{ if ( ! CEdit::Create( dwStyle, rect, pParentWnd, nID ) )

return FALSE;

LimitText( 16 );

return TRUE;

}

// other class declaration stuff removed...

};

·To use message-handler functions to modify behavior:

1.Define the message-handler function OnChar to examine each incoming character. If the character is a digit, the BACKSPACE key, or the TAB key, call the OnChar function of CEdit to pass it to the base class for normal processing. If the character is not a digit, then don't call the CEdit class's OnChar function and the base class will never see the character. The implementation of the OnChar member function of CNumEdit is as follows.

class CNumEdit : public CEdit

{

public:

BOOL Create(DWORD dwStyle, const RECT& rect,

CWnd* pParentWnd, UINT nID);

// filter incoming characters

afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags );

DECLARE_MESSAGE_MAP()

};

void CNumEdit::OnChar( UINT nChar, UINT nRepCnt, UINT nFlags )

{

if( (( nChar <= '9' ) && ( nChar >= '0' ))

|| ( nChar == VK_TAB )

|| ( nChar == VK_BACK ))

{

CEdit::OnChar( nChar, nRepCnt, nFlags );

}

}

2.Define a message-map entry for the WM_CHAR message:

BEGIN_MESSAGE_MAP( CNumEdit, CEdit )

ON_WM_CHAR()

END_MESSAGE_MAP()

3.Filtering WM_CHAR messages works fine for removing nondigits from keyboard input, but to be complete you would also have to handle the WM_PASTE message to filter nondigits from Clipboard data. A discussion of how to filter Clipboard data is beyond the scope of this section.

·To extend functionality with new member functions:

To be truly useful, the CNumEdit class can provide new functions to get and set the value of the control with numeric values. That way, the code that converts the text to a number can be encapsulated in the derived control. Programmers who use the control can simply get and set its value without worrying about conversion.

class CNumEdit : public CEdit

{

public:

BOOL Create( DWORD dwStyle, const RECT& rect,

CWnd* pParentWnd, UINT nID );

// filter incoming characters

afx_msg void OnChar( UINT nChar, UINT nRepCnt, UINT nFlags );

// provide Get and Set functions to deal with numeric value

LONG GetValue();

void SetValue( LONG v );

DECLARE_MESSAGE_MAP()

};

·To implement GetValue and SetValue:

Use the standard C run-time functions sscanf and sprintf to convert between number and text. Use the CWnd member functions GetWindowText and SetWindowText to access the text shown in the edit control. Possible implementations of these functions are shown below:

LONG CNumEdit::GetValue()

{

char buff[64];

LONG v;

GetWindowText( buff, sizeof( buff ) );

if( sscanf( buff, "%d", &v ) )

{

return v;

}

else

{

return 0;

}

}

void CNumEdit::SetValue( LONG v )

{

char buff[64];

sprintf( buff, "%d", v );

SetWindowText( buff );

}