Previous Book Contents Book Index Next

Inside Macintosh: Programmer's Guide to MacApp / Part 2 - Working With MacApp
Chapter 19 - Working With Dialog Boxes and Controls

Recipes--Dialog Boxes and Controls

This section provides a general outline of how to work with dialog boxes and controls, as well as recipes and sample code that demonstrate how to display a modal dialog box and respond to user manipulation of control objects.

Working With Dialog Boxes and Controls--A General Outline

Dialog boxes are handled like other window and view objects in your application. Dialog boxes usually contain controls such as buttons, checkboxes, and scroll bars. MacApp defines the TControl class, a subclass of TView, and a number of specialized control view subclasses.

  1. To initialize MacApp's UDialog unit, you call the InitUDialog routine from your main routine, after calling InitUMacApp and before creating your application object. (See "Recipe--Launching a Simple Application," beginning on page 292.)
  2. To use a dialog box in your application, follow these steps:

    • Define a subclass of TDialogView to serve as the main dialog-box view.
    • Use a view-editing program to define a window containing the main dialog-box view and its view hierarchy of control views and other view objects.
    • In the resource definition, set the window's fTargetID field to the view ID of an edit-text or number-text view to be selected when the dialog box is first displayed. If there are no edit- or number-text views in the dialog box, set fTargetID to the view ID of the dialog view.

  3. To work with control view objects in a dialog box, follow these steps (see also the recipe beginning on page 461):

    • Define a subclass of each MacApp control view class that you need to modify.
    • Override the DoEvent method of your control view class to handle specific events. Constants for the event numbers generated by user actions with control objects are defined in the UMacAppGlobals unit and are shown in Table 19-1.

      For a different approach to control view objects, see the recipe beginning on page 465.

  4. To use a modal dialog box (page 467), follow these steps:

    • In your view resource template (described in step 2 above), identify which control items in the dialog box cause it to be dismissed.
    • Create the window and view hierarchy for your dialog box from the view resource you have defined. Call the NewTemplateWindow method of MacApp's global view server object (gViewServer) to create the window.
    • If you do not use the TDialogView class for your dialog-box view, make sure the view class you do use calls its window's SetDialogItems method to associate a TDialogBehavior object (see page 239) with the window. (The TDialogView class calls SetDialogItems automatically in its DoPostCreate method.)
    • Call the window's PoseModally method to display the dialog box. The window's PoseModally method calls the PoseModally method of its TDialogBehavior object. The window's PoseModally method returns the ID of the control object that dismisses the dialog box. For example, the user may dismiss the dialog box by clicking the OK button, the Cancel button, or another button. Your application can base its action on the returned ID.
    • After PoseModally returns, call the Close method of the window.

Recipe--Responding to User Actions in a Control View

When a user presses the mouse button while the cursor is over an application window, the application receives a mouse-down event from the operating system. The application object dispatches the event to the window containing the mouse-down event--typically, the event is passed down to the smallest subview that contains the event and wishes to respond to it. This type of event dispatching is described in "View Hierarchy Dispatching," beginning on page 103.

MacApp's control view classes include views to display standard buttons, scroll bars, checkboxes, radio buttons, and text- and number-entry fields. All of these classes descend from the TCtlMgr class, and for many of them the TCtlMgr::DoMouseCommand method handles a mouse-down event in the control view. The DoMouseCommand method calls the Toolbox routine TrackControl to track the mouse while the user presses the mouse button. If TrackControl returns a nonzero value (the user clicked the control), DoMouseCommand calls HandleEvent, passing the control view's fEventNumber field. Possible values for the fEventNumber field are shown in Table 19-1.

The HandleEvent method is a method of TEventHandler. It gives any enabled behavior object a chance to handle the event in its DoEvent method. If none does, it calls the control view's DoEvent method. You can override DoEvent in your control view subclasses to respond to a user click on the control.

