Click here to Skip to main content
15,867,330 members
Articles / Desktop Programming / MFC
Article

CxSkinButton

Rate me:
Please Sign up or sign in to vote.
4.91/5 (54 votes)
9 Jul 2008CPOL 580.9K   15.4K   166   88
A skinned button class with clipping region, tooltip and mouse tracking

Sample Image - sample.gif

With this class you can easily give a skin to your buttons in few steps:

  1. Add xSkinButton.cpp and xSkinButton.h to the project.
  2. Include xSkinButton.h in the header file where the controls are defined
  3. Create (or edit) a member variable for each button you want to customize as CxSkinButton. If the Class Wizard doesn't show the CxSkinButton type, select CButton and then edit the code manually.
  4. Add the bitmap resource for the buttons:
    • NORMAL bitmap: default button image.
    • DOWN bitmap: pushed button image.
    • OVER bitmap:(optional) image to shown when the mouse is over the button. If NULL, NORMAL bitmap will be used.
    • DISABLED bitmap: (optional) image for the disabled state. If NULL, NORMAL bitmap will be used.
    • FOCUS bitmap: (optional) image for the focused state. If NULL, NORMAL bitmap will be used.
    • MASK bitmap: (optional) clipping region. If you don't use the MASK, the button will be a rectangular owner-draw control. The default transparent color is RGB(255,255,255).
  5. In the window initialization add the CxSkinButton methods:
    C++
    BOOL CxSkinButtonDemoDlg::OnInitDialog()
    {
    //    ...
        m_btn1.SetTextColor(RGB(255,0,0));
        m_btn1.SetToolTipText("Button1");
        m_btn1.SetSkin(IDB_B1NORM,IDB_B1DOWN,IDB_B1OVER,IDB_B1GRAY,
                           0,IDB_B1MASK,0,0,0);
    //    ...

    The SetTextColor and SetToolTipText are self explaining; SetSkin is:

    C++
    void CxSkinButton::SetSkin(UINT normal,UINT down,UINT over, 
                               UINT disabled, UINT focus,UINT mask, 
                               short drawmode, short border, 
                               short margin)
    • normal, down, over, disabled, focus, mask = bitmap resource IDs
    • drawmode = if mask is not NULL, this should be 0 (normal); else you can try 1 (stretch) or 2 (tiled).
    • border = if mask is not NULL or is not rectangular, this should be 0; else you can try 1 to draw the standard 3D border around the button.
    • margin = if mask is not NULL or is not rectangular, this should be 0; else you can try to change this value to draw a dotted rectangle over the button when the control has the focus.

Remarks

  • The BS_OWNERDRAW style is added automatically, you don't need to set the "Owner draw" property in the resource editor.
  • only NORMAL and DOWN bitmaps are essential; OVER bitmap is a plus.
  • DISABLED bitmap is not necessary if the button is always enabled. You can also use the button text (automatically etched) to show the disabled state.
  • MASK bitmap is not necessary if the button is rectangular.
  • for rectangular buttons you should set the margin parameter greater than 0 (4 is a good choice), or use the FOCUS bitmap.
  • for rectangular buttons you can use 'flat' bitmaps and set the border parameter to 1. In this situation, NORMAL and DOWN bitmaps can also share the same resource.

Release History

v1.00 - 15/03/2001
- basic implementation and interface.
v1.10 - 28/03/2001
enhanced mouse tracking implementation (by Milan Gardian).
- SetCapture removed: accelerators now are dispatched.
- Double-click handling.
- Better behavior during "button-pressed" (mouse button down & holding) phase
v1.20 - 02/04/2001
- New CreateRgnFromBitmap() implementation. Mask bitmap now works in any display color mode, any mask color depth and any mask size.
v1.21 - 14/04/2001
- OnMouseLeave cast error fixed.
- Fixed FocusRect defect when OVER bitmap was NULL.
v1.30 - 25/04/2001
- Fixed CreateRgnFromBitmap failure under WinNT4.0
v1.40 - 29/06/2001
- check & radio button add on.
- added "focus" bitmap.
- fixed CreateRgnFromBitmap bug.
- fixed shortcut bug.
- fixed text drawing code.
v1.41 - 27/10/2001
- Fixed memory leakage in CreateRgnFromBitmap
v1.50 - 07/07/2008
- fixed memory leaks using SelectObject and GetDC (thanks to sachelis and Bernd Giesen)
- added SetToolTipColor (thanks to Mykel)

Compatibility

Win95,WinNT = Yes, requires IE3.0 or higher
Win98, WinME, W2k, WinXP = Yes

Thanks to all the Code Project developers!
Special thanks to:
Milan Gardian for mouse and keyboard tracking code.
Fable(at)aramszu(dot)net for ExtCreateRegion replacement code.
Rainer Mangold for radio-button and check-box code.
Andy Green and Cameron Epp for suggestions & debugging.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Italy Italy
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralRe: CToolTipCtrl has a possible issue Pin
Bernd Giesen6-Apr-04 2:13
Bernd Giesen6-Apr-04 2:13 
GeneralRe: CToolTipCtrl has a possible issue Pin
scully126-Apr-04 2:19
scully126-Apr-04 2:19 
GeneralRe: CToolTipCtrl has a possible issue Pin
eastar092117-Aug-04 19:32
eastar092117-Aug-04 19:32 
GeneralRe: CToolTipCtrl has a possible issue Pin
VH_DEV16-Feb-22 4:58
VH_DEV16-Feb-22 4:58 
GeneralSet tooltip color Pin
sps-itsec4616-Jun-03 2:13
sps-itsec4616-Jun-03 2:13 
GeneralRe: Set tooltip color Pin
Davide Pizzolato16-Jun-03 9:24
Davide Pizzolato16-Jun-03 9:24 
GeneralRe: Set tooltip color Pin
sps-itsec4617-Jun-03 9:11
sps-itsec4617-Jun-03 9:11 
GeneralMultiple line support added Pin
robkent2-Jun-03 3:44
robkent2-Jun-03 3:44 
Hi,

I've added a multiple line support for scinned buttons. The method CxSkinButton::DrawItem looks
now like follows:

void CxSkinButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
ASSERT (lpDrawItemStruct);
//TRACE("* Captured: %08X\n", ::GetCapture());

//Check if the button state in not in inconsistent mode...
POINT mouse_position;
if ((m_button_down) && (::GetCapture() == m_hWnd) && (::GetCursorPos(&mouse_position))){
if (::WindowFromPoint(mouse_position) == m_hWnd){
if ((GetState() & BST_PUSHED) != BST_PUSHED) {
//TRACE("* Inconsistency up detected! Fixing.\n");
SetState(TRUE);
return;
}
} else {
if ((GetState() & BST_PUSHED) == BST_PUSHED) {
//TRACE("* Inconsistency up detected! Fixing.\n");
SetState(FALSE);
return;
}
}
}

CString sCaption;
CDC *pDC = CDC::FromHandle(lpDrawItemStruct->hDC); // get device context
RECT r=lpDrawItemStruct->rcItem; // context rectangle
int cx = r.right - r.left ; // get width
int cy = r.bottom - r.top ; // get height
// get text box position
RECT tr={r.left+m_FocusRectMargin+2,r.top,r.right-m_FocusRectMargin-2,r.bottom};

//R.K. 07.03.03 New values for multi-line support
RECT rectsave = tr;
int hsize;
int iret;

GetWindowText(sCaption); // get button text
pDC->SetBkMode(TRANSPARENT);

// Select the correct skin
if (lpDrawItemStruct->itemState & ODS_DISABLED){ // DISABLED BUTTON
if(m_bDisabled.m_hObject==NULL)
// no skin selected for disabled state -> standard button
pDC->FillSolidRect(&r,GetSysColor(COLOR_BTNFACE));
else // paint the skin
DrawBitmap(pDC,(HBITMAP)m_bDisabled,r,m_DrawMode);
// if needed, draw the standard 3D rectangular border
if (m_Border) pDC->DrawEdge(&r,EDGE_RAISED,BF_RECT);
// paint the etched button text
pDC->SetTextColor(GetSysColor(COLOR_3DHILIGHT));

//RK pDC->DrawText(sCaption,&tr,DT_SINGLELINE|DT_VCENTER|DT_CENTER);
// R.K. 07.03.03 Calculate new values for multi-line support

hsize = tr.bottom - tr.top; // Check maxsize of Rect
// Calc hsize of multiline text
iret = pDC->DrawText(sCaption,&tr, DT_WORDBREAK|DT_CALCRECT|DT_CENTER);
tr = rectsave;

tr.top = (hsize - iret) / 2;
pDC->DrawText(sCaption,&tr, DT_WORDBREAK|DT_CENTER);

pDC->SetTextColor(GetSysColor(COLOR_GRAYTEXT));
OffsetRect(&tr,-1,-1);
//RK pDC->DrawText(sCaption,&tr,DT_SINGLELINE|DT_VCENTER|DT_CENTER);

hsize = tr.bottom - tr.top;
iret = pDC->DrawText(sCaption,&tr, DT_WORDBREAK|DT_CALCRECT|DT_CENTER);
tr = rectsave;

tr.top = (hsize - iret) / 2;
pDC->DrawText(sCaption,&tr, DT_WORDBREAK|DT_CENTER);

} else {
// SELECTED (DOWN) BUTTON
if ((lpDrawItemStruct->itemState & ODS_SELECTED)){
if(m_bDown.m_hObject==NULL)
// no skin selected for selected state -> standard button
pDC->FillSolidRect(&r,GetSysColor(COLOR_BTNFACE));
else { // paint the skin
DrawBitmap(pDC,(HBITMAP)m_bDown,r,m_DrawMode);
}
OffsetRect(&tr,1,1); //shift text
// if needed, draw the standard 3D rectangular border
if (m_Border) pDC->DrawEdge(&r,EDGE_SUNKEN,BF_RECT);
} else {
// DEFAULT BUTTON
if(m_bNormal.m_hObject==NULL)
// no skin selected for normal state -> standard button
pDC->FillSolidRect(&r,GetSysColor(COLOR_BTNFACE));
else // paint the skin
if ((m_tracking)&&(m_bOver.m_hObject!=NULL)){
DrawBitmap(pDC,(HBITMAP)m_bOver,r,m_DrawMode);
} else {
DrawBitmap(pDC,(HBITMAP)m_bNormal,r,m_DrawMode);
}
// if needed, draw the standard 3D rectangular border
if (m_Border) pDC->DrawEdge(&r,EDGE_RAISED,BF_RECT);
}
// paint the focus rect
if ((lpDrawItemStruct->itemState & ODS_FOCUS)&&(m_FocusRectMargin>0)){
r.left += m_FocusRectMargin ;
r.top += m_FocusRectMargin ;
r.right -= m_FocusRectMargin ;
r.bottom -= m_FocusRectMargin ;
DrawFocusRect (lpDrawItemStruct->hDC, &r) ;
}
// paint the enabled button text
pDC->SetTextColor(m_TextColor);
//RK pDC->DrawText(sCaption,&tr,DT_SINGLELINE|DT_VCENTER|DT_CENTER);


int hsize = tr.bottom - tr.top;
int iret = pDC->DrawText(sCaption,&tr, DT_WORDBREAK|DT_CALCRECT|DT_CENTER);
tr = rectsave;

tr.top = (hsize - iret) / 2;
pDC->DrawText(sCaption,&tr, DT_WORDBREAK|DT_CENTER);

}
}

