Visual C++ 6 Unleashed

Visual C++ 6 Unleashed

By MICKEY WILLIAMS and David Bennett

Modeless Dialog Boxes

So far, you've seen how to create a modal dialog box using the DoModal() function. When a modal dialog box is displayed, the user is unable to interact with any other parts of the application's user interface until the dialog box is closed.

You may want to display a dialog box like a control palette, for example, or to provide feedback such as coordinates from a drawing operation. To do this, you'll need a modeless dialog box.

Modeless Dialog Box Resource Templates

You should consider a few differences when creating a dialog box template for a modeless dialog box.

First, you may not require OK and Cancel buttons for a modeless dialog box, because these usually are associated with modal operations. The user normally closes a modeless dialog box via the close box (a small x) in the upper-right corner of the dialog box. If you do decide to use OK and Cancel buttons, you must override the default OnOK() and OnCancel() message handlers and create your own that call DestroyWindow(). You must do this because the default functions call EndDialog(), which only closes a dialog box in modal operation.

Second, if you want your dialog box to be visible after creation, you should set the Visible flag in the dialog box's properties (on the More Styles tab). By setting this flag, you create the dialog box with the WS_VISIBLE style. Otherwise, the dialog box is created but remains invisible unless you call the dialog box's ShowWindow() function passing TRUE.

Figure 5.9 shows the Visible property for a modeless dialog box with no OK or Cancel button.

05fig09.gif

Figure 5.9 A typical modeless dialog box template and properties.

Creating and Destroying Modeless Dialog Boxes

You can create a modeless dialog box by using the Create() function instead of the DoModal() function to display the dialog box.

You should pass the dialog box's template ID (or a resource name) to Create() and optionally a pointer to a parent window. If the creation succeeds, Create() returns a TRUE value and the dialog box is created.

If you haven't specified the WS_VISIBLE flag on the dialog box template, nothing is displayed until you call ShowWindow(TRUE). If you have set this flag, the modeless dialog box is displayed immediately.

The Create() function returns immediately after the dialog box is created—unlike the DoModal(). This has implications for the scope and lifetime of your modeless dialog box class. You should never declare your modeless dialog box as a local function variable as you might with a modal dialog box. If you do, when the function returns, the dialog box's handler object is destroyed, and the modeless dialog box is destroyed immediately by the CDialog base class destructor.

Instead, you can allocate the memory dynamically and, depending on your application requirements, track the memory or just let the dialog box delete itself when it is closed.

If you don't track the dialog box, for example, you may create an instance from a menu handler function like this:

void CDialogTemplDoc::OnShowdialogDisplaymodeless()
{
   CModeless* pDlgModeless = new CModeless;
   pDlgModeless->Create(CModeless::IDD);
}

If you allocate the memory in this way, you must delete it after the dialog box is closed to avoid memory leaks. When the dialog box is closed, the last message sent to your dialog box handler class is WM_NCDESTROY. The default implementation of OnNcDestroy() then does some housekeeping and finally calls the PostNcDestroy() virtual function. You can override this virtual function to delete the C++ this pointer to the dialog box object itself.

You can add an override to your CDialog-derived class by using the Add Virtual Function dialog box and then adding the delete statement like this:

void CModeless::PostNcDestroy()
{
    CDialog::PostNcDestroy();

    // Delete Ourselves
    delete this;
}

If you want to create the modeless dialog box from a memory-based dialog box template (instead of a resource), you can call the CreateIndirect() function passing a pointer to a DLGTEMPLATE structure or a global memory segment handle to memory containing the structures.

Tracking Modeless Dialog Boxes

The memory-tracking technique in the preceding section is very simplistic and allows the user to create many instances of the modeless dialog box, which probably is undesirable. Also, none of the instances can be destroyed from any objects in your code, because the dialog box object is the only thing that knows where its memory is located.

A more likely scenario is that your document would track the modeless dialog box. To do this, you would add a member variable to your document to track the dialog box so that you can destroy it from the document, and send it messages to provide feedback from other elements of the user interface. You also should place a pointer back to the parent document so that you can inform the document of when the user closes the dialog box.

You could add a pointer to your CDocument-derived class to the modeless dialog box handler class, for example, and initialize it through the constructor like this:

CModeless::CModeless(CDialogTemplDoc* pParent)
    : m_pParent(pParent)
{
  Create(CModeless::IDD);
}

void CModeless::PostNcDestroy()
{
  CDialog::PostNcDestroy();
  m_pParent->m_pDlgModeless = NULL;
  delete this;
}

You'll notice that the constructor also calls Create() so that the modeless dialog box is created and displayed when the object is constructed, thus simplifying the creation process from the calling object. The PostNcDestroy() function sets its pointer to the modeless dialog box equal to NULL, indicating that the dialog box is dead and gone.

The document then can track the modeless dialog box with its own member pointer and close it from a menu handler, as shown in these lines:

CDialogTemplDoc::CDialogTemplDoc() : m_pDlgModeless(NULL)
{
}
void CDialogTemplDoc::OnShowdialogDisplaymodeless()
{
  if (!m_pDlgModeless) m_pDlgModeless = new CModeless(this);
}

void CDialogTemplDoc::OnShowdialogClosemodeless()
{
  if (m_pDlgModeless) m_pDlgModeless->DestroyWindow();
}

This technique also lets you update the controls in the modeless dialog box from other application objects (such as a view to indicate the current mouse position) using dialog box member functions accessible through the document's pointer.

Only one instance of the modeless dialog box can be created at any one time, because the document menu handler function checks the pointer to see whether it already points to a modeless dialog box.

Dialog Bars

A dialog bar is a special form of modeless dialog box that encapsulates the functionality of a control bar (like a toolbar or status bar). This bar lets the modeless dialog box dock naturally to the window frames or float with a small frame.

You can create a dialog box template for a dialog bar like that for a normal modeless dialog box. The only difference is that you should set only the WS_CHILD (the Child setting in the Style combo box) and not the WS_VISIBLE flag (visible from the More Styles tab).

You then can construct the dialog bar using the MFC CDialogBar class (inherited from CControlbar) or your own derived class. The default constructor doesn't require any parameters. You normally would embed the CDialogBar object inside a frame window class, such as CMainFrame, as you might a toolbar or status bar.

After you construct the dialog bar, you can call its Create() function to create it. If you've embedded the dialog bar inside a frame window class, you probably will call the Create() function inside the frame's OnCreate() function, just like a toolbar.

The dialog bar's Create() function differs from a modeless dialog box's Create() function and is more like a control bar's Create(). The first parameter is a pointer to the parent window (normally a frame window). The second parameter is the ID of the dialog box template resource that you want to use for the dialog bar (or a resource name string). The third parameter lets you specify a docking/alignment style. This is just like a control bar style and can be a flag value such as CBRS_TOP, CBRS_LEFT, or CBRS_NOALIGN. You should supply an ID for the control bar as the last parameter to uniquely identify the dialog bar.

You can handle the control and messages of a dialog bar as you would a modeless dialog box. Dialog bars let you embed all the normal controls you can use on a dialog box template while letting you dock and reposition the bar like a toolbar.

Share ThisShare This

Informit Network