Click here to Skip to main content
15,867,488 members
Articles / Desktop Programming / MFC
Article

Modeless Dialog Management

Rate me:
Please Sign up or sign in to vote.
4.71/5 (25 votes)
7 Jul 20035 min read 148.2K   6.1K   61   20
A simple method for tracking and dealing with multiple modeless dialogs.

Introduction

This article presents a method for easily managing modeless dialogs.  Functionality includes:

  • Determining if the dialog is already created
  • Closing the dialog automatically when the parent object is destroyed
  • Notifying the parent object when the dialog is closed
  • Accessing functions in the dialog from the parent object.

The concept is based on a tracker object that exists in the dialog's parent and a helper class from which the dialog is dual-derived.  The helper class automates the interaction between the dialog and its tracker, and the tracker allows the parent to manage the dialog.

The term "parent" is used to represent the class that creates the modeless dialog and is therefore responsible for keeping an eye on it.  The parent is not necessarily a parent window.  It could be any object that needs to have a modeless dialog.

Background

Dealing with 1 or 2 modeless dialogs is not difficult.  You track a pointer to the dialog, make sure the dialog clears the pointer or messages the parent when it closes, check if a dialog is already posted before re-instantiating the class, etc, etc.  These simple steps present many opportunities for mistakes, omissions, and general confusion.  This class was designed to wrap those details and provide a standard way of dealing with modeless dialogs floating around in an application.

Many applications avoid modeless dialogs.  This keeps things simple when modeless functionality is not needed.  The complexity of modeless dialogs is analogous to multi-threading in that it requires management of multiple independent processes.

Often, however, in robot control systems, measurement systems or other hardware control applications, it is necessary to monitor many things simultaneously and provide user input where needed.  Modeless dialogs are a perfect match for these cases.

Using the code

These steps are briefly described in the ModelessDialogTracker.h file:

Step 1

Add a ModelessDialogHelper derivation to your wizard-generated dialog class.  Note the block of "modeless dialog specific" functions in this header file.  These are useful for modeless dialogs, but not essential for the task at hand.  It is, however, a useful block of code to paste into any dialog that is on its way to becoming modeless.

#include "ModelessDialogTracker.h"

class DialogA : public CDialog, ModelessDialogHelper
{
DECLARE_DYNAMIC(DialogA)
public:
  // stuff specific to the modeless dialog. //
  DialogA(ModelessDialogTracker& tracker);
  BOOL Create(UINT nID, CWnd* pWnd) 
  { return CDialog::Create(nID,pWnd); }
  void PostNcDestroy()
  { delete this; }
  void OnCancel()
  { DestroyWindow(); }
  ////////////////////////////////////
  virtual ~DialogA();
  enum { IDD = IDD_DIALOG1 };
  protected:
  virtual void DoDataExchange(CDataExchange* pDX); 
      // DDX/DDV support

DECLARE_MESSAGE_MAP()
};

The ModelessDialogHelper class simply constructs and destructs with the dialog class since it is in its derivation chain.  At construction and destruction time, the ModelessDialogTracker is notified of the change in status.

Step 2

Enhance the constructor of the wizard-generated dialog constructor (in the dialog .cpp file):

DialogA::DialogA(ModelessDialogTracker& tracker)
: ModelessDialogHelper(tracker, *this)
{
}

Step 3

