Click here to Skip to main content
Click here to Skip to main content

Making Cool Button using Images

By , 8 Jun 2010
 

Updated: June 8, 2010  

I know you did not love me the first time when I wrote this article because of my non-nativity and non-familiarity with English language and ON_MESSAGE macro implementation which was causing a crash in release mode. I have updated this article and hope you will like it this time. I have added the Source Files and Sample Projects for download separately and the solution in Sample Project is converted to Visual Studio 2008. 

Introduction 

There is a bunch of articles regarding creating cool buttons on CP and Internet but if you are looking for a 10-minute solution to a problem without having to go through nice, huge and fancy articles, this one is for you. 

The way it works is, it assumes that each button has three states, which  are 1) Normal, 2) Hover and 3) Down. 

Normal: When a button is visible to the user on the screen and mouse has not entered the button region. 

Hover: When a button has lit up for the user who has moved the mouse pointer over it.

Down: This is the state when the button is pushed (or clicked), or when the left mouse button is pressed and not release yet. 

 

For each state of a button, it requires an image from the resource and all three images have to be the same size. 

Here is the snapshot of how the three states will look like. 

Snapshot 

Sample screenshot


How to use? 

Create a button on your dialog and set it to be Owner-Drawn. As most of you may know it already that this could be set either from the dialog resource editor or it could be set in run-time. 

There is a Class CCoolButton, derived from CButton, You need to create a member of CCoolButton type for each button in your Dialog class, and use DDX_Control in DoDataExchange to subclass.

Following is the only constructor you may use:

 CCoolButton(int nIDNormal, int nIDHover, int nIDDown);

The three arguments are resource IDs, you should specify the bitmap resource ids. .

The Default constructor is private, you must specify the three bitmaps from resources when creating object.

 

10-minute implementation 

Step 1: Download the Source Files from the link on top.
Step 2: Add Fcool.h and Fcool.cpp to your project. 
Step 3: For each button
               a) Add three (Normal, Hover and Down) images unless some buttons share the look.
               b) Add a member of CCoolButton in your CDialog-derived class
               c) Change your buttons to Owner-Drawn.
               d) In DoDataExchange(), DDX_Control your button resource id with the member object of CCoolButton in your CDialog-derived class.
Step 4: Tell your boss that your work for the week is done :) 

 

How does it works?

All it does it is, it traps the three 1) When the mouse pointer has not entered the region, 2) Mouse pointer has entered the region and 3) Mouse pointer is in the button region and user has pressed the left button. 

In constructor, bitmap has been loaded from resource to three CBitmap object aggregated by CCoolButtons.

_TrackMouseEvent() is being used to trap the event when the mouse pointer leaves the button region, I called it "OnLeave" , and defined in msg map, using OnMessage.

When users moves the mouse pointer in the button region, the status of the button changes to Hover and the Custom (overridden) DrawItem invokes, so we draw Hover Image from m_bmpHover member. and it calls _TrackMouseEvent() to try to see if the pointer has left the region or not. 

Following is code for OnMouseMove,

void CCoolButton::OnMouseMove(UINT nFlags, CPoint point)
{
 CRect ButtonRect;
 TRACKMOUSEEVENT tme;
 
 GetWindowRect(&ButtonRect);
 if (m_Status == DOWN) 
 {
  if (IsInRect(ButtonRect,point))
  {
 
  }
  else
  {
   SetStatus(HOVER);
   Invalidate(FALSE);
  }
 }
 else if (m_PrevStatus == DOWN && m_Status == HOVER)
 {
  SetStatus(DOWN);
 }
 else
 {
  if (IsInRect(ButtonRect,point))
  {
   SetStatus(HOVER);
   Invalidate(FALSE);
  }
  else
  {
   SetStatus(NORMAL);
  }
 }
 CButton::OnMouseMove(nFlags, point);
 tme.cbSize = sizeof(TRACKMOUSEEVENT);
 tme.dwFlags = TME_LEAVE;
 tme.hwndTrack = m_hWnd; _TrackMouseEvent(&tme);
 }

Normally, in windows if you look at the normal button, when you press the mouse left button down over it and without releasing move the pointer out of the region, it doesnt spare the button's soul, although it draws the normal picture again but when you move the pointer back in the region (with the left button pressed) it draws the "down" image again and if you release the button while the pointer is in the region, it sends the command. To mimic this behavior I have used the help of SetCapture() and ReleaseCapture() in OnLButtonDown() and OnLButtonUp() respectively. 

Following is DrawItem.

void CCoolButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct)
{
 CDC *pthisDC;
 CDC bmpDC;
 CBitmap *pOldBitmap;
 pthisDC = CDC::FromHandle(lpDrawItemStruct->hDC);
 bmpDC.CreateCompatibleDC(pthisDC);
 switch (m_Status)
 {
 case NORMAL:
  pOldBitmap = bmpDC.SelectObject(&m_bmpNormal);
  break;
 case HOVER:
  pOldBitmap = bmpDC.SelectObject(&m_bmpHover);
   break;
 case DOWN:
  pOldBitmap = bmpDC.SelectObject(&m_bmpDown);

  break;
 }
 pthisDC->BitBlt(0,0,lpDrawItemStruct->rcItem.right-lpDrawItemStruct->rcItem.left,
 lpDrawItemStruct->rcItem.bottom - lpDrawItemStruct->rcItem.top,&bmpDC,0,0,SRCCOPY);
 bmpDC.SelectObject(pOldBitmap);
 bmpDC.DeleteDC();
 
}

Here we go, your button is ready. I would like to know your feedback on this article 

Alright! :) 

I know its not performance efficient and it could be improved in a lots of ways and I have not done the "disable" button state yet. But again this is for those who are looking for a 10 minutes solution to turn all buttons on their forms to cool buttons. 

License

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

About the Author

FahadAsh
Systems Engineer
United States United States
Member
My name is Fahad Ashfaque, I work for a financial company in USA as Senior Software Engineer. I have done MS in computer science. My hobbies are Enjoying the Nature, Finding new ways to living green, Swimming, Reading news, Researching on stuff etc. My hotmail account is not active. Please use CP to send me a message.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalbug fixsussJae-in, Kim7 Dec '04 - 14:25 
Your custom message handlers are probably wrong. This happens all the time, and always results in a scenario that works under the debug stack and fails in release.   When ever you use the ON_MESSAGE macro, the message handler must be declared as:   afx_msg LRESULT...
Generalresizing the button.sussAnonymous21 Jul '04 - 9:59 
Hello, How would I resize this button at runtime along with "EASY-SIZE" found her in code project? thanks sk
GeneralFile not foundmemberFilomela11 May '04 - 20:59 
The file '/useritems/Cool_Buttons/CoolButton.zip' doesn't exist.
Generalfix bugssussstevphen(china)17 Mar '04 - 10:11 
When use release mode,some bugs appear: nafxcw.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex nafxcw.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex Release/coolbutton.exe : fatal error LNK1120: 2 unresolved externals Error executing...
GeneralRe: fix bugsmembervite10 Jun '04 - 16:20 
Thank you!
GeneralRe: fix bugssussAnonymous13 Jul '04 - 2:31 
Merci!!!!!!!!!!!!!
GeneralUnvisible buttons images ! (at release mode)membermuhon2 Nov '03 - 21:20 
m_bmpNormal.LoadBitmap(nIDNorm...
Generalbag fixmembernetcfan29 Sep '03 - 2:47 
.h //afx_msg void OnMouseLeave(); LRESULT OnMouseLeave(WPARAM,LPARAM); .cpp //void CCoolButton::OnMouseLeave() //{ // SetStatus(NORMAL); // Invalidate(); //} LRESULT CCoolButton::OnMouseLeave(WPARAM,LPARAM) { SetStatus(NORMAL); Invalidate(); return 0; }     rrr
Generalthere is a error in releasememberletaoqin28 Sep '03 - 22:10 
in release it cant show in nomal, and have a error when exit
GeneralSetCapture/ReleaseCapturememberDavide Pizzolato22 Sep '03 - 1:12 
Article: In this event I have called SetCapture(), and in OnLButtonUp i wrote ReleaseCapture()   It's not a good idea, look the implementation in other custom buttons with _TrackMouseEvent
GeneralCButtonSTmemberDavide Calabro22 Sep '03 - 0:37 
http://www.codeproject.com/but...
QuestionIllustration ?memberStlan21 Sep '03 - 23:20 
Some illustration(s) (screenshot of sample app. for instance) would be welcome in your article. This greatly improves the visual quality of articles and help readers to see what you will are talking about...
GeneralSource code pagememberbruno leclerc21 Sep '03 - 20:43 
Source code page --> can't be found
GeneralRe: Source code pagememberFahadAsh21 Sep '03 - 22:06 
hi, Thanks for telling me. Check again now.   Regards Fahad   All mistakes done in life are experiences, and that should not be ignored

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130516.1 | Last Updated 8 Jun 2010
Article Copyright 2003 by FahadAsh
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid