| Tenouk C & C++ | MFC Home | Modal Dialog & Windows Common Controls 5 | Modeless Dialog & Windows Common Dialogs 2 | Download | Site Index |


 

 

 

 

 

Module 6:

The Modeless Dialog and Windows Common Dialogs 1

 

 

 

 

 

Program examples compiled using Visual C++ 6.0 (MFC 6.0) compiler on Windows XP Pro machine with Service Pack 2. Topics and sub topics for this Tutorial are listed below:

  1. The Modeless Dialog and Windows Common Dialogs

  2. Modeless Dialogs

  3. Creating Modeless Dialogs

  4. User-Defined Messages

  5. Dialog Ownership

  6. A Modeless Dialog Example: MYMFC9

 

 

 

The Modeless Dialog and Windows Common Dialogs

 

Now you'll move on to the modeless dialog and to the common dialogs. Modeless dialogs allow the user to work elsewhere in the application while the dialog is active. The common dialog classes are the C++ programming interface to the group of Windows utility dialogs that include File Open, Page Setup, Color, and so forth and that are supported by the dynamic link library COMDLG32.DLL. In this Module's first example, you'll build a simple modeless dialog that is controlled from a view. In the second example, you'll derive from the COMDLG32 CFileDialog class a class that allows file deletion.

 

Modeless Dialogs

 

In the MFC Library version 6.0, modal and modeless dialogs share the same base class, CDialog, and they both use a dialog resource that you can build with the dialog editor. If you're using a modeless dialog with a view, you'll need to know some specialized programming techniques.

 

Creating Modeless Dialogs

 

For modal dialogs, you've already learned that you construct a dialog object using a CDialog constructor that takes a resource template ID as a parameter, and then you display the modal dialog window by calling the DoModal() member function. The window ceases to exist as soon as DoModal() returns. Thus, you can construct a modal dialog object on the stack, knowing that the dialog window has been destroyed by the time the C++ dialog object goes out of scope.

Modeless dialogs are more complicated. You start by invoking the CDialog default constructor to construct the dialog object, but then to create the dialog window you need to call the CDialog::Create member function instead of DoModal(). Create takes the resource ID as a parameter and returns immediately with the dialog window still on the screen. You must worry about exactly when to construct the dialog object, when to create the dialog window, when to destroy the dialog, and when to process user-entered data. Here's a summary of the differences between creating a modal dialog and a modeless dialog.

 

 

Modal Dialog

Modeless Dialog

Constructor used

Constructor with resource ID param

Default constructor (no params)

Function used to create window

DoModal()

Create() with resource ID param

 

Table 1

 

User-Defined Messages

 

Suppose you want the modeless dialog window to be destroyed when the user clicks the dialog's OK button. This presents a problem. How does the view know that the user has clicked the OK button? The dialog could call a view class member function directly, but that would "marry" the dialog to a particular view class. A better solution is for the dialog to send the view a user-defined message as the result of a call to the OK button message-handling function. When the view gets the message, it can destroy the dialog window (but not the object). This sets the stage for the creation of a new dialog. You have two options for sending Windows messages: the CWnd::SendMessage function or the PostMessage() function. The former causes an immediate call to the message-handling function, and the latter posts a message in the Windows message queue. Because there's a slight delay with the PostMessage() option, it's reasonable to expect that the handler function has returned by the time the view gets the message.

 

Dialog Ownership

 

Now suppose you've accepted the dialog default pop-up style, which means that the dialog isn't confined to the view's client area. As far as Windows is concerned, the dialog's "owner" is the application's main frame window, not the view. You need to know the dialog's view to send the view a message. Therefore, your dialog class must track its own view through a data member that the constructor sets. The CDialog constructor's pParent parameter doesn't have any effect here, so don't bother using it.

 

A Modeless Dialog Example: MYMFC9

 

We could convert the previous Module monster dialog to a modeless dialog, but starting from scratch with a simpler dialog is easier. Example MYMFC9 uses a dialog with one edit control, an OK button, and a Cancel button. As in the previous Module example, pressing the left mouse button while the mouse cursor is inside the view window brings up the dialog, but now we have the option of destroying it in response to another event, pressing the right mouse button when the mouse cursor is inside the view window. We'll allow only one open dialog at a time, so we must be sure that a second left button press doesn't bring up a duplicate dialog.

To summarize the upcoming steps, the MYMFC9 view class has a single associated dialog object that is constructed on the heap when the view is constructed. The dialog window is created and destroyed in response to user actions, but the dialog object is not destroyed until the application terminates. Here are the steps to create the MYMFC9 example:

 

Run AppWizard to produce \mfcproject\mymfc9 (or whatever directory you have designated for the project). Accept all the defaults but two: select Single Document and deselect Printing And Print Preview and ActiveX Controls. The options and the default class names are shown here.

 

MFC AppWizard new project creation dialog.

 

Figure 1: MFC AppWizard new project creation dialog.

 

MYMFC9 project summary.

 

Figure 2: MYMFC9 project summary.

 

Use the dialog editor to create a dialog resource. Choose Resource from Visual C++'s Insert menu, and then select Dialog. The dialog editor assigns the ID IDD_DIALOG1 (the default ID) to the new dialog. Change the dialog caption to Modeless Dialog. Accept the default OK and Cancel buttons with IDs IDOK and IDCANCEL, and then add a static text control and an edit control with the default ID IDC_EDIT1. Change the static text control's caption to Edit 1. Here is the completed dialog. Be sure to select the dialog's Visible property.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Modifying the dialog properties.

 

Figure 3: Modifying the dialog properties.

 

More dialog properties modification.

 

Figure 4: More dialog properties modification.

 

Modifying the static text control properties.

 

Figure 5: Modifying the static text control properties.

 

Modifying the Edit control properties.

 

Figure 6: Modifying the Edit control properties.

 

Modeless dialog with its controls.

 

Figure 7: Modeless dialog with its controls.

 

Use ClassWizard to create the CMymfc9Dialog class. Choose ClassWizard from Microsoft Visual C++'s View menu. Fill in the New Class dialog as shown here, and then click the OK button.

 

Creating a new CMymfc9Dialog class dialog prompt.

 

Figure 8: Creating a new CMymfc9Dialog class dialog prompt.

 

The CMymfc9Dialog class information.

 

Figure 9: The CMymfc9Dialog class information.

 

Changing the default class header and implementation file names if required, not for this example.

 

Figure 10: Changing the default class header and implementation file names if required, not for this example.

 

New class included in MYMFC9 project, ready to be used.

 

Figure 11: New class included in MYMFC9 project, ready to be used.

 

Add the message-handling functions shown below.

 

Object ID

Message

Member Function

IDCANCEL

BN_CLICKED

OnCancel

IDOK

BN_CLICKED

OnOK

 

Table 2

 

To add a message-handling function, click on an object ID, click on a message, and then click the Add Function button. The Add Member Function dialog box appears. Edit the function name if necessary, and click the OK button.

 

Add a message-handling function for IDOK object.

 

Figure 12: Add a message-handling function for IDOK object.

 

Add a variable to the CMymfc9Dialog class. While in ClassWizard, click on the Member Variables tab, choose the IDC_EDIT1 control, and then click the Add Variable button to add the CString variable m_strEdit1.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Adding a member variable to the CMymfc9Dialog class.

 

Figure 13: Adding a member variable to the CMymfc9Dialog class.

 

Entering the member variable name and type.

 

Figure 14: Entering the member variable name and type.

 

Edit mymfc9Dialog.h to add a view pointer and function prototypes. Type in the following code in the CMymfc9Dialog class declaration:

 

private:

    CView* m_pView;

 

Adding a view pointer’s type and name.

 

Figure 15: Adding a view pointer’s type and name.

 

Also, add the function prototypes as follows:

 

public:

    CMymfc9Dialog(CView* pView);

    BOOL Create();

 

Visual C++ MFC code segment

 

Listing 1.

 

Using the CView class rather than the CMymfc9View class allows the dialog class to be used with any view class. Edit mymfc9Dialog.h to define the WM_GOODBYE message ID. Add the following line of code:

 

#define WM_GOODBYE  WM_USER + 5

 

Visual C++ MFC code segment

 

Listing 2.

 

The Windows constant WM_USER is the first message ID available for user-defined messages. The application framework uses a few of these messages, so we'll skip over the first five messages.

 

Viewing the resource symbols in the project.

 

Figure 16: Viewing the resource symbols in the project.

 

Inserting and deleting resource symbol through the Resource Symbol dialog.

 

Figure 17: Inserting and deleting resource symbol through the Resource Symbol dialog.

 

 

Visual C++ maintains a list of symbol definitions in your project's resource.h file, but the resource editor does not understand constants based on other constants. Don't manually add WM_GOODBYE to resource.h because Visual C++ might delete it.

 

