6.5 Dialogs

Microsoft Visual C++/Microsoft Foundation Classes


6.5. Dialogs

6.5.1. How do I center my dialog?

Use the CWnd::CenterWindow method accomplish this. I usually put it in my OnInitDialog overloaded function. Since CDialog is an ancestor of CWnd, you can call the method directly:

BOOL CMyDialog::OnInitDialog()
{     
    //Perform any other dialog initialization up here.
    CenterWindow();
    return TRUE;
}

[email protected], 6/1/95.

6.5.2. How do I get the 'old style' common dialogs on win95?

MFC detects if it is running on Win95, and if so, replaces the standard FileOpen Dialog with an explorer version of the FileOpen Dialog. You can prevent MFC from using this "explorer" version by adding the following line to your CFileDialog derived class constructor:

m_ofn.Flags &= ~OFN_EXPLORER;

[email protected] (Andy DeWolfe), via programmer.win32, 5/10/95

UPD!! 6.5.3. How do I subclass a win95 common dialog?

You can do it but Microsoft has made it much more difficult in Win95. You need to create a "child dialog template" (with the WS_CHILD style) and set it to m_ofn.lpTemplateName (making sure m_ofn.hInstance is set to your app instance). This template must *only* contain the controls that you are adding to the dialog (ie. NOT the whole dialog with the standard controls duplicated as in Win3.x).

