This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


January 1998

Microsoft Systems Journal Homepage

C++ Q&A

Download Ccode.exe (23KB)

Paul DiLascia is the author of Windows ++: Writing Reusable Code in C++ (Addison-Wesley, 1992) and a freelance consultant and writer-at-large. He can be reached at askpd@pobox.com or http://pobox.com/~askpd.

Q I am writing an MFC app that is not a doc/view app. To implement the app with MFC, I used a document object that doesn't do anything. It all seems to work fine, but whatever I do, MFC gives my app a title like "untitled–DataWiz" (DataWiz is the name of my app). How can I get rid of the "untitled" part so only the name of my app appears?
Chad Frawkin

A Easy. All you have to do is turn off the flag FWS_ADDTOTITLE in your main frame window. The best place to do this is in your frame's PreCreateWindow function.

 BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs)
 {
     cs.style &= ~FWS_ADDTOTITLE;    // no title
     return CFrameWnd::PreCreateWindow(cs);
 }
Normally, MFC turns this flag on in CDocTemplate::CreateNewFrame, where MFC creates the frame.

 // in CDocTemplate::CreateNewFrame
 pFrame->LoadFrame(m_nIDResource,
                   WS_OVERLAPPEDWINDOW | FWS_ADDTOTITLE,
                   NULL, &context);
      If FWS_ADDTOTITLE is set, CFrameWnd::OnUpdateFrameTitle combines the name of the current document with the name of the app, which it gets from the resource string, whose ID is IDR_MAINFRAME or whatever ID you use for your document template. OnUpdateFrameTitle adds the title to the document name, as in "DataWiz–untitled"; FWS_PREFIXTITLE shows the doc name first, as in "untitled–DataWiz". By default, MFC sets FWS_PREFIXTITLE if FWS_ADDTOTITLE is set and the program is running in Windows® 95 or Windows NT® 4.0.

 // in CFrameWnd::PreCreateWindow
 if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)
     cs.style |= FWS_PREFIXTITLE;
      By turning the FWS_ADDTOTITLE style off, you turn off this behavior. It's important to turn off the flag before you call CFrameWnd::PreCreateWindow. If you want to turn the flag on or off after the frame is created, you can use ModifyStyle.

 // turn title feature off
 ModifyStyle(FWS_ADDTOTITLE, 0);
      You may wonder if there are other FWS styles. There's only one: FWS_SNAPTOBARS. This style controls the sizing of the frame that encloses a floating control bar. FWS_SNAPTOBARS makes the frame fit the control bar, something you'd never use in a normal frame window unless you want a very tiny frame with no client area. MFC uses FWS_SNAPTOBARS in CMiniDockFrameWnd, which is the miniframe that encloses a floating control bar. You might use FWS_SNAPTOBARS if you're implementing your own custom floating-style control bars.

Q I'm using the Windows 95 File Open and File Save common dialogs in my program, but since I'm using them for things other than opening and saving files (like creating, compacting, and repairing a database), I would like to replace the caption of the IDOK button. First I tried to use templates, but for some reason I couldn't manage to do it; my program kept crashing. So I thought I could get a CMyFileDialog class and use OnInitialUpdate to get the IDOK button and replace its caption. But whenever I try to get the button, MFC returns NULL. This is the last pesky detail left on a huge six-month project. What should I do?

Rui Santos
Portugal