Add the modeless constructor in the file mymfc9Dialog.cpp. You could modify the existing CMymfc9Dialog constructor, but if you add a separate one, the dialog class can serve for both modal and modeless dialogs. Add the lines shown below.

 

// modeless constructor

CMymfc9Dialog::CMymfc9Dialog(CView* pView)

{

    m_pView = pView;

}

 

Visual C++ MFC code segment

 

Listing 3.

 

You should also add the following line to the AppWizard-generated modal constructor:

 

m_pView = NULL;

 

Visual C++ MFC code segment

 

Listing 4.

 

The C++ compiler is clever enough to distinguish between the modeless constructor CMymfc9Dialog(CView*) and the modal constructor CMymfc9Dialog(CWnd*). If the compiler sees an argument of class CView or a derived CView class, it generates a call to the modeless constructor. If it sees an argument of class CWnd or another derived CWnd class, it generates a call to the modal constructor.

Add the Create() function in mymfc9Dialog.cpp. This derived dialog class Create() function calls the base class function with the dialog resource ID as a parameter. Add the following lines:

 

BOOL CMymfc9Dialog::Create()

{

    return CDialog::Create(CMymfc9Dialog::IDD);

}

 

Visual C++ MFC source code segment

 

Listing 5.

 

Create() is not a virtual function. You could have chosen a different name if you had wanted to. Edit the OnOK() and OnCancel() functions in mymfc9Dialog.cpp. These virtual functions generated by ClassWizard are called in response to dialog button clicks. Add the following code:

 

// not really a message handler

void CMymfc9Dialog::OnCancel()

{

    if (m_pView != NULL)

    {

        // modeless case - do not call base class OnCancel

        m_pView->PostMessage(WM_GOODBYE, IDCANCEL);

    }

    else

    {

        CDialog::OnCancel(); // modal case

    }

}

 

Visual C++ MFC source code segment

 

Listing 6.

 

// not really a message handler

void CMymfc9Dialog::OnOK()

{

    if (m_pView != NULL)

    {

        // modeless case -- do not call base class OnOK

        UpdateData(TRUE);

        m_pView->PostMessage(WM_GOODBYE, IDOK);

    }

    else

    {

        CDialog::OnOK(); // modal case

    }

}

 

Visual C++ MFC source code segment

 

Listing 7.

 

If the dialog is being used as a modeless dialog, it sends the user-defined message WM_GOODBYE to the view. We'll worry about handling the message later.

 

For a modeless dialog, be sure you do not call the CDialog::OnOK or CDialog::OnCancel function. This means you must override these virtual functions in your derived class; otherwise, using the Esc key, the Enter key, or a button click would result in a call to the base class functions, which call the Windows EndDialog() function. EndDialog() is appropriate only for modal dialogs. In a modeless dialog, you must call DestroyWindow() instead, and if necessary, you must call UpdateData() to transfer data from the dialog controls to the class data members. Edit the mymfc9View.h header file. You need a data member to hold the dialog pointer:

 

private:

    CMymfc9Dialog* m_pDlg;

 

Adding a data member/member variable to hold the dialog pointer.

 

Figure 18: Adding a data member/member variable to hold the dialog pointer.

 

Visual C++ MFC source code segment

 

Listing 8.

 

If you add the forward declaration:

 

class CMymfc9Dialog;

 

Visual C++ MFC source code segment

 

Listing 9.

 

At the beginning of mymfc9View.h, you won't have to include mymfc9Dialog.h in every module that includes mymfc9View.h.

 

Modify the CMymfc9View constructor and destructor in the file mymfc9View.cpp. The CMymfc9View class has a data member m_pDlg that points to the view's CMymfc9Dialog object. The view constructor constructs the dialog object on the heap, and the view destructor deletes it. Add the following code:

 

CMymfc9View::CMymfc9View()

{

    m_pDlg = new CMymfc9Dialog(this);

}

 

CMymfc9View::~CMymfc9View()

{

  // destroys window if not already destroyed  

  delete m_pDlg;

}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

----------------------------------------------------------------------------

Visual C++ MFC source code segment

 

Listing 10.

 

Add code to the virtual OnDraw() function in the mymfc9View.cpp file. The CMymfc9View OnDraw() function which skeleton was generated by AppWizard should be coded as follows in order to prompt the user to press the mouse button:

 

void CMymfc9View::OnDraw(CDC* pDC)

{

    pDC->TextOut(30, 30, "Press the left mouse button here.");

}

 

Visual C++ MFC source code segment

 

Listing 11.

 

Use ClassWizard to add CMymfc9View mouse message handlers. Add handlers for the WM_LBUTTONDOWN and WM_RBUTTONDOWN messages.

 

