Default and Cancel Buttons

The design of Windows dialog boxes includes the concepts of the default button and the cancel button. The default button is activated when the Enter key is pressed and the control that currently has the focus is not itself a button. In other words, when a button has the focus, pressing the Enter key triggers that button. The cancel button is similar but simpler: when the Esc key is pressed, whatever button is marked as the cancel button is activated. Esc always affects the cancel button regardless of what control has the focus.

OLE Controls provides a means to duplicate this behavior. To begin with, controls that act as buttons (those that understand default and cancel operations) mark themselves with the bit OLEMISC_ACTSLIKEBUTTON. In design mode, a container provides the programmer with menu commands or some similar method to mark one such control with "default" and another control with "cancel." These commands should be enabled only if the selected control is marked with OLEMISC_ACTSLIKEBUTTON. Assigning default and cancel buttons is similar to marking one button as DEFPUSHBUTTON and giving another the identifier IDCANCEL in a typical Windows dialog.

What the container does with this information depends on a number of factors. Let's take the easy one first: the Esc key. When this key is pressed, the keystroke first goes to the UI-active control. If that control is marked CTRLINFO_EATS_ESCAPE, we can be assured that it will process that keystroke. Note, however, that the button marked "cancel" in the container doesn't know that it's the cancel button, so it will not process this keystroke even if it is UI active. Anyway, if the UI-active control does not eat the Esc key, the container checks whether it has a button marked "cancel" and, if so, calls that control's IOleControl::OnMnemonic. Because the control knows it's a button (it's marked as such), it understands any mnemonic to mean "Press me" and will fire its primary event, even if the mnemonic isn't in its CONTROLINFO. That's part of being a button.

The default button is a little more complicated. First of all, there is an added UI element—a thick frame around the button that indicates that the button is the default. Second, the default button isn't always the one that the programmer marked. As a demonstration, open a typical Windows dialog box, such as File Open, and the initial default button will be the DEFPUSHBUTTON in the template, in this case the OK button. Notice that the OK button doesn't have the focus, but it is still the default because it is marked as such. Now hit the Tab key until the Cancel button has the focus. Notice that it is now the default button because it has the thick frame. If you press Enter, you cancel the dialog. Well, don't cancel the dialog; instead, use the mouse to click on one of the list boxes. Notice that the focus goes to the list box and that the OK button once again becomes the default button.

This is the behavior OLE Controls allows you to duplicate, and it involves the DisplayAsDefault ambient property. Only one site in any given form or document should have this flag set to TRUE at any one time. When it is set, and the control in that site is a push button, the control will draw itself with a thick border. So first the container has to change this ambient property in its sites as focus changes between buttons as detected in IOleControl::OnFocus. If any button receives the focus, the container sets DisplayAsDefault to TRUE and calls IOleControl::OnAmbientPropertyChange. If the control with the focus is not a button, the container sets DisplayAsDefault to FALSE, notifies the control, and sets DisplayAsDefault to TRUE for the marked default button. Also, whenever any nondefault button loses the focus, the container must set that site's DisplayAsDefault to FALSE and notify the control.

Handling OnFocus and the DisplayAsDefault ambient property in this fashion treats the UI element more or less correctly. We still need to handle the Enter key and nonbutton controls that eat it. As with Esc, if the UI-active control eats the Enter key, it will specify CTRLINFO_EATS_RETURN. When such a control receives the focus, the container must turn off DisplayAsDefault for even the default button because the Enter key will never reach the container to be processed as a mnemonic.