Custom Image Button

shabbir's Avatar author of Custom Image Button
This is an article on Custom Image Button in C++.
Rated 5.00 By 2 users

Steps to integrate code into your programs



This is an easy and flexible way to use bitmaps as buttons in your application, and here are the detail steps of how you can add the custom button to your application.
  1. Create a new MFC AppWizard (exe) based project and name it as BtnSample



  2. Select dialog based and click on Finish



  3. You will see the following dialog and click on OK button



  4. After some progress bars you will see the following project created for you.



  5. Now Add a button to the Dialog Application from the Control Toolbox



  6. Now Copy the GuiButton.h and GuiButton.cpp files from the attachment into your project location and Add the files to the workspace.



  7. Add a variable to the button using the class wizard and keep the variable type as CButton



  8. Open BtnSampleDlg.h and change the variable type you have created in the above step from CButton to CGuiButton and add the
    Code: Cpp
    #include "GuiButton.h"
    at the top of the file.
  9. Now copy the bitmap you would like to use for your button and add import the bitmap resource into the project



  10. Finally add the following line in the OnInitDialog of your dialog in the file BtnSampleDlg.cpp. Remember to skip the About Dialog classes.
    Code: Cpp
    m_btn.SetSkin(IDB_BITMAP1);
  11. If you have successfully done all the steps try compiling and runing the program and you should see


What is it in the code ...



This was all about running the program and integrating the GuiButton into your project but what is there in the GuiButton I will try to explain that as well.
First the SetSkin function.
Code: Cpp
/////////////////////////////////////////////////////////////////////////
/// <b>Function: SetSkin</b>
///
/// \param  uiNormal  (in) Normal Bitmap ID
///
/// \param  uiDisabled    (in) Disabled Bitmap ID
///
/// \param  clrTextColor  (in) Text Color where default color is white
///
/// \return void
///
/// \remarks    Sets the skin to the button
///
/////////////////////////////////////////////////////////////////////////
void CGuiButton::SetSkin(UINT uiNormal, UINT uiDisabled, COLORREF clrTextColor)
{
    TRACE(TEXT("CGuiButton::SetSkin\n"));
    // --------------------------------------
    // Free previous allocated bitmaps
    // --------------------------------------
    m_NormalBitmapDC.DeleteObject();
    m_DisabledBitmapDC.DeleteObject();

    // --------------------------------------
    // Load bitmaps corresponding to states
    // --------------------------------------
    if (uiNormal>0)
        m_NormalBitmapDC.LoadBitmap(uiNormal);
    if (uiDisabled>0)
        m_DisabledBitmapDC.LoadBitmap(uiDisabled);

    m_TextColor = clrTextColor;
}
I hope the code is pretty much self explanatory.

Now some of the overridden methods and what they do.
Code: Cpp
/////////////////////////////////////////////////////////////////////////
/// <b>Function: PreSubclassWindow</b>
///
/// \param  NONE
///
/// \return void
///
/// \remarks    PreSubclassWindow to make the button is Owner Draw.
///
/////////////////////////////////////////////////////////////////////////
void CGuiButton::PreSubclassWindow()
{
    CButton::PreSubclassWindow();

    // Modifying the style to OwnerDraw
    ModifyStyle(0, BS_OWNERDRAW );

}


/////////////////////////////////////////////////////////////////////////
/// <b>Function: OnEraseBkgnd</b>
///
/// \param  pDC   (in\out)
///
/// \return BOOL
///
/// \remarks    We do not want the base class to erase the background.
///
/////////////////////////////////////////////////////////////////////////
BOOL CGuiButton::OnEraseBkgnd(CDC* pDC)
{
    // Do not erase background to be transparent
    return true;
}


/////////////////////////////////////////////////////////////////////////
/// <b>Function: OnKillFocus</b>
///
/// \param  pNewWnd   (in)
///
/// \return void
///
/// \remarks    Validates the button so that the Focus rect is removed
///             correctly
///
/////////////////////////////////////////////////////////////////////////
void CGuiButton::OnKillFocus(CWnd* pNewWnd)
{
    this->Invalidate();
}
and each of them is well documented.