MacApp defines event number constants, as well as certain change notification constants, in the UMacAppGlobals unit. Both are shown in Table 19-1.
Table 19-1 MacApp event and change notification constants
Event nameNumberDescription
mOKHit1OK button clicked.
mCancelHit2Cancel button clicked.
mButtonHit3TButton object clicked.
mCheckBoxHit4Checkbox object clicked.
mClusterHit5Cluster object clicked.
mEditTextHit6Edit text object clicked.
mIconHit7TIcon object clicked.
mListItemHit8List item clicked.
mListScrollBarHit9Scroll bar of TListView object clicked.
mPictureHit10TPicture object clicked.
mPopupHit11Pop-up menu object clicked.
mRadioHit12Radio button clicked.
mStaticTextHit13Static text object clicked.
mHScrollBarHit14Horizontal scroll-bar object clicked.
mVScrollBarHit15Vertical scroll-bar object clicked.
mEditTabKey16Tab key pressed during editing.
mEditReturnKey17Return key pressed during editing.
mEditEnterKey18Enter key pressed during editing.
mPatternHit19TPattern object clicked.
mControlHit20Control object clicked.
mCancelKey21Cancel button selected by keypress.
mDefaultKey22Default button selected by keypress.
mSmallIconHit23Small icon object clicked.
mBecameWindowTarget24Object became window target.
mResignedWindowTarget25Object resigned as window target.
mBecameTarget26Object became target.
mResignedTarget27Object resigned as target.
mClosed28View closed.
mAboutToLoseControl29Application about to be switched out.
mScrapChanged30Data in desk scrap changed.
mTurnOn31Control item turned on.
mTurnOff32Control item turned off.
mToggle33Control item toggled.
mDismiss34Item dismissed.

To override the DoEvent method of a control view class to respond to a user action, you perform these steps:

  1. Define a subclass for the control view.

    • The subclass should descend from the TCtlMgr class, or from one of MacApp's predefined control classes, such as TButton or TCheckBox.
    • Override the DoEvent method.

  2. Implement a DoEvent method.

    • In the DoEvent method, respond to event numbers of interest.
    • For other event numbers, call Inherited.

The sample code shown in this recipe is for a hypothetical button class.

Define a Subclass for the Control View

The following is a partial listing for a custom button class:

class TYourSpecialButton : public TButton
   virtual void DoEvent(EventNumber eventNumber,
                     TEvent*     event);
// Some code not shown.
You define additional fields and methods to perform the operations specific to the TYourSpecialButton class.

Implement a DoEvent Method

The DoEvent method is called when a user clicks an instance of your control class--in this case, a special button view. The DoEvent method responds to event numbers of interest and calls Inherited for event numbers it does not handle.

void TYourSpecialButton::DoEvent(EventNumbereventNumber,
                           TEvent*     event) // Override.
   switch (eventNumber)
      case mButtonHit:
            // Perform any special handling for your 
            // button class here.

         // Event not handled; pass it on.
         Inherited::DoEvent(eventNumber, source, event);
This simple version of DoEvent performs special handling for an mButtonHit event number. Otherwise, it calls Inherited to let its parent class handle the event.

Recipe--An Alternative Approach to User Actions in
a Control

The previous recipe described how to handle a user action by defining a subclass of TButton and overriding the DoEvent method. Although that mechanism is effective and works for buttons and other controls, it does have some drawbacks:

On the other hand, you nearly always define a subclass of TDialogView (see page 460), and the dialog view class is a logical place to centralize knowledge about events that take place in the dialog. Why not use the TDialogView class to handle user actions for simple control items?

To handle events (in this case, a button hit) in your subclass of TDialogView, you perform these steps:

  1. Define a unique constant for each event you will handle.
  2. Define a view resource for your dialog view.

    • Use the TButton class for a button used in the dialog box.
    • Set the event number field of the button to the constant you defined.

  3. Override the DoEvent method in your subclass of TDialogView.

    • Your DoEvent method should handle all the event constants you have defined.

The sample code in this recipe is for a hypothetical dialog view.

Define a Unique Constant for Each Event You Will Handle

