Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

CRoundButton2 - A fancy graphical button

4.84/5 (58 votes)
29 Sep 2005CPOL9 min read 1   19.4K  
A self drawing, round button for different styles and usages.

Image 1

Introduction

I needed a button for a skinnable user-interface, where the user could change the visual aspects. My first idea, a button that is skinned by bitmaps, was a little unhandy for my interface, so I searched for a button that could draw itself using an internal formula and some user-given parameters. A long story told short, I found no class that fitted my needs, so I started coding.

My first attempt was a disaster ;-). An application using about 15 buttons started in 10 secs - because the buttons needed the time to be drawn. The second attempt was much better. The time critical graphics are drawn once in a universal way and are then used by the buttons to draw their own images. With this structure it was also possible to generate a global button-style for the complete project.

Introducing the used classes and structs

CRoundButton2 needs two classes: "CRoundButton2" and "CRoundButtonStyle". Each button needs an object of type "CRoundButton2". This class represents the button and its functionality. Only one object (OK, OK, one per used button-style) is needed of type "CRoundButtonStyle". This object controls the style of all buttons using it. Let's start with it...

CRoundButtonStyle

CRoundButtonStyle has some public functions, but the two that are of interest to us besides the constructor and the destructor are:

  • GetButtonStyle(tButtonStyle* _ptButtonStyle)

    which is used to fill a tButtonStyle structure with the current set style.

  • SetButtonStyle(tButtonStyle* _ptButtonStyle)

    which is used to set the current style to the one given in the tButtonStyle structure.

Both functions need a pointer to an object of type tButtonStyle.

tButtonStyle

tButtonStyle is a structure containing all changeable options of the style. The image shows the main parameters of the button-style:

Image 2

The button's geometry is calculated by the shown function.

The parameters are given in detail:

  • double m_dSizeAA

    The size in pixels of the used Anti-Aliasing zone to make the image smooth.

  • double m_dRadius

    The radius of a button-edge. If the button is too small for the selected radius, it is scaled down, but be careful, the scaling routine is not very good. If you have to use very small buttons along with big ones, use a second button-scale which is different only in this parameter.

  • double m_dBorderRatio

    The ratio (between 0.0 and 1.0) between the border and the radius. If you are given a radius of 16.0 and m_dBorderRatio is 0.25, the button's border would be 4.0 pixels wide.

  • double m_dHeightBorder

    The color of the border is calculated by the function y=x^2, with this height. You have to experiment a little to find a good value for your button.

  • double m_dHeightButton

    The color of the button is calculated by the function y=x^2, with this height. You have to experiment a little to find a good value for your button.

  • double m_dHighLightX

    The position of the button's highlight in X-direction. Use this and the next value to change the 3D-effect.

  • double m_dHighLightY

    The position of the button's highlight in X-direction. Use this and the previous value to change the 3D-effect.

  • double m_dRadiusHighLight

    The radius of the used highlight. You have to experiment a little to find a good looking value.

  • double m_dPowerHighLight

    The power of the used highlight. More of this and the highlighted area turns to white ;-)

  • tColorScheme m_tColorBack

    This is the color for the button's background. Because the button doesn't draw itself transparent, you can give the color of your dialog face here, so the button looks round rather than rectangular.

  • tColorScheme m_tColorBorder

    This is the color shown at the highest point of the button's border.

  • tColorScheme m_tColorFace

    This is the color shown on the button's face, the area in the middle of the button.

tColorScheme

The coloring of the button is done by this structure, which includes colors for all different states of the button.

These are given in detail here:

  • COLORREF m_tDisabled

    This color is used if the button is disabled.

  • COLORREF m_tEnabled

    This color is used if the button is enabled, but not clicked.

  • COLORREF m_tClicked

    This color is used if the button is clicked. This means checked, if the button is a checkbox or selected if the button is a radio button.

  • COLORREF m_tPressed

    This color is used if the button is pressed. This means, the mouse is over the button and the left mouse button is clicked.

  • COLORREF m_tHot

    This color is used if the button is a hot-button and the mouse pointer is hovering over the button.

So each button uses an object of class....

