CTRLTEST Sample: Implements Custom Controls
The CTRLTEST sample illustrates several techniques for implementing and using custom controls:
Implementing CParsedEdit, a specialized edit control deriving functionality from its library control class, and three methods of using custom controls.
Using the spin control. The spin control has small up-arrow and down-arrow buttons for incrementing or decrementing a value.
Bitmap button implementation of Custom menu commands using CBitmapButton.
Owner (parent window) drawing of menus and list boxes. Corresponding control classes deriving from the CMenu class and CListBox provide this feature in an object-oriented way.
Using resource files not maintainable by the Microsoft Visual C++ resource editors. This illustrates the benefits and drawbacks of using an .rc2 file in a dialog box that has a custom control with styles defined by constants in a header file.
All the illustrations in CTRLTEST are initiated through menu commands.
This sample code is provided to illustrate a concept and should not be used in applications or Web sites, as it may not illustrate the safest coding practices. Microsoft assumes no liability for incidental or consequential damages should the sample code be used for purposes other than as intended.
To get samples and instructions for installing them:
On the Visual Studio Help menu, click Samples.
For more information, see Visual Studio Samples.
The most recent version and complete list of samples is available online from the Visual Studio 2008 Samples page.
You can also locate samples on your computer's hard disk. By default, samples and a Readme file are copied into a folder under \Program Files\Visual Studio 9.0\Samples\. For Express editions of Visual Studio, all samples are located online.
Building and Running the Sample
To build and run the CTRLTEST sample
Open the solution Ctrltest.sln.
On the Build menu, click Build.
On the Debug menu, click Start Without Debugging.
Example: Implementing and Using Custom Controls
You can implement a custom control by deriving from CWnd, but it is much easier if you can borrow functionality from a 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, 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.
Caption blank. This is the initial value shown in the CParsedEdit control.
Class:paredit. This is the WNDCLASS name that is registered by CParsedEdit::RegisterControlClass in PAREDIT2.CPP, before the dialog is called.
Visible:checked. The control is visible.
Tabstop:checked. The user can tab to this control.
Style:0x5081002, 0x5081001, 0x5081003, 0x5081ffff for the four parsed edit controls. The style 0x500000 is for WS_CHILD and WS_VISIBLE, and 0x1000 is for WS_TABSTOP. All custom controls have the WS_CHILD style. The WS_VISIBLE and WS_TABSTOP styles are automatically set by the dialog editor when you check the Visible and Tabstop styles. 0x80000 is for WS_BORDER. Since the dialog editor property page for a custom control does offer all of the window styles such as WS_BORDER, you must look up the constant in \Microsoft Visual Studio .NET 2003\Vc7\PlatformSDK\Include\WINUSER.H. The styles 0x0001, 0x0002, 0x0004, and 0x0ffff are defined in PAREDIT.H as, respectively, PES_NUMBERS, PES_LETTERS, PER_OTHERCHARS, and PES_ALL.
The hexadecimal styles in the custom control property page are not self-documenting. If it is important to you to use the symbolic styles, such as PES_NUMBERS and PES_LETTERS, you can alternatively manually edit a separate resource file, such as RES\Ctrltest.rc2, that is included by the resource compiler at compile time, but is not read by Visual C++ at editing time. For a discussion of the pros and cons of manually editing a custom control dialog in an .rc2 file, see Using resource files not maintainable by the Visual C++ resource editors.
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:
Bitmap Button 1 - The dialog constructor explicitly loads the bitmap resources for each of the three states (up, down, and focus) of the button by calling CBitmapButton::LoadBitmaps.
Bitmap Button 2 - The dialog's OnInitDialog function calls CBitmapButton::Autoload to load the bitmap resources based on the following naming convention. The window text of the control serves as the base resource name, and the letters U, D, and F are appended to create the names of the resources for each of the three bitmap images for, respectively, up, down, and focus. For example, the three bitmap resources for the OK button are named OKU, OKD, and OKF.
Bitmap Button 3 - The dialog box is an extension of the second dialog box above, using a fourth possible button state, disabled. To use this dialog, click the left- or right-arrow bitmap button until the displayed number reaches 1, the lowest number, or 10, the highest number. When the limit is reached, the button is disabled and a fourth bitmap image is displayed. The naming convention for the bitmap resource for the disabled state is an X suffix, as is reflected in the resource names PREVX and NEXTX.
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:
Custom Menu -This menu item calls a CColorMenu pop-up menu, derived from CMenu. Each submenu item displays one of eight colors using the self-drawing feature. A message box confirms the color you select from the submenu.
Custom List Box - This menu item calls a dialog box that displays a CColorListBox, derived from CListBox. The list box has eight entries, each drawn in one of eight colors using the self-drawing feature. TRACE output confirms your selection from the list box.
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:
Custom control styles symbols - For example, "msctls_updown32" is a style defined for the spin control. Although Visual C++ can interpret this symbol as it reads in the .rc2 file, Visual C++ would write it back out to the .rc2 file as a hexadecimal value.
Standard Windows WS_ or control style symbols used in a control from a standard Windows control-derived class - For example, ES_AUTOHSCROLL is defined for the spin control in the IDD_SPIN_EDIT dialog. Although Visual C++ can interpret these symbols as it reads in the .rc2 file, Visual C++ would write it back out to the .rc2 files as a hexadecimal value.
Arithmetic in the .rc file - Expressions such as "IDC_EDIT1+2" to identify controls in the IDD_SPIN_EDIT dialog would be written back out to the .rc2 file as a single hexadecimal value by Visual C++.
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 tradeoff 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::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
Some samples, such as this one, have not been modified to reflect the changes in the Visual C++ wizards, libraries, and compiler, but still demonstrate how to complete your desired task.