Now the main function DrawItem
Code: Cpp
/////////////////////////////////////////////////////////////////////////
/// <b>Function: DrawItem</b>
///
/// \param  lpDrawItemStruct  (in\out)
///
/// \return void
///
/// \remarks    Draws the button in 3 steps
///             1. Bitmap     - Draws the loaded bitmap
///             2. Text       - Draws the button text
///             3. Focus Rect - Draws the focus rect of the button
///
/////////////////////////////////////////////////////////////////////////
void CGuiButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
    ASSERT (lpDrawItemStruct);

    CDC btnDC ;
    btnDC.Attach(lpDrawItemStruct->hDC);    // get device context

    CBitmap* pbitmapDC=NULL;
    BITMAP bmpStruct;

    CRect btnRect;
    GetClientRect(&btnRect);

    // --------------------------------------
    // Draw the bitmap
    // --------------------------------------
   
    // Get the correct Bitmap
    if(! IsWindowEnabled())
        pbitmapDC=&m_DisabledBitmapDC;
    else
        pbitmapDC=&m_NormalBitmapDC;
   
    // Select the bitmap into a Compatible DC
    CDC *bmpDC = new CDC();
    bmpDC->CreateCompatibleDC(&btnDC);
    bmpDC->SelectObject(pbitmapDC);

    if(pbitmapDC != NULL)
    {
        pbitmapDC->GetBitmap(&bmpStruct);
        const int iDiff = 1;
        // Draw the bitmap on the button leaving one pixel from each side of the button
        btnDC.StretchBlt(iDiff, iDiff, btnRect.Width()-(2*iDiff), btnRect.Height()-(2*iDiff),bmpDC,0,0,bmpStruct.bmWidth,bmpStruct.bmHeight, SRCCOPY );
    }

    // --------------------------------------
    // Draw the Text
    // --------------------------------------

    CString sCaption;
    GetWindowText(sCaption);    // get button text

    int iLength = sCaption.GetLength();

    CRect textRect;
    textRect = lpDrawItemStruct->rcItem;

    int       oldMode = btnDC.SetBkMode(TRANSPARENT);
    COLORREF oldColor = btnDC.SetTextColor(m_TextColor);

    CSize sz;
    sz = btnDC.GetTextExtent(sCaption);
 
    BOOL bNoOfLines = FALSE;
    UINT uiDrawTextFormat = DT_CENTER|DT_VCENTER|DT_SINGLELINE;
   
    int iOffset = btnDC.DrawText(sCaption,textRect,uiDrawTextFormat);
   
   
    // --------------------------------------
    // Draw the Focus Rect
    // --------------------------------------
    if( (iLength)  && (lpDrawItemStruct->itemState & ODS_FOCUS) )
    {
        CRect focusRect( btnRect );
        focusRect.InflateRect(-1,-1,-1,-1);
        btnDC.DrawFocusRect(&focusRect);
    }

    btnDC.SetTextColor(oldColor) ;
    btnDC.SetBkMode(oldMode) ;
    btnDC.Detach() ;   
   
}
Its also well explained and here is the quote from the comment
Quote:
Originally Posted by Comment
Draws the button in 3 steps
1. Bitmap - Draws the loaded bitmap
2. Text - Draws the button text
3. Focus Rect - Draws the focus rect of the button
You can download the attached sample and run the program.
Attached Files
File Type: zip GuiButton.zip (2.7 KB, 370 views)
File Type: zip GuiButtonSample.zip (15.5 KB, 337 views)
like this
0
shabbir's Avatar, Join Date: Jul 2004
Go4Expert Founder
I have reported the article for Nominate your favorite article of the month for November 2007. Add your nominations as well.
0
skynetto's Avatar, Join Date: Dec 2007
Newbie Member
any chance of using an external image with LoadImage? SetSkin is expecting an UINT and LoadImage gives me a HANDLE or at least a HBITMAP
0
skynetto's Avatar, Join Date: Dec 2007
Newbie Member
Quote:
Originally Posted by skynetto
any chance of using an external image with LoadImage? SetSkin is expecting an UINT and LoadImage gives me a HANDLE or at least a HBITMAP
OK got just have to modify what setskin is expecting of and change the LoadBitmap reference on guibutton.cpp
0
sampathkambar's Avatar, Join Date: Jan 2008
Light Poster
What if,i want that button to be created dynamically..... what shall i do at that time.
Thanks in advance...
0
shabbir's Avatar, Join Date: Jul 2004
Go4Expert Founder
Just make the object of the Custom button and specify the property as and when needed.
0
CasualProgrammer's Avatar, Join Date: Jul 2009
Newbie Member
Hello -

Is there a later release of this?

In GuiButton.cpp at line 229 (or so), there's:

CDC *bmpDC = new CDC();

But there is no corresponding "delete bmpDC" from that point to the end of the function. This produces a small (what, 16-byte?) memory leak each time the button is pressed.

Casual
0
nasko700's Avatar
Newbie Member
Hi! I find that if you drag the window (where is also your imageButton) out of the monitor's sizes and then drag it back, the image is not there anymore. I think, I have to use serialization, or?
0
sammor's Avatar, Join Date: Aug 2010
Newbie Member
HI,
I have 6 buttons with images,
when I launch the application, no problems,
when I click on some buttons, some Buttons disappear.
I am using it in CE 6.00 environment
Thanks
0
shabbir's Avatar, Join Date: Jul 2004
Go4Expert Founder
Quote:
Originally Posted by sammor View Post
HI,
I have 6 buttons with images,
when I launch the application, no problems,
when I click on some buttons, some Buttons disappear.
I am using it in CE 6.00 environment
Thanks
May be some paint issues.