Note: The source code for this article has been updated by Georg Schreiber to correct a problem with loading bitmaps and icons outside the main module (eg in a DLL). All changes in the updated source is marked by "GS980730".
Technical BackgroundCSXButton is an owner draw button that takes care of the following:
1. Loading an icon or bitmap from a resource.For an icon, this is done with the often overlooked ::LoadImage function. ::LoadImage adds one necessary feature which the standard CWinApp::LoadIcon does not have. When loading the image from a resource, ::LoadImage will resize the icon to any width and height dimensions that you specify. CWinApp::LoadIcon will only load an icon to the sizes specified in the system metrics, which is usually 32x32. NOTE: The "View" and "Super View" icons above come from one 32x32 icon.
For a bitmap, the image is loaded with ::CreateMappedBitmap. Using this function allows CSXButton to give the bitmaps a transparent quality, mapping one color which you specify, to the system button face color, retrieved from GetSysColor( COLOR_BTNFACE ). If you use a bitmap with a mask, a disabled image is also created mapping the masked color to white, RGB(255, 255, 255). This will allow the windows embossing drawing effect (the effect used to create the 'disabled' look) to give the illusion of ignoring the masked area.
2. Drawing the image in normal and disabled state.
3. Drawing the text in normal and disabled state.Both of these are done by another function that I feel is often overlooked, the CDC::DrawState function. Using this function, CSXButton doesn't have to worry about 'embossing' the disabled icon and text, CDC::DrawState is simply told that the item being drawn is disabled and it takes care of the 'embossing'. CDC::DrawState also supports the drawing of the mnemonic character which is the underlined character used as the accelerator shortcut for the button. Not all text drawing functions support this.
4. Drawing the special 'default button'.One unfortunate consequence of using an owner draw button is that you can no longer make an owner draw button the 'default button'. This is why CSXButton has a member function SetDefaultButton(). This function does two things. It will tell CSXButton to draw the extra default button border, and it will use the CDialog::SetDefID() function to tell the dialog that owns the button to make it the default item on the dialog.
5. Drawing the focus rectangle.The one and only DrawFocusRect() is used here.
How to use itTo use CSXButton, follow the steps below:
1. Add the source files to your project.
2. Create a button on your dialog, and make sure you turn on the "Owner Draw" property as shown below:
3. Create the button member variable.
Option 1: Add a member variable for the button in the ClassWizard, and specify the variable type to be CSXButton as shown below. NOTE: Since you did not create the CSXButton class through the ClassWizard, you will have to rebuild the .clw ClassWizard file for your project. For instructions on how to do this, search the VC++ documentation for "Rebuilding the ClassWizard (.CLW) File".
Option 2: Create you own member variable in your dialog of type CSXButton, and subclass the variable. For example:
// In Dialog Class Header File CSXButton m_brock; // In Dialog OnInitDialog() m_brock.SubclassDlgItem( IDC_BUTTON_ROCK, this /*parent* );
4. Call any one of the following functions to attach an image to the button, and note that the nWidth and nHeight do NOT have to be the actual size of the image, the image will be scaled to the size you specify. You can only use bitmaps with 16-colors or less, and icons with 256 colors or less. Use caution on the images you select because the colors may not be displayed correctly on machines that do not support the color palette in your images.
CSXButton::SetIcon( nID, nWidth, nHeight ); CSXButton::SetBitmap( nID, nWidth, nHeight ); CSXButton::SetMaskedBitmap( nID, nWidth, nHeight, crTransparentMask );
m_brock.SetIcon( IDI_ROCK, 40, 150 ); m_bhelp.SetIcon( IDI_HELP, 16, 16 ); m_brock.SetBitmap( IDB_ROCK, 40, 150 ); m_brock.SetBitmap( IDB_ROCK, 12, 67 ); // Use the bitmap IDB_ROCK, and replace any instance of // RGB( 255, 0, 0 ) (bright red), with the system // color of the button face. m_brock.SetMaskedBitmap( IDB_ROCK, 40, 150, RGB( 255, 0, 0 ) );
5. If it is the default button, call CSXButton::SetDefaultButton() to make it the default button. If you need to de-activate, or switch default buttons, call CSXButton::SetDefaultButton( FALSE );
m_bok.SetDefaultButton( FALSE ); // Deactivate m_brock.SetDefaultButton(); // Activate
6. Set the positioning of the image and text. Default positioning is the image offset 4 pixels from the left border, and the text is offset 8 pixels from the right of the image. Both image and text are centered vertically. The buttons in the above sample, except for "This Space For Rent", use this default positioning. You can change these offsets with CSXButton::SetImageOffset( int nPixelsFromLeftofBorder ), and CSXButton::SetTextOffset( nPixelsFromLeftOfImage ).
For added flexibility, you can use CSXButton::SetTextPos( CPoint p), and CSXButton::SetImagePos( CPoint p ), with (0,0) being the upper left corner of the button. You can also use the #define SXBUTTTON_CENTER for either the x or y in the CPoint to automatically center the text or image. The "This Space For Rent" button in the above example uses this method.
m_bplanet.SetIcon( IDI_PLANET, 32, 32 ); m_bplanet.SetImagePos( CPoint( SXBUTTON_CENTER, 6 ) ); m_bplanet.SetTextPos( CPoint( SXBUTTON_CENTER, 42 ) );
EnhancementsThe following is a list of some items that could be added to the CSXButton class, as I am sure there are many more. If you would like to add these enhancements, or any others you can think of, submit them to this site so others can share from your work. And as always, please try your hardest to maintain compatibility with any previous versions of CSXButton.
1. multi-line text.
2. Use of default image sizes.
3. State images like CBitmapButton.
Special thanks to Mark Findlay originally asking for such a button in the CodeGuru's message area, and for later on becoming the guinea pig.
- Fixed bug in drawing of focus rectangle.
- Added resource cleanup.
Last updated: 12 August 1998