Printer Friendly Version      Send     
Click to Rate and Give Feedback
Related Articles
Learn how to use Windows Presentation Foundation (WPF), XAML, and the deep XML support in Visual Basic to generate user interfaces dynamically.

By Beth Massi (October 2008)
We introduce you to the EDI functionality within BizTalk Server 2006 R2, illustrating schema creation, document mapping, EDI delivery and transmission, and exception handling.

By Mark Beckner (August 2008)
This month's column explains how to use Windows HTTP Services, or WinHTTP, the new, powerful API for implementing HTTP clients.

By Kenny Kerr (August 2008)
In this article, author John Torjo presents a guide to his C++ GUI library called eGUI++ and explains how it makes user interface programming easier.

By John Torjo (June 2008)
More ...
Articles by this Author
What's the deal with const functions, and lots more on the reasoning behind the design of the C++/CLI.

By Paul DiLascia (February 2007)
This month DLL problems, context menus, MFC strings to managed C++, and more.

By Paul DiLascia (October 2006)
This month Paul DiLascia teaches readers the right way to create dynamic dialogs, explains satellite DLLs and discusses language resource DLLs.

By Paul DiLascia (September 2006)
This month Paul DiLascia codes some Microsoft Office-style dialog box features.

By Paul DiLascia (August 2006)


By Paul DiLascia (July 2006)
Many of you are no doubt in the process of upgrading to Visual Studio® 2005, so I thought now would be a good time to relate some of my own experiences with the new compiler. What took me so long? Hey, I'm a retro kind of guy! Better late than never!.

By Paul DiLascia (June 2006)
This month: CWebVersion revisited using HTTP instead of FTP, and adding sounds to an MFC-based app.

By Paul DiLascia (May 2006)


By Paul DiLascia (April 2006)
More ...
Popular Articles
Learn how to automate custom SharePoint application deployments, use the SharePoint API, and avoid the hassle of custom site definitions.

By E. Wilansky, P. Olszewski, and R. Sneddon (May 2008)
This article presents an overview of the motivation behind new techniques that decompose problems into independent pieces for optimal use of parallel programming.

By David Callahan (October 2008)
Here we present a rundown of the various language paradigms of CLR-based languages via short language introductions and code samples.

By Joel Pobar (May 2008)
If you're unfamiliar with Windows Presentation Foundation (WPF), building that first Silverlight custom control can be a daunting experience. This article walks through the process.

By Jeff Prosise (August 2008)
More ...
Read the Blog
Well designed code keeps things that have to change together as close together in the code as possible and allows unrelated things in the code to change independently, while minimizing duplication in the code. In the October 2008 issue of MSDN Magazine, Jeremy Miller shows you some design ...
Read more!
The process for ink capture and analysis on the Tablet PC is straightforward in managed code. To the uninitiated developer, however, creating unmanaged Tablet PC applications can be rather daunting. In the October 2008 issue of MSDN Magazine, Gus Class a quick introduction to the Tablet PC ...
Read more!
Multicore systems are becoming increasingly prevalent, but the majority of software today will not automatically take advantage of this additional processing ability. And multithreaded programming, for anything but the most trivial of systems, is incredibly difficult and error prone today. In the October 2008 issue of MSDN ...
Read more!
Concurrent programming is notoriously difficult, even for experts. You have all of the correctness and security challenges of sequential programs plus all of the difficulties of parallelism and concurrent access to shared resources. In the October 2008 issue of MSDN Magazine, David Callahan describes ...
Read more!
A major advantage of AJAX and Silverlight applications is that they can transparently and continuously interact with a back-end service. The problem is that they run over HTTP, which wasn't designed with security in mind. In the September 2008 issue of MSDN Magazine, Dino Esposito shows you ...
Read more!
Unhandled exception processing shouldn't be a mystery. It's actually quite useful since it gives a crashing application an opportunity to perform last-minute diagnostic logging about what went wrong. In the September 2008 issue of MSDN Magazine, Gaurav Khanna discusses how ...
Read more!
More ...
New information has been added to this article since publication.
Refer to the Editor's Update below.

C++ At Work
Counting MDI Children, Browsing for Folders
Paul DiLascia

Code download available at: CAtWork0506.exe (195 KB)
Browse the Code Online

Q I'm writing a Multiple Document Interface (MDI) app using MFC. From the parent window, how can I check whether all the MDI child windows have been closed? If they are all closed, then I want to activate a panel in my main window.
Q I'm writing a Multiple Document Interface (MDI) app using MFC. From the parent window, how can I check whether all the MDI child windows have been closed? If they are all closed, then I want to activate a panel in my main window.
Ramesh