To identify a button event you are interested in, you use code like the following:

const EventNumber mCountRaisinsButtonHit = 14001;
The constant number, in this case 14001, should be well outside the range of event numbers defined by MacApp (shown in Figure 19-1). Also, make sure that any event number constants you define remain unique across all code modules in your application.

Define a View Resource for Your Dialog View

The use of view dialog resources is described in "Working With View Resource Templates," beginning on page 425. Using a view resource editor application, you can create view hierarchies in a graphical environment. A view editor also allows you to set initial values for many fields of the view objects in the hierarchy.

Use a view editor to create a TButton object for the Count Raisins button, then set the button's fEventNumber field to 14001.

Be sure the number you enter (14001) matches the constant defined in the previous step.

Override the DoEvent Method in Your Subclass of TDialogView

You normally define a subclass of TDialogView to serve as the main dialog view for dialog boxes in your application. You can define your subclass with code like the following, which overrides the DoEvent method:

class TYourDialogView : public TDialogView
   virtual void DoEvent(EventNumber eventNumber,
                     TEvent*     event); // Override.

Your version of the DoEvent method should handle any control event constants you have defined, using code like the following:

void TYourDialogView::DoEvent(EventNumbereventNumber,
                        TEvent*     event) // Override.
   switch (eventNumber)
      case mCountRaisinsButtonHit:
            long raisinsPerBox = this->CountTheRaisins()
            // Any other code to handle this case.

      // Add cases for other event numbers your dialog view handles.

      // Default is to pass the event on.
         // Event not handled; pass it on.
         Inherited::DoEvent(eventNumber, source, event);
In this case, when the Count Raisins button is hit, the dialog view determines how many raisins are in the box and sets the appropriate variable. It may need to access other fields of the dialog box, which it can rightly be expected to know about.

You can use this mechanism to handle many control events for a given dialog box, though at some point the switch statement may become too large for good coding style.

Recipe--Displaying a Modal Dialog Box

The DemoDialogs application has a Modal Beep Dialog menu command. The menu command displays a modal dialog box containing OK and Cancel buttons, a static text item, and a number text item in which the user can enter the number of beeps. The default number of beeps is 1, the minimum is 1, and the maximum is 10. These values are specified in the file DemoDialogs.r, in the cModelessBeepDialog view resource, in the NumberText subview definition, by the text "1, 1, 10" (shown on page 470). You can modify these values by editing the view resource.

If the user dismisses the dialog box with the OK button, the specified number of beeps is performed, unless it is out of range--then an error message is displayed and the dialog box is not dismissed. If the user cancels the dialog box, no beeps are performed. The Modal Beep Dialog menu command demonstrates how to display a modal dialog box.

To display a modal dialog box, you perform these steps:

  1. Define a 'View' resource for the dialog box window and view hierarchy.

    • Define a resource ID constant for the view resource.
    • Define the view resource for the window.

  2. Create, display, and close the dialog box.

    • Create the window with the NewTemplateWindow method.
    • Call the window's PoseModally method to display the dialog box.
    • Call the window's Close method after the user dismisses the dialog box.

The sample code shown in this recipe is from the DemoDialogs application.

Define a 'View' Resource for the Dialog-Box Window Hierarchy

The recipe beginning on page 451 ("Recipe--Creating a Window From a View Resource") describes how to define a resource ID constant and a view resource for a window. The key to defining a view resource for a dialog box is to use a TDialogView as the parent view for any control view objects in the dialog box.

The DemoDialogs application defines a constant for the Modal Beep Dialog menu command in the file DemoDialogs.r:

#define cModalBeepDialog1005
The following is a partial listing of the 'View' resource for the dialog box displayed by the Modal Beep Dialog menu command. It is defined in the file DemoDialogs.r:

