6.3. Dialogs

6.3.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;
}

scot@stingray.com, 6/1/95.

6.3.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;

andyd@andyne.on.ca (Andy DeWolfe), via programmer.win32, 5/10/95

6.3.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!]

joej@golddisk.com, Joe Janakovic, via programmer.win32, 6/10/95

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

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

Dean McCrory, MSMFC, 6/16/95

6.3.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.3.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.

ewalker@tezcat.com, mfc-l, 7/12/95

6.3.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 :-(

chris@chrism.demon.co.uk, programmer.win32, 7/12/95

6.3.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...]

null@diku.dk, 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).

jhasling@gascad.co.at, programmer.controls, 7/11/95

6.3.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.3.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.3.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;
}