Adding handlers for the WM_LBUTTONDOWN and WM_RBUTTONDOWN messages.

 

Figure 19: Adding handlers for the WM_LBUTTONDOWN and WM_RBUTTONDOWN messages.

 

Now edit the code in file mymfc9View.cpp as follows:

 

void CMymfc9View::OnLButtonDown(UINT nFlags, CPoint point)

{

    // creates the dialog if not created already

    if (m_pDlg->GetSafeHwnd() == 0)

    {

        m_pDlg->Create(); // displays the dialog window

    }

}

 

void CMymfc9View::OnRButtonDown(UINT nFlags, CPoint point)

{

    m_pDlg->DestroyWindow();

    // no problem if window was already destroyed

}

 

Visual C++ MFC source code segment

 

Listing 12.

 

For most window types except main frame windows, the DestroyWindow() function does not destroy the C++ object. We want this behavior because we'll take care of the dialog object's destruction in the view destructor.

Add the dialog header include statement to file mymfc9View.cpp. While you're in mymfc9View.cpp, add the following dialog header include statement after the view header include statement:

 

#include "mymfc9Dialog.h"

 

 

Listing 13.

 

Add your own message code for the WM_GOODBYE message. Because ClassWizard does not support user-defined messages, you must write the code yourself. This task makes you appreciate the work ClassWizard does for the other messages.

 

In mymfc9View.cpp, add the following line after the BEGIN_MESSAGE_MAP statement but outside the AFX_MSG_MAP brackets:

 

ON_MESSAGE(WM_GOODBYE, OnGoodbye)

 

Visual C++ MFC source code segment

 

Listing 14.

 

Also in mymfc9View.cpp, add the message handler function itself:

 

LRESULT CMymfc9View::OnGoodbye(WPARAM wParam, LPARAM lParam)

{

    // message received in response to modeless dialog OK

    // and Cancel buttons

    TRACE("CMymfc9View::OnGoodbye %x, %lx\n", wParam, lParam);

    TRACE("Dialog edit1 contents = %s\n", (const char*) m_pDlg->m_strEdit1);

    m_pDlg->DestroyWindow();

    return 0L;

}

 

Visual C++ MFC source code segment

 

Listing 15.

 

In mymfc9View.h, add the following function prototype before the DECLARE_MESSAGE_MAP() statement but outside the AFX_ MSG brackets:

 

afx_msg LRESULT OnGoodbye(WPARAM wParam, LPARAM lParam);

 

Visual C++ MFC code segment

 

Listing 16.

 

With Win32, the wParam and lParam parameters are the usual means of passing message data. In a mouse button down message, for example, the mouse x and y coordinates are packed into the lParam value. With the MFC library, message data is passed in more meaningful parameters. The mouse position is passed as a CPoint object. User-defined messages must use wParam and lParam, so you can use these two variables however you want. In this example, we've put the button ID in wParam. Build and test the application. Build and run MYMFC9. Press the left mouse button and then press the right button. Be sure the mouse cursor is outside the dialog window when you press the right mouse button. Press the left mouse button again and enter some data, and then click the dialog's OK button. Does the view's TRACE statement correctly list the edit control's contents?

 

MYMFC9 program output.

 

Figure 20: MYMFC9 program output.

 

MYMFC9 program output, modeless dialog launched when the left mouse button clicked.

 

Figure 21: MYMFC9 program output, modeless dialog launched when the left mouse button clicked.

 

 

Figure 22.

 

If you use the MYMFC9 view and dialog classes in an MDI application, each MDI child window can have one modeless dialog. When the user closes an MDI child window, the child's modeless dialog is destroyed because the view's destructor calls the dialog destructor, which, in turn, destroys the dialog window.

 

Continue on next module...part 2.

 

 

 

 

Further reading and digging:

  1. MSDN MFC 7.0 class library online documentation.

  2. MSDN MFC 9.0 class library online documentation - latest version.

  3. Porting & Migrating your older programs.

  4. MSDN Library

  5. DCOM at MSDN.

  6. COM+ at MSDN.

  7. COM at MSDN.

  8. Windows data type.

  9. Win32 programming Tutorial.

  10. The best of C/C++, MFC, Windows and other related books.

  11. Unicode and Multibyte character set: Story and program examples.

 

 


 

| Tenouk C & C++ | MFC Home | Modal Dialog & Windows Common Controls 5 | Modeless Dialog & Windows Common Dialogs 2 | Download | Site Index |