CTRLTEST: Implements Custom Controls

Click to open or copy the CTRLTEST project files.

CTRLTEST illustrates several techniques for implementing and using custom controls:

All the illustrations in CTRLTEST are initiated through menu commands.

Example: Implementing and Using Custom Controls

You can implement a custom control from scratch by deriving from CWnd, but it is much easier if you can borrow functionality from one of the standard Windows controls by deriving from its control class in the library. CTRLTEST does this to implement a specialized edit control, CParsedEdit. This edit control accepts only specified sets of characters as user input: numeric, alphabetic, and/or noncontrol characters. CParsedEdit is derived from CEdit. It has an OnChar message handler to filter the characters.

The implementation of the commands in the Simple menu illustrates three methods of using a custom control. The methods are distinguished according to how the application associates instances of the control in the dialog box with the CParsedEdit class. Each Simple menu command displays a dialog box with four instances of the CParsedEdit control. Data entered in the dialog box is sent to the debug port as TRACE output. The three Simple menu commands are:

Test C++ Derived Class

The CParsedEdit controls are data members of the dialog class. The controls are explicitly created in the dialog's OnInitDialog function by calling CParsedEdit::CreateSet. See Dertest.cpp.

Test WNDCLASS Registered

The CParsedEdit controls are laid out in a dialog template resource (IDD_WNDCLASS_EDIT) as custom controls with a WNDCLASS identified as "paredit." It is instructive to examine the properties of these custom controls using the Visual C++ dialog editor.

Test Dynamic Subclassed

The controls are laid out in a dialog template resource (IDD_SUB_EDIT in Ctrltest.rc) as standard edit controls. The controls are declared as CParsedEdit data members in the dialog class. The dialog's OnInitDialog function calls CParsedEdit::SubClassEdit, which in turn calls CWnd::SubclassDlgItem, to associate each specific instance of the edit control with the CParsedEdit class. See Paredit.cpp.

Example: Spin Control

The CTRLTEST sample includes an implementation of a spin control. The spin control has a small up-arrow and a small down-arrow button for incrementing or decrementing a value.

The Spin Control command calls a dialog box that has four CParsedEdit controls, each associated with a spin control. Data entered in the CParsedEdit controls in this dialog box is filtered to accept only nonnegative integers. The user can enter numeric data by either typing the value into the CParsedEdit control or using the associated spin control.

Example: Bitmap Button

The implementation of the following Custom menu commands illustrates ways to use CBitmapButton:

Example: Owner Drawing (Menu and List Box)

Various Windows controls and menus have an owner draw feature that lets the parent (or owner) window draw whatever it wants in the client area of the control instead of the standard control behavior. The corresponding control classes and CMenu class provide this feature in a more convenient, object-oriented way: The control or menu class handles the drawing. This is called "self drawing."

CTRLTEST illustrates the general technique of owner drawing in implementing the following commands in the Custom menu:

Example: Using Resource Files Not Maintainable by Visual C++ Resource Editors

The resource file CTRLTEST\RES\Ctrltest.rc2 is an example of a resource file not maintainable by the Visual C++ resource editors in a human-readable form. If you were to open Ctrltest.rc2 in Visual C++, and then save it, you would lose useful human-readable information, even though the Resource Compiler would still be able to compile the .rc2 file and produce an equivalent binary .res file. Thus, RES\Ctrltest.rc2 has been added as a #include in Ctrltest.rc with a Compile-Time Directive specified with the Resource File Set Includes command.

The following are three categories of human-readable information that are not maintainable by the Visual C++ resource editors. Two of these are demonstrated in Ctrltest.rc2:

The CTRLTEST sample illustrates the pros and cons of using an .rc2 file in the case of a dialog that has a custom control with styles defined with constants in a header file. Both dialogs IDD_WNDCLASS_EDIT and IDD_SPIN_EDIT have custom controls with symbolically defined styles; but IDD_WNDCLASS is specified in a .rc file editable by the Visual C++ dialog editor, whereas IDD_SPIN_EDIT is specified in a .rc2 file that is only manually editable.

The differences between using the .rc file and the .rc2 file can be summarized as follows:

For the IDD_WNDCLASS_EDIT dialog, the resource script is defined in Ctrltest.rc. For the IDD_SPIN_EDIT dialog, the resource script is defined in RES\Ctrltest.rc2. For the IDD_WNDCLASS_EDIT dialog, the WNDCLASS custom control is "paredit", the style constants are defined in PAREDIT.H and an example style constant is PES_NUMBER. IDD_WNDCLASS_EDIT is editable by Visual C++ but cannot use #define styles. IDD_SPIN_EDIT is not editable by Visual C++ but can use #define styles.

The trade-off is that if you use the .rc2 file, you can use human-readable symbolic styles defined in the header file for the custom control, but you cannot edit the .rc2 file with the Visual C++ dialog editor. It is easier to lay out the dialog using Visual C++ than it is to manually write resource script; and writing resource script is more error prone. On the other hand, the styles are not self-documenting when displayed in hexadecimal in the custom control property page by the Visual C++ dialog editor.

This sample demonstrates the following keywords:

AfxGetInstanceHandle; AfxMessageBox; AfxThrowResourceException; CBitmapButton::AutoLoad; CDC::FillRect; CDC::FrameRect; CDialog::DoModal; CDialog::EndDialog; CDialog::OnInitDialog; CDialog::OnOK; CDialog::OnSetFont; CEdit::Create; CEdit::SetSel; CFrameWnd::Create; CListBox::AddString; CListBox::CompareItem; CListBox::DrawItem; CListBox::GetItemData; CListBox::MeasureItem; CMenu::AppendMenu; CMenu::CreateMenu; CMenu::Detach; CMenu::DrawItem; CMenu::EnableMenuItem; CMenu::FromHandle; CMenu::GetMenuString; CMenu::MeasureItem; CRect::Width; CStatic::Create; CString::Format; CString::LoadString; CWinApp::Enable3dControls; CWinApp::InitInstance; CWnd::Attach; CWnd::EnableWindow; CWnd::FromHandle; CWnd::GetDlgCtrlID; CWnd::GetDlgItem; CWnd::GetDlgItemInt; CWnd::GetMenu; CWnd::GetParent; CWnd::GetWindowRect; CWnd::GetWindowText; CWnd::IsWindowEnabled; CWnd::MessageBox; CWnd::OnChar; CWnd::OnCommand; CWnd::OnVScroll; CWnd::PostNcDestroy; CWnd::SendMessage; CWnd::SetDlgItemInt; CWnd::SetFocus; CWnd::SetFont; CWnd::SetWindowPos; CWnd::ShowWindow; CWnd::SubclassDlgItem; CallWindowProc; GetBValue; GetClassInfo; GetGValue; GetRValue; GetSystemMetrics; HIWORD; IsCharAlpha; IsCharAlphaNumeric; LOWORD; MAKEINTRESOURCE; MAKELONG; MessageBeep; ModifyMenu; RGB; RegisterClass; SetWindowLong