Hope this is helpful.

Robert
GeneralRe: Multiple line support added Pin
Peggy Van Lierde11-Jun-03 4:00
Peggy Van Lierde11-Jun-03 4:00 
Generalenabling WS_CLIPCHILDREN causes bad drawing of masked buttons Pin
ohadp21-May-03 21:05
ohadp21-May-03 21:05 
GeneralAdding a popup menu which belongs to the button caused error. Pin
Sky.net4-Mar-03 16:54
Sky.net4-Mar-03 16:54 
GeneralSelectClipRgn error Pin
wangtao5826-Feb-03 19:53
wangtao5826-Feb-03 19:53 
QuestionHow can I use a bitmap bigger than that used in your code Pin
Member 1549349-Jan-03 16:40
Member 1549349-Jan-03 16:40 
GeneralMemory leak in DrawBitmap Pin
Anonymous12-Oct-02 7:39
Anonymous12-Oct-02 7:39 
GeneralRe: Memory leak in DrawBitmap Pin
Davide Pizzolato12-Oct-02 20:47
Davide Pizzolato12-Oct-02 20:47 
GeneralCxSkinButton type doesn't shows to chose member variable Pin
18-Jun-02 8:23
suss18-Jun-02 8:23 
GeneralRe: CxSkinButton type doesn't shows to chose member variable Pin
Davide Pizzolato18-Jun-02 9:43
Davide Pizzolato18-Jun-02 9:43 
GeneralRe: CxSkinButton type doesn't shows to chose member variable Pin
18-Jun-02 20:50
suss18-Jun-02 20:50 
GeneralRe: CxSkinButton type doesn't shows to chose member variable Pin
Davide Pizzolato19-Jun-02 8:33
Davide Pizzolato19-Jun-02 8:33 
GeneralRe: CxSkinButton type doesn't shows to chose member variable Pin
23-Jun-02 3:45
suss23-Jun-02 3:45 
GeneralUsing with ON_UPDATE_COMMAND_UI() Pin
Dana Holt4-Jun-02 15:50
Dana Holt4-Jun-02 15:50 
Questionwho can explain this function to me? Pin
24-Mar-02 17:44
suss24-Mar-02 17:44 
GeneralOther problem Pin
17-Dec-01 17:13
suss17-Dec-01 17:13 
GeneralRe: Other problem Pin
Davide Pizzolato19-Dec-01 6:52
Davide Pizzolato19-Dec-01 6:52 
GeneralAnother correction Pin
19-Nov-01 5:25
suss19-Nov-01 5:25 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.