A Windows® and MFC don't provide any functions specifically to get the number of MDI child windows, but implementing what you want is easy enough. In fact, I can think of half a dozen ways to skin this cat. You could trap WM_CREATE/WM_DESTROY messages; you could install a Windows hook with SetWindowsHookEx; you could use EnumWindows to enumerate the child windows and count how many there are. But often the simplest solution is the one most easily overlooked.
A Windows® and MFC don't provide any functions specifically to get the number of MDI child windows, but implementing what you want is easy enough. In fact, I can think of half a dozen ways to skin this cat. You could trap WM_CREATE/WM_DESTROY messages; you could install a Windows hook with SetWindowsHookEx; you could use EnumWindows to enumerate the child windows and count how many there are. But often the simplest solution is the one most easily overlooked.
All you need to solve this problem—something that works whether you're using MDI or some other multiple-window user interface of your own design—is a lowly list. Figure 1 shows a class based on the Standard Template Library (STL) list that does the job. It's hardly even worth encapsulating, but I did just in case typing "push_back" is too weird for Windows-based programmers; CWinList lets you type "Add" instead. To use CWinList, just add a global instance somewhere, either as a global variable or data member in your main application class:
class CMyApp : public CWinApp {
public:
   CWinList m_winlist; // list of open windows
};
In order for your children to be tracked, all they have to do is add and remove themselves from the list as they're created and destroyed. The obvious place to do it is within the constructor and destructor for whichever window class you want to keep track of:
CMyView::CMyView()
{
   theApp.m_winlist.Add(this);
}
CMyView::~CMyView()
{
   theApp.m_winlist.Remove(this);
}
Alternatively, you can call Add and Remove from your OnCreate and OnDestroy handlers to ensure your list contains only window objects with valid HWNDs. Since CWinList is derived from list<CWnd*>, you have the full power of STL at your disposal. For example, you can use the STL list iterator to enumerate the windows in your list:
CWinList& wl = theApp.m_winlist;
for (CWinList::iterator it=wl.begin(); it!=wl.end(); it++) {
   CWnd* pWnd = *it;
   // do something
}
I wrote a little test program called WinCount that uses CWinList to count MDI child windows. Figure 2 shows it running. WinCount has a status bar panel in the lower-right corner that shows the number of windows open, and the application's About dialog lists the windows' captions. The About dialog uses CWinList::iterator to generate its feedback message; the status bar panel is a standard MFC indicator panel with an ON_UPDATE_COMMAND_UI handler that displays the number of views:
void CMainFrame::OnUpdateWinIndicator(CCmdUI* pCmdUI)
{
   CString s;
   s.Format(_T("Open:%d"), theApp.m_winlist.size());
   pCmdUI->SetText(s);
}
Figure 2 Counting MDI Children with WinCount 
List::size is an STL list method that returns the number of items in the list. Note that for WinCount, there's a one-to-one correspondence between views and MDI child windows, so counting the number of views is the same as counting the number of MDI children. If you have a more complex user interface with multiple views inside each MDI child frame, you'd have to override CMDIChildWnd to put the Add/Remove calls in your derived class, or you could use some other window class that appears exactly once in each child frame. You can use as many CWinLists as you want to keep track of different kinds of window classes.
I know we live in the heady age of MFC, .NET, and GUI Frameworks that do everything for you—but don't forget how to use basic data structures!
[ Editor's Update - 5/9/2005: In the original implementation of CWinList shown in Figure 1, CWinList is derived from list<CWnd*>. Generally, however, it is considered bad practice to derive from STL containers as doing so can sometimes lead to unpredictable results. As such, the download for this article contains a new version where CWinList is now a typedef. This requires using push_back and remove instead of Add and Remove.]

Q I'm building an app in C++ using Visual Studio® .NET and MFC. In my program, the user has to pick a folder in which to copy some files. I can run the OpenFileDialog to let the user select a file, but how do I get the Open dialog to display only folders? Almost every installation program I've seen displays a dialog that shows only folders, but I can't seem to find the right flags.
Q I'm building an app in C++ using Visual Studio® .NET and MFC. In my program, the user has to pick a folder in which to copy some files. I can run the OpenFileDialog to let the user select a file, but how do I get the Open dialog to display only folders? Almost every installation program I've seen displays a dialog that shows only folders, but I can't seem to find the right flags.
Laine Chandler

A The reason you can't find the right flags is you're looking at the wrong function! The File Open dialog (the Win32® GetOpenFileName or MFC's CFileDialog) doesn't do folders. To display the folder-browser dialog, you have to call a special shell function, SHBrowseForFolder. To use it, you stuff a BROWSEINFO struct with a bunch of information, then call SHBrowseForFolder. Windows displays a dialog like the one in Figure 3. Users can navigate the folder hierarchy, expand and collapse items, and select the folder they want.
A The reason you can't find the right flags is you're looking at the wrong function! The File Open dialog (the Win32® GetOpenFileName or MFC's CFileDialog) doesn't do folders. To display the folder-browser dialog, you have to call a special shell function, SHBrowseForFolder. To use it, you stuff a BROWSEINFO struct with a bunch of information, then call SHBrowseForFolder. Windows displays a dialog like the one in Figure 3. Users can navigate the folder hierarchy, expand and collapse items, and select the folder they want.
Figure 3 Calling SHBrowseForFolder 
Alas, using SHBrowseForFolder isn't quite as easy as CFileDialog. It requires at least a minimal understanding of shellspeak, which makes heavy use of COM, IShellFolder, and PIDLs. In case you don't already know, PIDL (pronounced "piddle") is short for pointer-to-item-ID-list. The actual C type is LPITEMIDLIST, or LPCITEMIDLIST for the const variety. A PIDL is a byte string the shell uses to identify shell objects like files and folders, as well as pseudo-objects like My Computer or My Network Places. For ordinary files and folders, the bytes are the path name in Unicode, but for other objects the bytes are different so don't rely on that. The important thing is that when your user finally chooses a folder, SHBrowseForFolder returns it as a PIDL. To get the path name, you have to convert.
Since SHBrowseForFolder is so useful, and since MFC doesn't have a class to encapsulate it, I decided to write one for you. (I'm so nice, I know.) CFolderDialog hides the coding grungies and makes browsing for folders as easy as eating pie. All you have to do is instantiate and call BrowseForFolder:
CFolderDialog dlg(this);
LPCITEMIDLIST pidl = dlg.BrowseForFolder(
   _T("Pick a folder, dude!"), BIF_RETURNONLYFSDIRS | BIF_STATUSTEXT);
CString path = dlg.GetPathName(pidl);
CFolderDialog even has a handy function to convert the PIDL to a CString path name. SHBrowseForFolder can return the selected folder as a TCHAR string if you give it a buffer in BROWSEINFO::pszDisplayName, but the display name returned is only the final part of the full path—for example "Photos" if the folder is C:\MyStuff\Pub\Photos. If you want the full path name, you have to convert the PIDL using SHGetPathFromIDList or my own GetPathName. If the display name is what you want, call CFolderDialog::GetDisplayName.
If all you want to do is let the user pick a folder, then BrowseForFolder and GetPathName are all you need. But SHBrowseForFolder can do lots more. As with GetOpenFileName, it lets you supply a callback function to customize its behavior. If you provide a BrowseCallbackProc in BROWSEINFO::lpfn, Windows will call it when stuff happens. For example, Windows sends BFFM_INITIALIZED when the folder dialog has initialized itself, and BFFM_SELCHANGED when the user selects a new folder. Your callback procedure can process these notifications and do whatever it wants. For example, you can enable or disable the OK button by sending BFFM_ENABLEOK, or change the button text with BFFM_SETOKTEXT. CFolderDialog replaces the C-style callback with C++ virtual handler functions in typical MFC fashion, so instead of writing a callback proc you derive from CFolderDialog and override virtual functions like OnInitialized and OnSelChanged. Internally, CFolderDialog uses its own callback that calls these methods.
To exercise the more advanced features of CFolderDialog, I wrote a test app called FolderPick. It has two commands that run the folder dialog. One command displays the "old-style" dialog; the other, the new. The new style (BIF_NEWDIALOGSTYLE) creates a larger, sizeable dialog with a Make New Folder button and—if you specify BIF_EDITBOX—an edit box where the user can type the folder name. Other flags include BIF_BROWSEFORCOMPUTER to show computers and BIF_BROWSEFORPRINTER for printers. BIF_RETURNONLYFSDIRS tells Windows to return only file system directories, not pseudo-folders like My Network Places, and BIF_STATUSTEXT creates a status window whose text you can set. (BIF_STATUSTEXT is not supported for new-style dialogs.) For the full list of flags, see the documentation for BROWSEINFO.
FolderPick derives a new class, CMyFolderDialog, with overrides for OnInitialized and OnValidateFailed. When the dialog is initialized, FolderPick sets the status text and changes the name of the OK button to "Choose Me!"
void CMyFolderDialog::OnInitialized()
{
   SetStatusText(_T("Nice day, isn't it?"));
   SetOKText(L"Choose Me!");
}
There are a couple of things to underscore here. First, CFolderDialog has wrappers like SetStatusText and SetOKText for folder dialog messages like BFFM_SETSTATUSTEXT and BFFM_SETOKTEXT. If you were programming in C, you'd call ::SendMessage; with CFolderDialog you just call the wrappers. The only caveat is that you can only call these wrappers from within your virtual notification handlers (OnInitialized, OnSelChanged, and the rest) because m_hWnd is valid only while the folder dialog is actually running, not before or after calling BrowseForFolder. Internally, CFolderDialog subclasses the folder dialog the first time its callback receives a notification. The second thing to notice is that some BFFM_ messages require Unicode strings, not LPCTSTRs. That's why "Choose Me!" in the snippet is a wide character string (prefixed with L).
The Microsoft documentation has a couple of minor errors I should point out in case you try to program SHBrowseForFolder in C. The documentation says to pass the string for BFFM_SETOKTEXT in WPARAM; actually, it's LPARAM. It also says that BFFM_SETSELECTION requires a Unicode string, but BFFM_SETSELECTION is available in both A and W flavors, so you can use LPCTSTR.
If you use BIF_EDITBOX with the new-style dialog, Windows displays an edit control where the user can type the name of the folder. If the user types something bad, Windows calls the browser proc with BFFM_VALIDATEFAILED. CFolderDialog processes this by calling OnValidateFailed. FolderPick overrides OnValidateFailed to display an error message like the one in Figure 4.
BOOL CMyFolderDialog::OnValidateFailed(LPCTSTR lpsz)
{
   MessageBox(...);
   return TRUE; // don't dismiss dialog
}
Figure 4 Error Message from FolderPick 
Another cool feature that SHBrowseForFolder supports is custom filtering. This lets you control which items appear in the folder dialog on a per-item basis. As with callbacks, the mechanics are a bit tedious. You have to implement a COM interface, IFolderFilter, with two methods: GetEnumFlags and ShouldShow. When the folder dialog sends your callback BFFM_IUNKNOWN, you have to QueryInterface the IUnknown it passes for IFolderFilterSite, then call IFolderFilterSite::SetFilter with your IFolderFilter. Now the folder dialog calls your IFolderFilter::ShouldShow to filter each item. You can return S_OK to show the item or S_FALSE to hide it. Once you've installed your filter, IFolderFilterSite is no longer needed so you can Release it.
Naturally, I encapsulated all of this, too. To use custom filtering, all you have to do is call BrowseForFolder with bFilter=TRUE, and override two virtual functions: OnGetEnumFlags and OnShouldShow. No need to deal with COM, QueryInterface, or IFolderFilter. Figure 5 and Figure 6 show the code that performs this magic. CFolderDialog implements its own IFolderFilter internally, one that calls the corresponding virtual CFolderDialog functions. CFolderDialog::OnIUnknown uses the Active Template Library (ATL) CComQIPtr smart pointer class to make COM coding a breeze.
If you decide to use custom filtering, be careful because it overrides flags like BIF_RETURNONLYFSDIRS (return only file system dirs). Just for fun, I decided to implement BIF_RETURNONLYFSDIRS myself for FolderPick by manually disabling the OK button if the item selected is not a file system folder. To check for a file system object, you'd think the proper way would be to call IShellFolder::GetAttributesOf and look for SFGAO_FILESYSTEM. But when I tried this, "My Computer" had the SFGAO_FILESYSTEM attribute even though it's not really a file system folder! Go figure. The only reliable way I could discover to tell if a shell object is really a file or folder is to call GetPathName and check for an empty string. This is what I did in CMyFolderDialog to disable the OK button for non-folders. Download the source from the MSDN®Magazine Web site for details.
Finally, in order to help you understand what happens and when, I sprinkled CFolderDialog liberally with TRACE diagnostics. Figure 6 shows a sample run. You can turn the diagnostics on or off by setting a global variable CFolderDialog::bTRACE. Of course, the diagnostics appear only in debug builds. If you download the code, you also get a free copy of TraceWin to view the diagnostics without running under the debugger.
SHBrowseForFolder has lots of flags and features I haven't covered, but whichever features you decide to use, CFolderDialog alleviates the grunt work and lets you program SHBrowseForFolder the MFC way. Happy programming!

Send your questions and comments for Paul to  cppqa@microsoft.com.


Paul DiLascia is a freelance software consultant and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). In his spare time, Paul develops PixieLib, an MFC class library available from his Web site, www.dilascia.com.

© 2008 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.
Page view tracker