A You had the right idea, but things are a little different with the new common file dialogs, which have evolved to new levels of sophistication and confusion—in Windows 95. But first, a quick historical perspective.
      Back in the old days, you had to write your own dialogs for opening and closing files using a strange Windows API function called DlgDirList that populated a listbox with file names. Ten thousand sixty-two programmers all wrote their own File Open dialogs using DlgDirList. Then someone in Redmond had a brilliant idea: since almost every app has to open and save files, why not incorporate the open and save dialogs in a DLL—aka subroutine—everyone could use? They must have been taking their smart pills! Programmers around the planet celebrated when COMMDLG.DLL came out, and we all rushed to use it in our apps.
      The COMMDLG functions that do dialogs are GetOpenFileName and GetSaveFileName. Both require an OPENFILENAME structure, which contains an intimidating array of parameters for controlling the dialog. You can specify style flags, filters, and your own title, among other things. You can even specify your own hook procedure in lpfnHook. MFC encapsulates all this in a class called CFileDialog. Somewhere in the bowels of CWinApp (actually, it's in CDocManager), MFC creates a CFileDialog to handle the Open and Save As commands, but you can create an open or save dialog anywhere in your program.

 CFileDialog dlg(TRUE); // open dialog
 dlg.DoModal(); // run it
      If you want the save dialog, create with FALSE instead of TRUE. The constructor has several other optional parameters with reasonable defaults you can override. For example, if you want a Read Only checkbox in your dialog, you can write:

 CFileDialog dlg(TRUE, NULL, NULL, 0);
The last zero replaces the MFC default flag OFN_HIDEREADONLY, which hides the checkbox. In any case, after DoModal returns, you can call any number of functions like dlg.GetPathName to get the name of the file the user chose.
      All this should be very simple, straightforward, and familiar. But behind the scenes, MFC does a lot of legwork to make CFileDialog look like any other MFC-style dialog object. In particular, it sets OPENFILENAME::lpfnHook to subclass the dialog in MFC's standard way, so all messages go through MFC's message map mechanism. So you can write ON_COMMAND handlers to handle button clicks or message handlers for messages like WM_SETFOCUS or WM_ ACTIVATE. If you want to change the name of one or more controls in a file dialog—or any dialog, for that matter—the simplest thing to do is override the virtual function OnInitDialog. OnInitDialog is different from other message handlers in that it's a virtual function. You don't need a message map entry for WM_INITDIALOG; all you have to do is override OnInitDialog.

 BOOL CMyDialog::OnInitDialog()
 {
     SetDlgItemText(IDOK, 
                   "Do it, Man!");
     SetDlgItemText(IDCANCEL,"Not!");
 return CFileDialog::OnInitDialog();
 }
It looks perfectly sane—but if you try it in Windows 95 or Windows NT 4.0, it won't work! With Windows 95 and Windows NT 4.0, the situation is completely different.
Figure 1 Flashy Explorer-look Dialog
Figure 1 Flashy Explorer-look Dialog
      When Windows 95 came out, the common file dialog got a facelift. Instead of the boring, pass-me-a-Prozac-please, flat-style dialogs with listboxes, you get the flashy, newfangled Explorer look with folder icons and toolbar buttons and all the cool stuff in Figure 1. But not only has the look changed, so has the subclassing mechanism. When you (or MFC) specify a window proc in lpfnHook, Windows 95 doesn't hook the dialog directly, but instead creates an empty child dialog, which is the hooked window. In MFC-speak this means that the CFileDialog object is not attached to the Explorer-style Open File dialog, but to this special empty child dialog (see Figure 2).
Figure 2 Old-style versus Explorer-style Dialog
Figure 2 Old-style versus Explorer-style Dialog

So if you try to set the text of a child button with SetDlgItemText(IDOK), it fails because the child dialog has no OK button—or indeed any controls at all. If you call GetDlgItem(IDOK) from within CMyDialog, Windows/MFC returns NULL. Instead, you have to write this:

 // from OnInitDialog
 GetParent()->SetDlgItemText(IDOK, _T("Do it, Man!"));
This works fine, but the official way to do it is to use a new function designed for just this purpose: CFileDialog::SetControlText.

 SetControlText(IDOK, _T("Do it, Man!"));
      CFileDialog::SetControlText sends the Explorer dialog (the parent of the CFileDialog) a special common dialog message: CDM_SETCONTROLTEXT. The dialog handles this message by setting the text of the corresponding item. There are other CDM_XXX messages and notifications as well; Figure 3 gives the complete list. MFC encapsulates all this nicely with wrappers that send the messages to the parent dialog and virtual functions you can override to handle notifications. Notice I said virtual; to handle the common dialog notifications, you don't have to add anything to your message map—just write the function. All message handlers should work this way, but that's another story.
Figure 4 Old Open Dialog
Figure 4 Old Open Dialog
Figure 5 New Open Dialog
Figure 5 New Open Dialog

      I wrote a program, OpenDlg, that shows how to change the names of control buttons in both the old (see Figure 4) and new (see Figure 5) Open dialogs. MFC lets you control which dialog it displays by setting the flag OFN_EXPLORER in OPENFILENAME::Flags. By default, MFC sets this flag if you're running Windows 95 or Windows NT 4.0.

 // (in CFileDialog::CFileDialog)
 if (afxData.bWin4)// Windows 95 or Windows NT 4.0  
     m_ofn.Flags |= OFN_EXPLORER;
Figure 6 Menu
Figure 6 Menu
      OpenDlg has a menu command (see Figure 6) that flips between styles. The code is in Figure 7. In addition to changing the names of controls, OpenDlg also shows how to handle common dialog notifications and how to add your own controls. My dialog adds a Press me! button and a read-only edit control that displays the CDN_XXX notifications as they arrive.
Figure 8 Add-on Template
Figure 8 Add-on Template
      So much for changing the names of existing controls—what if you want to add your own controls? It's easy. All you have to do is design the dialog with the controls you want and set it in the OPENFILENAME struct. Figure 8 shows how I designed the add-on template. Make sure you create your dialog as a child dialog with the Clip Siblings, No Borders, and No Title Bar styles. Positioning the controls so they line up perfectly with the ones in the Explorer dialog requires a bit of trial and error, but it's not too hard. Once you've created your template, you have to set it in the OPENFILENAME struct before you run the dialog. To be object-oriented about it, I wrote my own DoModal override.

 // (in CMyOpenDlg::DoModal)
 if (m_ofn.Flags & OFN_EXPLORER) {
     m_ofn.lpTemplateName = 
         MAKEINTRESOURCE(IDD_MYOPENDLG);
     m_ofn.Flags |= 
         OFN_ENABLETEMPLATE;
 }
      Now Windows uses the dialog resource IDD_MYOPENDLG for the child dialog. Any controls you define are true children of the child dialog (CMyOpenDlg), not the Explorer dialog, so you can handle notifications from your template controls the normal way—by adding message and ON_COMMAND handlers to your dialog class. Thus, CMyOpenDlg handles the Press me! button with an ON_COMMAND(ID_PRESS_ME) entry in its message map. Figure 9 shows a Spy++ screen with OpenDlg running and the child dialog highlighted. You can see the Explorer dialog with my custom title, "Like, pick a file, dude," all its controls, and my child dialog (with no name) with the Press me! button and edit control.
Figure 9 Spy++ Screen
Figure 9 Spy++ Screen

      You can customize the old-style file dialogs too, but it's more work. When you specify a different template with lpTemplateName and OFN_ENABLETEMPLATE, Windows uses your template to replace the entire dialog, as opposed to appending it. So you have to create a dialog with all the expected controls and all the expected IDs. The easiest way to find out what the IDs are supposed to be is to run the dialog and look at it with Spy++. Beyond a certain point, though, it's easier to just write your own dialog from scratch.
      For more poop on customizing Explorer-style dialogs, see "Explorer-Style Hook Procedures" in the Platform SDK docs.

Have a question about programming in C or C++? Send it to askpd@pobox.com

From the January 1998 issue of Microsoft Systems Journal.