The EX06A program required little coding for a lot of functionality. Now we'll make a new version of this program that uses some hand-coding to add extra features. We'll eliminate EX06A's rude habit of dumping the user in response to a press of the Enter key, and we'll hook up the scroll bar controls.
Taking Control of the OnOK Exit
In the original EX06A program, the CDialog::OnOK virtual function handled the OK button, which triggered data exchange and the exit from the dialog. Pressing the Enter key happens to have the same effect, and that might or might not be what you want. If the user presses Enter while in the Name edit control, for example, the dialog closes immediately.
What's going on here? When the user presses Enter, Windows looks to see which pushbutton has the input focus, as indicated on the screen by a dotted rectangle. If no button has the focus, Windows looks for the default pushbutton that the program or the resource specifies. (The default pushbutton has a thicker border.) If the dialog has no default button, the virtual OnOK function is called, even if the dialog does not contain an OK button.
You can disable the Enter key by writing a do-nothing CEx06aDialog::OnOK function and adding the exit code to a new function that responds to clicking the OK button. Here are the steps:
void CEx06aDialog::OnClickedOk() { TRACE("CEx06aDialog::OnClickedOk\n"); CDialog::OnOK(); }
void CEx06aDialog::OnOK() { // dummy OnOK function -- do NOT call CDialog::OnOK() TRACE("CEx06aDialog::OnOK\n"); }
For Win32 ProgrammersDialog controls send WM_ COMMAND notification messages to their parent dialogs. For a single button click, for example, the bottom 16 bits of wParam contain the button ID, the top 16 bits of wParam contain the BN_CLICKED notification code, and lParam contains the button handle. Most window procedure functions process these notification messages with a nested switch statement. MFC "flattens out" the message processing logic by "promoting" control notification messages to the same level as other Windows messages.
For a Delete button (for example), ClassWizard generates notification message map entries similar to these:
ON_BN_CLICKED(IDC_DELETE, OnDeleteClicked) ON_BN_DOUBLECLICKED(IDC_DELETE, OnDeleteDblClicked)Button events are special because they generate command messages if your dialog class doesn't have notification handlers like the ones above. As Chapter 13 explains, the application framework "routes" these command messages to various objects in your application. You could also map the control notifications with a more generic ON_ COMMAND message-handling entry like this:
ON_COMMAND(IDC_DELETE, OnDelete)In this case, the OnDelete function is unable to distinguish between a single click and a double click, but that's no problem because few Windows-based programs utilize double clicks for buttons.
Just as pressing the Enter key triggers a call to OnOK, pressing the Esc key triggers a call to OnCancel, which results in an exit from the dialog with a DoModal return code of IDCANCEL. EX06A does no special processing
for IDCANCEL; therefore, pressing the Esc key (or clicking the Close
button) closes the dialog. You can circumvent this process by substituting a
dummy OnCancel function, following approximately the same procedure you used for the OK button.
Hooking Up the Scroll Bar Controls
The dialog editor allows you to include scroll bar controls in your dialog, and ClassWizard lets you add integer data members. You must add code to make the Loyalty and Reliability scroll bars work.
Scroll bar controls have position and range values that can be read and written. If you set the range to (0, 100), for example, a corresponding data member with a value of 50 positions the scroll box at the center of the bar. (The function CScrollBar::SetScrollPos also sets the scroll box position.) The scroll bars send the WM_ HSCROLL and WM_ VSCROLL messages to the dialog when the user drags the scroll box or clicks the arrows. The dialog's message handlers must decode these messages and position the scroll box accordingly.
Each control you've seen so far has had its own individual message handler function. Scroll bar controls are different because all horizontal scroll bars in a dialog are tied to a single WM_HSCROLL message handler and all vertical scroll bars are tied to a single WM_VSCROLL handler. Because this monster dialog contains two horizontal scroll bars, the single WM_ HSCROLL message handler must figure out which scroll bar sent the scroll message.
Here are the steps for adding the scroll bar logic to EX06A:
enum { nMin = 0 }; enum { nMax = 100 };
Add the following code to the CEx06aDialog member function OnInitDialog in the file ex06aDialog.cpp:
CScrollBar* pSB = (CScrollBar*) GetDlgItem(IDC_LOYAL); pSB->SetScrollRange(nMin, nMax); pSB = (CScrollBar*) GetDlgItem(IDC_RELY); pSB->SetScrollRange(nMin, nMax);
void CEx06aDialog::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) { int nTemp1, nTemp2; nTemp1 = pScrollBar->GetScrollPos(); switch(nSBCode) { case SB_THUMBPOSITION: pScrollBar->SetScrollPos(nPos); break; case SB_LINELEFT: // left arrow button nTemp2 = (nMax - nMin) / 10; if ((nTemp1 - nTemp2) > nMin) { nTemp1 -= nTemp2; } else { nTemp1 = nMin; } pScrollBar->SetScrollPos(nTemp1); break; case SB_LINERIGHT: // right arrow button nTemp2 = (nMax - nMin) / 10; if ((nTemp1 + nTemp2) < nMax) { nTemp1 += nTemp2; } else { nTemp1 = nMax; } pScrollBar->SetScrollPos(nTemp1); break; } }
The scroll bar functions use 16-bit integers for both range and position.