resource 'View' (cModalBeepDialog, purgeable)
      {'wind', 857, "", 'WIND', enabled, noIdle, {}, MAThreeOh, 
      {72, 64}, {80, 270}, sizeVariable, sizeVariable, shown,
      Window {movableDBoxProc, 'numb', goAwayBox, notResizable,
         ignoreFirstClick, freeOnClosing, disposeOnFree,
         closesDocument, openWithDocument,dontAdaptToScreen,
         dontStagger, forceOnScreen, centerHorizontally,
         doesntFloat, doesntHideOnSuspend, generateActivates, 0,
         kWindowTitles, 6}, 1}, 
      {'dlog', 666, "", 'DLOG', enabled, noIdle, {},
      MAThreeOh, {0, 0}, {1000, 500}, sizeFixed, sizeFixed,
      shown, doesntWantToBeTarget, handlesCursor, 
      letsSubViewsHandleCursor, noCursorID, handlesHelp, 
      letsSubViewsHandleHelp, noHelpID, 1, NoDrawingEnvironment {},
      NoAdorners {}, 0, DialogView {'ok  ', 'cncl'}, 4}, 
      {'butn', 208, "", 'ok  ', enabled, noIdle, {}, MAThreeOh, 
      {'butn', 92, "", 'cncl', enabled, noIdle, {}, MAThreeOh, 
      {'stat', 96, "", noID, notEnabled, noIdle, {}, MAThreeOh, 
      {'nmbr', 176, "", 'numb', enabled, noIdle,
      NumberText {mEditTextHit, notHilited, notDimmed, sizeable,
      {3, 3, 3, 3}, 130, dontPreferOutline, dontAutoWrap,
      dontEraseFirst, justRight, kStaticTextText, 19, 5,
      LeftRightUpDownBackspace, 1, 1, 10},NoSubviews}
This view resource defines a window containing a dialog view, Cancel and OK buttons, a static text view, and a number text view. Note that the view ID for the number text view is 'numb'.

Create, Display, and Close the Dialog Box

The DemoDialogs application creates, displays, and closes the Modal Beep Dialog dialog box in the MakeModalBeepDialog method:

void TTestApplication::MakeModalBeepDialog(CommandNumber aCommandNumber)

   TWindow *   aWindow = NULL;
   IDType      dismisser;
   TNumberText * aNumberText = NULL;
   long        n = 0;

   // Create the dialog box window. Fail if unsuccessful.
   aWindow = gViewServer->NewTemplateWindow(
                              (short)aCommandNumber, NULL);

   // Display the dialog box as a modal dialog.
   dismisser = aWindow->PoseModally();

   // On return, examine dismisser.
   if (dismisser == 'ok  ')
      // Find the number text view and extract the number of beeps.
      // Use dynamic cast to make sure we have the right kind of view.
      aNumberText = MA_DYNAMIC_CAST(TNumberText,
      if (aNumberText)
         n = aNumberText->GetValue();
   for (long i = 1; i <= n; i++)

} // TTestApplication::MakeModalBeepDialog
Validation occurs when the user attempts to dismiss the dialog box with the OK button. Validation is described in "Validating Dialog-Box Data," beginning on page 244. In your application, you can replace the code that creates beeps with code that uses the entered number for some other purpose.

Call the Window's PoseModally Method to Display the Dialog Box

The following line from the MakeModalBeepDialog method displays the modal dialog box:

dismisser = aWindow->PoseModally();
The TWindow::PoseModally method calls the PoseModally method of its TDialogBehavior behavior object. That method repeatedly makes the call


until the dialog box is dismissed. It then returns the ID of the control object that dismissed the dialog box (such as the Cancel or OK button).

Call the Window's Close Method

The following line from the MakeModalBeepDialog method closes the modal dialog box window:

The CloseAndFree method calls Close to close the window. If the window is freed on closing, CloseAndFree also frees it.

Your application can validate data when the user attempts to close the dialog box. If the data is invalid, you can put up an error message and leave the dialog box open. For more information, see "Validating Data When a User Accepts Changes to a Dialog Box," beginning on page 244.

Previous Book Contents Book Index Next

© Apple Computer, Inc.
25 JUL 1996