• No results found

Radio Button & Check Box

Although check box, radio button and push button look very differently from one another, they are essentially same type of controls. All of them can handle

command messages, and their status can be set using the same function. The only difference among them is how they behave after being clicked by mouse. For a push button, after being clicked, it will go to checked state, as the mouse

releases, it will automatically return to its normal state; for a check box, it toggles between checked and unchecked states after being clicked (it does not respond to mouse button release events); for a radio button, checking any button in a group will uncheck the previously checked button, so that at any time, only one button within a group could be checked.

On a tool bar, implementing normal check box and radio button does not make much sense, so they are not included as default features. If we want to add these controls, we can use dialog bar rather than tool bar. However, if we want the features of radio button and check box, we can use normal push buttons to imitate the behaviors of two types of controls.

The key of letting a push button behave like radio button and check box is to find a way of setting the states of buttons contained in the tool bar. In MFC, the states of tool bar buttons are managed in the same way with that of menu items. We can set a button’s state by trapping user-interface update command message (UPDATE_COMMAND_UI), then calling member functions of class CCmdUI to change a button’s state. In order to map this massage to a member function, we need to use ON_UPDATE_COMMAND_UI macro. The following is the format of this message mapping:

ON_UPDATE_COMMAND_UI(command ID, member function name)

The format of the corresponding member function is:

afx_msg void FunctionName(CCmdUI *pCmdUI);

The function has only one parameter, which is the pointer to a CCmdUI object.

Class CCmdUI handles user-interface updating for tool bar buttons and menu items. Some most commonly used member functions are listed in the following table:

Function Usage

CCmdUI::Enable(…) Enable or disable a control

CCmdUI::SetCheck(…) Set or remove the check state of a control

CCmdUI::SetRadio(…) Set check state of a control, remove check state of all other controls in the group

From time to time, the operating system will send user-interface update

command messages to the application, if there exists macros implementing the above-mentioned message mapping for any control contained in the tool bar, the control’s state can be set within the corresponding message handler.

For a concrete example, if we want to disable ID_BUTTON_RED button under certain situations, we can declare a member function OnUpdateButtonRed(…) in class CBarDoc

as follows (of course, we can also handle this message in other three classes):

class CBarDoc : public CDocument {

……

protected:

afx_msg void OnUpdateButtonRed(CCmdUI *pCmdUI);

……

};

The message mapping can be implemented in file "BarDoc.cpp":

BEGIN_MESSAGE_MAP(CBarDoc, CDocument)

ON_UPDATE_COMMAND_UI(ID_BUTTON_RED, OnUpdateButtonRed) END_MESSAGE_MAP()

The member function can be implemented as follows:

void CBarDoc::OnUpdateButtonRed(CCmdUI *pCmdUI) {

if(under certain situations)pCmdUI->Enable(FALSE);

else pCmdUI->Enable(TRUE);

}

Usually we use a Boolean type variable as the flag, which represents "certain situations" in the above if statement. We can toggle this flag in other functions, this will cause the button state to be changed automatically. By doing this, the button’s state is synchronized with the variable.

To set check state for a button, all we need to do is calling function

CCmdUI::SetCheck(…) instead of CCmdUI::Enable(…) in the message handler.

Sample

Sample 1.2\Bar demonstrates how to make the four color buttons behave like radio buttons. At any time, one and only one button will be set to checked state (it will recess and give the user an impression that it is being held down).

To implement this feature, a member variable m_uCurrentBtn is declared in class

CBarDoc. The value of this variable could be set to any ID of the four buttons in the member functions (other values are not allowed). In the user-interface update command message handler of each button, we check if the value of m_nCurrentBtn

is the same with the corresponding button’s ID. If so, we need to set check for this button, otherwise, we remove its check.

The following lists the steps of how to implement these message handlers:

1. Open file "BarDoc.h", declare a protected member variable m_uCurrentBtn in class CBarDoc:

class CBarDoc : public CDocument

{

……

protected:

UINT m_uCurrentBtn;

……

};

2. Go to file "BarDoc.cpp", in CBarDoc’s constructor, initialize m_uCurrentBtn red button’s resource ID:

CBarDoc::CBarDoc() {

m_uCurrentBtn=ID_BUTTON_RED;

}

This step is necessary because we want one of the buttons to be checked at the beginning.

3. Implement UPDATE_COMMAND_UI message mapping for four button IDs. This is almost the same with adding ON_COMMAND macros, which could be done through using Class Wizard. The only difference between two

implementations is that they select different message types from "Message"

window (see step 5 previous section). Here we should select

"UPDATE_COMMAND_UI" instead of "COMMAND".

1. Implement the four message handlers as follows:

void CBarDoc::OnUpdateButtonBlue(CCmdUI* pCmdUI) {

pCmdUI->SetCheck(pCmdUI->m_nID == m_uCurrentBtn);

}

void CBarDoc::OnUpdateButtonGreen(CCmdUI* pCmdUI) {

pCmdUI->SetRadio(pCmdUI->m_nID == m_uCurrentBtn);

}

void CBarDoc::OnUpdateButtonRed(CCmdUI* pCmdUI) {

pCmdUI->SetRadio(pCmdUI->m_nID == m_uCurrentBtn);

}

void CBarDoc::OnUpdateButtonYellow(CCmdUI* pCmdUI) {

pCmdUI->SetRadio(pCmdUI->m_nID == m_uCurrentBtn);

}

One thing to mention here is that CCmdUI has a public member variable m_nID, which stores the ID of the control that is about to be updated. We can compare it with variable CBarDoc::m_uCurrentBtn and set the appropriate state of the control.

With the above implementation, the red button will be checked from the beginning. We need to change the value of variable m_uCurrentBtn in order to

check another button. This should happen when the user clicks on any of the four buttons, which will cause the application to receive a WM_COMMAND message. In the sample, this will cause the message handlers CBarDoc::OnButtonRed(),

CBarDoc::OnButtonBlue()… to be called. Within these member functions, we can change the value of m_uCurrentBtn to the coresponding command ID in order to check that button:

void CBarDoc::OnButtonBlue() {

m_uCurrentBtn=ID_BUTTON_BLUE;

}

void CBarDoc::OnButtonGreen()

{

m_uCurrentBtn=ID_BUTTON_GREEN;

}

void CBarDoc::OnButtonRed() {

m_uCurrentBtn=ID_BUTTON_RED;

}

void CBarDoc::OnButtonYellow() {

m_uCurrentBtn=ID_BUTTON_YELLOW;

}

The message box implementation is removed here. By executing the sample application and clicking on any of the four color buttons, we will see that at any time, one and only one button will be in the checked state.

1.3. Check Box Implementation