Instantiate a ModelessDialogTracker object in the "parent" class (or any class that is to be responsible for the dialog's existence):

ModelessDialogTracker trackA;

Step 4

Pop your dialog using the Create() call.  Remember to set the Visible property of the dialog to TRUE in the resource editor (if you don't, you will post and invisible dialog -- not too impressive).  Note the check to see if it is already popped.  That call will SetActiveWindow() on the dialog if it is already up.  Also, notice the passing of the dialog tracker to the DialogA constructor.  This is needed so the helper can establish a link to the tracker class.  Since that link is established, there is no need to keep track of pnewDlg after the dialog is up.

#include "DialogA.h"
void CModelessDialogTrackerDemoDlg::OnPopA()
{
  if (trackA.IsAlreadyPopped())
    return;

  DialogA* pnewdlg = new DialogA(trackA); 
          // note the passing of the tracker.

  pnewdlg->Create(pnewdlg->IDD,NULL);    
}

That's it for basic usage.  You can pop the dialog, and the parent will keep track of the state of the dialog.  When the ModelessDialogTracker object, trackA, is destroyed, it will automatically close and cleanup the modeless dialog if necessary.

Additional usage

Forcible Close

Should you need to forcibly close the modeless dialog from the parent, you can just call CloseDialog():

trackA.CloseDialog(); // force it to close.

Calling Dlg Functions

If you want to call a function in the dialog (to update its contents or whatever), you can use the GetDlg() member of the tracker class and cast the return to the proper dialog type:  (see DialogB in the demo project.)

DialogB& dlg = (DialogB&)*trackB.GetDlg();

dlg.SomeFunctionInTheDialog(necessaryData);
         // access dialog directly.

Notification of Close

If you need to be notified, or take some action when the dialog is closed, you can simply derive your own class from ModelessDialogTracker and override the OnDialogClosed() member function:

class TrackDialogC : public ModelessDialogTracker
{
public:
  ...
  virtual void OnDialogClosed();
        // override if you want to do something when closed.
  ...
};

Then, instantiate your class (TrackDialogC) instead of ModelessDialogTracker, and use it to track the dialog.  When the dialog closes, the virtual function is called and you can take whatever action is necessary.  (See DialogC in the demo project.)

Demo Project

The demo project is dialog based and allows you to test-drive 3 implementations of this class:

  • DialogA - basic implementation.
  • DialogB - shows the parent calling functions in the modeless dialog.
  • DialogC - implements a derived dialog tracker class to update the dialog status in the parent.

Points of Interest

You'll notice a warning override in the ModelessDialogTracker.h file:

#pragma warning( disable : 4355 )     
      // the warning given when passing "this" 
      // to a member constructor.
      // in the case of the helper class, we WANT to do this.

This suppresses the following compiler warning:

DialogA.cpp(13) : warning C4355: 'this' : used in 
      base member initializer list

This is caused by the implementation of the DialogA constructor preamble where we pass "this", the dialog itself, into one of the base classes, ModelessDialogHelper's, constructor:

DialogA::DialogA(ModelessDialogTracker& tracker)
: ModelessDialogHelper(tracker, *this)
{
}

In this case, we want the helper class to have the pointer to the CDialog derived class so it can call the appropriate functions.  The warning is due to the fact that a base class should not need a pointer to a class lower in the derivation chain.  In the case of dual-derivation, this is not so.  Thus the passing of the pointer.  It bridges the gap between the helper class and the dialog itself since they are at the same level of the derivation hierarchy.  For this reason, we did the unthinkable and stomped out the warning.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
GeneralMDI Application vc6++ Pin
Salad Juris1-Apr-07 5:38
Salad Juris1-Apr-07 5:38 
QuestionHow to compile it ? Pin
mcv99918-Jul-06 23:49
mcv99918-Jul-06 23:49 
Generalit's a good example Pin
ytfrdfiw21-Jun-06 15:26
ytfrdfiw21-Jun-06 15:26 
GeneralModeLess Dialog Internals. Pin
HakunaMatada4-May-06 2:06
HakunaMatada4-May-06 2:06 
GeneralCould not compile the example code Pin
doctorrie22-Apr-06 19:32
doctorrie22-Apr-06 19:32 
GeneralRe: Could not compile the example code Pin
Baseiso123-Apr-06 8:55
Baseiso123-Apr-06 8:55 
GeneralModeless Dialog Pin
Member 195600524-Jun-05 12:32
Member 195600524-Jun-05 12:32 
GeneralRe: Modeless Dialog Pin
rbid25-Jun-05 20:40
rbid25-Jun-05 20:40 
GeneralModeless Dialog Pin
Member 195600524-Jun-05 12:31
Member 195600524-Jun-05 12:31 
GeneralNice and useful article. Pin
rbid22-Jan-05 10:24
rbid22-Jan-05 10:24 
QuestionTrue ModeLess? Pin
Ramanan.T22-Jul-04 20:59
Ramanan.T22-Jul-04 20:59 
AnswerRe: True ModeLess? Pin
Joe Calkins23-Jul-04 5:13
Joe Calkins23-Jul-04 5:13 
GeneralRe: True ModeLess? Pin
Ramanan.T5-Aug-04 18:34
Ramanan.T5-Aug-04 18:34 
GeneralMixing Modal & Modeless Pin
Davide Zaccanti8-Mar-04 23:40
Davide Zaccanti8-Mar-04 23:40 
GeneralModeless Dialog in DLL Pin
paul hoshovsky29-Feb-04 21:18
paul hoshovsky29-Feb-04 21:18 
GeneralRe: Modeless Dialog in DLL Pin
Joe Calkins6-Mar-04 17:56
Joe Calkins6-Mar-04 17:56 
GeneralQuestion about Modeless Pin
Talaat El Gamel8-Feb-04 10:03
Talaat El Gamel8-Feb-04 10:03 
GeneralRe: Question about Modeless Pin
Joe Calkins8-Feb-04 11:38
Joe Calkins8-Feb-04 11:38 
GeneralYou Left Out Something REAL Important Pin
#realJSOP10-Nov-03 0:42
mve#realJSOP10-Nov-03 0:42 
GeneralRe: You Left Out Something REAL Important Pin
Joe Calkins10-Nov-03 1:42
Joe Calkins10-Nov-03 1:42 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.