CRoundButton2

This class includes some Set and Get routines which are needed to control the behavior of the button. These are given in detail here:

  • bool SetRoundButtonStyle(CRoundButtonStyle* _ptRoundButtonStyle)

    This routine sets a pointer in the button object to the global button-style object. With this, you can control which button uses which button-style.

  • bool GetFont(LOGFONT* _ptLogFont)

    You have to provide the address of an object of type LOGFONT, which is filled by the function with the actual set font-data. LOGFONT is the structure used by MFC's CreateFontIndirect.

  • bool SetFont(LOGFONT* _ptLogFont)

    You have to provide the address of an object of type LOGFONT, which is used by the function to set the actual font of the button. LOGFONT is the structure used by MFC's CreateFontIndirect.

  • bool GetTextColor(tColorScheme* _ptTextColor)

    This routine fills the object at _ptTextColor with the current set color of the button's text. This color is given as tColorScheme, so you can use a different color for a disabled button, or you can mark a checked button by another font color.

  • bool GetTextColor(tColorScheme* _ptTextColor)

    This routine sets the font color of the button to the values given in _pttextColor. This color is given as tColorScheme, so you can use a different color for a disabled button, or you can mark a checked button by another font color.

  • void SetCheckButton(bool _bCheckButton)

    Changes the status of the button to check button or back. If _bCheckbutton is true, the button is a check button. With each mouse click, the checked status is changed.

  • bool GetCheckButton()

    Returns true if the button is a check button, otherwise false.

  • void SetRadioButton(bool _bRadioButton)

    Changes the status of the button to radio button or back. If _bRadiobutton is true, the button is a radio button. Each mouse click will check a radio-button. The un-checking of other radio buttons of the group has to be done manually (for now). (See an example on this later.)

  • bool GetRadioButton()

    Returns true if the button is a radio button, otherwise false.

  • void SetHotButton(bool _bHotButton)

    Changes the status of the button to hot-button or back. If _bRadiobutton is true, the button is a hot-button. If the mouse hovers over the button's face, the button will change its appearance to the hot-button style so it is more noticeable.

  • bool GetHotButton()

    Returns true if the button is a hot-button, otherwise false.

  • void SetCheck(bool _bIsChecked)

    Checks or unchecks the button.

  • bool GetCheck()

    Get the actual check-status of the button. true means the button is checked.

Using the code

First of all, you have to include these four files in your project:

  • RoundButton2.h
  • RoundButton2.cpp
  • RoundButtonStyle.h
  • RoundButtonStyle.cpp

The easiest way to use the code is to generate variables for each used button. The first step to do this: go to your dialog's header-file and include:

.
.
.
#include "..."

#include "RoundButton2.h"
#include "RoundButtonStyle.h"