When the dialog is invoked, your template will appear (by default) below the regular file dialog controls. If you put a static control with id stc32 (defined in include\dlgs.h), the common dialog code will rearrange things so that the original controls will appear wherever your put the stc32 control (you don't have to size it to match the common dlg code will do that for you).

You will need to supply m_ofn.lpfnHook and handle your additional controls through the hook proc. Note that since the system puts your dialog template ON TOP of the normal dialog, MFC message routing won't get to your controls so you can't code them through a message map in your CFileDialog derivative. If anybody has found a way around this, I'd love to hear it!!

This is very messy and Microsoft knows it. They promise a fix in MFC 4.0.[ed. note: This is much nicer in 4.0. There are virtuals to override for getting callbacks, plus it even handles the old and new style templates - pretty clever stuff!]

[email protected], Joe Janakovic, via programmer.win32, 6/10/95

This is actually a documentation bug ... I haven't found this information elsewhere.  If you're subclassing the common file dialog, and you want to close the dialog when the user presses a button, the entry in MSDN on OFNHookProc says that you should not use EndDialog, but that you should post a WM_COMMAND message to the main dialog (which is the parent of your customising dialog), specifying IDABORT as the control ID.  This works with the old-style, non-Explorer file dialog, but not with the Explorer-style dialog. You now have to use IDCANCEL; and this also works with the old-style dialog.

If you're using MFC, you can write the following:

if (GetParent() != GetTopLevelParent())
{
    // Explorer-style
    GetParent()->PostMessage(WM_COMMAND,
        MAKEWPARAM(IDCANCEL, BN_CLICKED));
}
else
{
    // old style
    PostMessage(WM_COMMAND,
        MAKEWPARAM(IDCANCEL, BN_CLICKED));
}

- rick cameron [ [email protected] ], 6/18/98

6.5.4. CDialog::Create() fails, what could be wrong?

  • Invalid HWND passed as a parent
  • Invalid dialog resource ID passed (be careful about numeric IDsvs. string IDs -- be careful with #define ID_MYDIALOG 0x1234 -- it is a "string" ID to the resource compiler).
  • One or more controls on your dialog could not be created, usually
  • Because of the use of a custom control that was not registered.
  • Calling EndDialog during the OnInitDialog message (or some other handler called early in the game)!
  • NULL HWND passed as parent when dialog has WS_CHILD style

That's about all I can think of right now,

Dean McCrory, MSMFC, 6/16/95

6.5.5. How do I create a toolbar/statusbar in a dialog?

There's a sample in the Microsoft Software Library, DLGCBR, that demonstrates how to do this. Basically there's four steps, outlined and then coded below……

To add a control bar to a dialog, you must create the control bar as usual, and then make room for the control bar within the client area of the dialog. For the control bar to function properly, the dialog must duplicate some of the functionality of frame windows. If you want ON_UPDATE_COMMAND_UI handlers to work for the control bars, you also need to derive new control bar classes, and handle the WM_IDLEUPDATECMDUI message. If your dialog is not the main window of your application, you will also need to modify its parent frame window to pass the WM_IDLEUPDATECMDUI message on to the dialog's control bars.

To make room for a control bar within the client area of the dialog, follow these steps in your dialog's OnInitDialog() function:

  1. Create the control bars.
CRect rcClientStart;
CRect rcClientNow;
GetClientRect(rcClientStart);
RepositionBars(AFX_IDW_CONTROLBAR_FIRST,
AFX_IDW_CONTROLBAR_LAST,0, reposQuery,rcClientNow);
  1. Figure out how much room the control bars will take by using the reposQuery option of RepositionBars():
CPoint ptOffset(rcClientStart.left - rcClientNow.left,
                rcClientStart.top - rcClientNow.top);
ptOffset.y += ::GetSystemMetrics(SM_CYMENU);
CRect rcChild;
CWnd* pwndChild = GetWindow(GW_CHILD);
while (pwndChild)
{
    pwndChild->GetWindowRect(rcChild);
    rcChild.OffsetRect(ptOffset);
    pwndChild->MoveWindow(rcChild, FALSE);
    pwndChild = pwndChild->GetNextWindow();
}
  1. Move all the controls in your dialog to account for space used by control bars at the top or left of the client area. If your dialog contains a menu, you also need to account for the space used by the menu:4. Increase the dialog window dimensions by the amount of space used by the control bars:
CRect rcWindow;
GetWindowRect(rcWindow);
rcWindow.right += rcClientStart.Width()
                  - rcClientNow.Width();
rcWindow.bottom += rcClientStart.Height() 
                   - rcClientNow.Height();
MoveWindow(rcWindow, FALSE);
  1. Position the control bars using RepositionBars().

To update the first pane of a status bar with menu item text, you must handle WM_MENUSELECT, WM_ENTERIDLE, and WM_SETMESSAGESTRING in your dialog class. You need to duplicate the functionality of the CFrameWnd handlers for these messages. See the CModelessMain class in the sample program for examples of these message handlers.

To allow ON_UPDATE_COMMAND_UI handlers to work for other status bar panes and for toolbar buttons, you must derive new control bar classes and implement a message handler for WM_IDLEUPDATECMDUI. This is necessary because the default control bar implementations of OnUpdateCmdUI() assume the parent window is a frame window. However, it doesn't do anything but pass the parent window pointer on to a function which only requires a CCmdTarget pointer. Therefore, you can temporarily tell OnUpdateCmdUI() that the parent window pointer you are giving it is a CFrameWnd pointer to meet the compiler requirements. Here's an example:

LRESULT CDlgToolBar::OnIdleUpdateCmdUI(WPARAM wParam,LPARAM lParam)
{
    if (IsWindowVisible())
    {
        CFrameWnd* pParent = (CFrameWnd*)GetParent();
        if (pParent)
        OnUpdateCmdUI(pParent, (BOOL)wParam);
    }
    return 0L;
}

To pass WM_IDLEUPDATECMDUI messages on to dialogs other than the main window, save dialog pointers in your frame window class and create a WM_IDLEUPDATECMDUI handler in that class. The handler should send the WM_IDLEUPDATECMDUI message on to the dialog child windows by using CWnd::SendMessageToDescendants(). Then perform default processing for the message within the frame window.

MS FAQ 6/25/95

6.5.6. Why isn't my CDialog::PreCreateWindow() getting called?

PreCreateWindow does not get called when you create a dialog box. If you would like to init some data/controls for a dialog box you have to trap the OnInitDialog message and do you stuff there. PreCreateWindow is use to modify params for a window that you are creating.

[email protected], mfc-l, 7/12/95

6.5.7. How do I embed a common dialog in a property page?

This question comes up frequently on the "MFC" forum of CompuServe and the simple answer - unfortunately - is that there is no way to do it :-(

[email protected], programmer.win32, 7/12/95

6.5.8. Why can't I DDX/DDV to initialize my CDialog controls?

You can't do anything with the dialog controls until your dialog is created - which doesn't happen until DoModal(). The standard way of overcoming the problems is to create member variables for the data, initialize them before calling DoModal and then transfer the values in OnInitDialog. Or perhaps in UpdateData(). Much like the ClassWizard member variables does it.

So have your dialog include a CStringList or CStringArray, put the values for the listbox in that and transfer them to the listbox in OnInitDialog. [etc...]

[email protected], programmer.controls, 7/11/95

Init your dialog in OnInitDialog. If necessary pass a pointer to your document to the constructor of your dialog (and save it in a private/protected m_pDoc member).

[email protected], programmer.controls, 7/11/95

6.5.9. How do I change the captions of a CPropertyPage?

You can change the label before adding the page to the property sheet in the following way. You have to derive a class from CPropertyPage and add a public function SetCaption which sets the caption.

void CPage1::SetCaption(char *str)
{
    m_strCaption = str; // m_strCaption is protected member of
                        //CPropertyPage
}

Now you can us the SetCaption() function in the following way.

CMySheet my("My PropSheet");
CPage1 p1;
p1.SetCaption(str); // Setting the caption
my.AddPage(&p1);
CAnotherSheet newps("New Sheet");
CPage1 p2;
p2.SetCaption(newstr);
newps.AddPage(&p2);
my.DoModal();

Ramesh, NetQuest., MSMFC 8/3/95

6.5.10. How do I trap F1 in my dialog?

The following Knowledge Base Article explains a way to trap the WM_KEYDOWN messages in the dialog box.

ID: Q117563, TITLE: How to Trap WM_KEYDOWN Messages in a CDialog

The next article explains how to provide context sensitive help in a dialog. It also points to sample code.

ID: Q110506, SAMPLE: Context Sensitive Help in a CDialog

Ramesh, NetQuest., MSMFC, 8/31/95

6.5.11. How do I change the icon for a dialog-only MFC application?

Add the following code to the InitInstance() for the CWinApp derived class:

BOOL CDialogTestApp::InitInstance()
{
    //...
#if(_MFC_VER >= 0x0300)
    SetClassLong(m_pMainWnd->m_hWnd, GCL_HICON,
                 (LONG)LoadIcon(IDC_ICONDIALOGAPP));
#else
    SetClassWord(m_pMainWnd->m_hWnd,GCW_HICON,
                 (WORD)LoadIcon(IDC_ICONDIALOGAPP));
#endif
    //...
    m_pMainWnd->ShowWindow(m_nCmdShow);
    return TRUE;
}

NEW!! 6.5.12. How to I get rid of the Help and Apply buttons on my CPropertySheet-derivative?

In the constructor for your CPropertySheet-derivative add the following lines:

m_psh.dwFlags |= PSH_NOAPPLYNOW;
m_psh.dwFlags &= ~PSH_HASHELP;

In the constructor for every CPropertyPage-derivative included in the Property Sheet, add the following line:

m_psp.dwFlags &= ~PSP_HASHELP; 

Eric Bergman-Terrell, [email protected], 5/16/97

NEW!! 6.5.13.  How do you add a resize 'handle' to a dialog?

The way I would handle this is to draw the control using DrawFrameControl( ... ) function (CDC:: and Win32 API):

void CMyDialog::OnPaint( )
{
    CDialog::OnPaint();
    CRect rc;
    GetClientRect( rc );
    rc.left = rc.right - GetSystemMetrics(SM_CXHSCROLL);
    rc.top = rc.bottom - GetSystemMetrics(SM_CYVSCROLL);
    CClientDC dc( this );
    dc.DrawFrameControl( rc, DFC_SCROLL, DFCS_SCROLLSIZEGRIP );
}

Don't forget to add the code for WM_NCHITTEST to return HTBOTTOMRIGHT in the area of your sizegrip.

UINT CMyDialog::OnNcHitTest(CPoint point)
{
    UINT ht = CDialog::OnNcHitTest(point);
    if( ht == HTCLIENT )
    {
        CRect rc;
        GetWindowRect( rc );
        rc.left = rc.right - GetSystemMetrics(SM_CXHSCROLL);
        rc.top = rc.bottom - GetSystemMetrics(SM_CYVSCROLL);
        if( rc.PtInRect( point ) )
            ht = HTBOTTOMRIGHT;
    }
    return ht;
}

Glenn Carr, [[email protected]], mfc-l, 6/27/98