class CMyDialog : public CDialog
{
.
.
.

The next step would be to go to the dialog-editor. First we need a button-style to use. Right-click onto the dialog-title and select "Add variable...". In the dialog, change the variable type to "CRoundButtonStyle" and give a name to the variable. This example uses m_tMyButtonStyle. Press "OK" and your first global button-style is generated.

Now, we need a variable for each used button. To get them, follow these steps for each button:

Right-click on the button you want to change and select "Add variable...". In the upcoming dialog, select your variable-name (this example uses m_tMyButton1 and so on) and change the variable-type to CRoundButton2. Press "OK" and the button's variable is included.

The last step needed is to associate the style with the buttons. Go to your dialog's OnInitDialog() routine and set the following line for each button:

BOOL CMyDialog::OnInitDialog()
{
    .
    .
    .
    All other initialization
    
    m_tMyButton1.SetRoundButtonStyle(&m_tMyButtonStyle);
    m_tMyButton2.SetRoundButtonStyle(&m_tMyButtonStyle);
    m_tMyButton3.SetRoundButtonStyle(&m_tMyButtonStyle);
    m_tMyButton4.SetRoundButtonStyle(&m_tMyButtonStyle);
    m_tMyButton5.SetRoundButtonStyle(&m_tMyButtonStyle);
    
    return TRUE
}

Now you can use your button like any other button from MFC.

Checkbox

If you want to use your button as a checkbox, you simply have to call...

BOOL CMyDialog::OnInitDialog()
{
    .
    .
    .
    All other initialization
    
    m_tMyButton1.SetCheckButton(true);

    return TRUE
}

...in your OnInitDialog() routine. The button will change the check-status with each click or you can change the status yourself with a call to void SetCheck(bool). You can get the actual check-status by calling bool GetCheck().

Radio button

A radio button is a little more complicated to implement. Let's pretend, for this example, that you want to use "MyButton2" to "MyButton4" as a radio button group. First, you have to set the button's status to "radio button" by using the following code:

BOOL CMyDialog::OnInitDialog()
{
    .
    .
    .
    All other initialization
    
    m_tMyButton2.SetRadioButton(true);
    m_tMyButton3.SetRadioButton(true);
    m_tMyButton4.SetRadioButton(true);

    return TRUE
}

A radio button is "checked" each time, the user clicks on it. You have to uncheck the unselected buttons of a radio button group by yourself. To do this, implement the OnBnClicked() function for each button of the radio button group. These functions include the following code:

void CMyDialog::OnBnClickedMyButton2()
{
    // Uncheck the two other Buttons
    m_tMyButton3.SetCheck(false);
    m_tMyButton4.SetCheck(false);
}

void CMyDialog::OnBnClickedMyButton3()
{
    // Uncheck the two other Buttons
    m_tMyButton2.SetCheck(false);
    m_tMyButton4.SetCheck(false);
}

void CMyDialog::OnBnClickedMyButton4()
{
    // Uncheck the two other Buttons
    m_tMyButton2.SetCheck(false);
    m_tMyButton3.SetCheck(false);
}

This will uncheck all the buttons of your radio button group that were not clicked.

Hot Button

To make a button hot, you have to call...

BOOL CMyDialog::OnInitDialog()
{
    .
    .
    .
    All other initialization
    
    m_tMyButton5.SetHotButton(true);

    return TRUE
}

... in your OnInitDialog() routine. A hot button will change its appearance if the mouse cursor hovers over the button

Redesign of the button

You have two possible parts in redesign. The first is the...

ButtonStyle

To redesign the style of the button you have to follow this simple concept:

// Generate local struct-object for style-data
tButtonStyle tStyle;

// Get the current style of your button-style-object
m_tMyButtonStyle.GetRoundButtonStyle(&tStyle);

// Do your changes here. As example lets change the radius 
// of the button and the color of the checked button face:
tStyle.m_dRadius = 16.0;
tStyle.m_tColorFace.m_tChecked = RGB(0xFF, 0xFF, 0);

// Save the style back
m_tMyButtonStyle.SetRoundButtonStyle(&tStyle);

Now, you have changed the button style, so all the buttons using this style will have a changed look.

The second style to change is the...

FontStyle

The FontStyle is a part of the CRoundButton2 class, because it's more often needed to change the font of a button than its graphical details. Think for example of a button with a symbol on it, like the tall thin button in the demo application. It uses a double arrow of "WingDings". This example shows the changing of both the font and the font-color of a button's caption.

// Generate local font object
LOGFONT tFont;

// Get the current font of your button-object
m_tMyButton1.GetFont(&tFont);

// Do your changes here. As example lets change the 
// font to "WingDings" and the font-size to 14:
strcpy(tFont.lfFaceName,"WingDings");
tFont.lfHeight = 14;

// Save the Font back
m_tMyButton1.SetFont(&tStyle);

// Generate local ColorSheme object
tColorScheme tColor;

// Get current Color of Caption
m_tMyButton1.GetFontColor(&tColor);

// Do your changes here. As example lets change the 
// captions' color of a checked button to yellow:
tColor.m_tChecked = RGB(0xFF, 0xFF, 0);

// Set Color of Caption
m_tMyButton1.SetFontColor(&tColor);

History

  • 12.09.2005 - Initial release.
  • 29.09.2005 - Added HotButton-function (Thanks to AndrewSmirnov) and fixed two bugs (Thanks to Thomas Kayser and Bhalvinder).

License

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