Click to Rate and Give Feedback
Related Articles
Here we present techniques for programmatic and declarative data binding and display with Windows Presentation Foundation.

By Josh Smith (July 2008)
See how Windows Forms applications can be adapted to use the new .NET Add-in framework (System.AddIn) this month.

By Mueez Siddiqui (July 2008)
Chris Gray discusses custom applications he’s developed for Windows Home Server and explores applications for energy savings.

By Chris Gray (July 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
The .NET Compact Framework 3.5 provides a subset of Windows Communication Foundation (WCF) functionality that you can harness to communicate between Windows Mobile devices and desktop PCs. We'll show you how.

By Andrew Arnott (Launch 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)
Joel Pobar presents an introduction to how compilers work and how you can write your own compiler to target the .NET Framework.

By Joel Pobar (February 2008)
Kenny Kerr sings the praises of the new Visual C++ 2008 Feature Pack, which brings modern conveniences to Visual C++.

By Kenny Kerr (May 2008)
More ...
Read the Blog
SQL Server Data Services (SSDS) is a robust, scale-free data service that internally uses proven SQL Server technology and exposes its functionality over industry standard Web service interfaces. In the July 2008 issue of MSDN Magazine, David Robinson introduces ...
Read more!
Windows Presentation Foundation (WPF) offers excellent support for managing the display and editing of complex data. In the December 2007 edition of MSDN Magazine, John Papa did a great job of explaining essential WPF data binding concepts. ...
Read more!
The most fundamental form of Web testing is HTTP request/response testing. This involves programmatically sending an HTTP request to the Web application, fetching the HTTP response, and examining the response for an expected value. In the May 2008 issue of MSDN Magazine, Read more!
In the November issue of MSDN Magazine, Jeffrey Richter demonstrates some recent additions to the C# programming language that make working with the APM significantly easier. In the June ...
Read more!
The July 2008 issue of MSDN Magazine is now available online. Here's what's in the issue: Data Services: Develop ...
Read more!
The June 2008 issue features the first installment of a new MSDN Magazine column on software design fundamentals. We’ll discuss design patterns and principles in a manner that isn't bound to a specific tool or lifecycle methodology. In this issue, Jeremy Miller starts the Patterns in Practice column ...
Read more!
More ...
From the January 2002 issue of MSDN Magazine
MSDN Magazine
OpenDlg Fixes Preview Problems
Paul DiLascia
Download the code for this article: CQA0201.exe (63KB)
Browse the code for this article at Code Center: OpenDlg

R
ecently I got an e-mail from a reader in Germany who noted that the comment in my code "Compiles with Visual C++® 6.0 for Windows® 98 and probably Windows 2000 too" is, and I quote, "not very funny." He then pointed out: "In a week, Windows XP will be the system of choice and you're still writing for Windows 98."
      Well, never one to ignore readers, and having achieved my goal of sitting out one entire version cycle, I can happily report that I have now finally upgraded my desktop to Windows XP, two weeks before the official ship date. I have also, likewise, upgraded my comment to fit: "Compiles with Visual C++ 6.0 for Windows XP and probably Windows 2000 too." So, just remember, don't anyone ever call me a Luddite!

Q My program overrides the CFileDialog box to add a preview pane so when the user clicks on one of our files, it shows what the file contains. However, during development one of our testers found that if you select a file, then select a directory (without changing to that directory), the preview pane still displays the file. I handle OnFileNameChange (CDN_SELCHANGE) and in that function I call CFileDialog::GetPathName to get the selected file name. But GetPathName returns the name of the file, even when a directory is selected. I tried using other notifications and functions like CDN_FOLDERCHANGE and GetFileName, but nothing seems to work. How can I find out whether the user has clicked on a file or a directory?
Mike Liss

Q I have a problem with the standard file open dialog. Is there any way to know whether the user clicked on a file or a folder when my program gets the CDN_SELCHANGE notification? It seems that the OPENFILENAME struct doesn't get updated when switching between a file and the folder. It only gets updated when switching between files.
Shail Arora

A Time again to visit the file open dialog. And what a pesky dialog it is, too! It does indeed seem that on Windows 2000 (for those of you still using it), when the user selects a file, then a folder, GetPathName (CDM_GETFILEPATH) returns the name of the file previously selected, not the folder. Well what do you expect—perfection? Remember, it's these little quirks that keep programmers in business. If it were easy, your grandmother would be doing this stuff!

Figure 1 Spy++
Figure 1 Spy++

      To fix the problem, you have to get at the list control inside the dialog. Many of the common dialogs have controls with documented IDs like stc1 for a static text control and lst1 for a listbox; these symbols are #defined in <dlgs.h>. You might think the list control would be lst1, but spelunking with Spy (see Figure 1) reveals that the list control is actually contained in another window of class, SHELLDLL_DefView. The SHELLDLL_DefView window has ID lst2; the list control inside it has child ID 1. So to get the list control, you can write:
// from your CFileDialog derivate
CListCtrl* plc = (CListCtrl*)GetParent()->
  GetDlgItem(lst2)->GetDlgItem(1);
      Remember, when you customize the open file dialog, your CFileDialog is actually a child of the real dialog, which explains why you must use GetParent. (For details see my article "Give Your Applications the Hot New Interface Look with Cool Menu Buttons" in the January 1998 issue of MSJ.) The cast to CListCtrl* works as per the usual MFC trick, because CListControl has neither data members nor virtual functions; it's a pure wrapper class. (Since GetDlgItem returns a pointer to a temporary CWnd, not a CListCtrl, downcasting would normally be a major booboo.)
      Once you have a pointer to the list control, you can do whatever you like—within reason. For example, to get the selected path name, call CListCtrl::GetItemText and append the result to the current open folder (GetFolderPath/CDM_GETFOLDERPATH). Once you have the path name, how do you tell if it's a file or the folder? Here's how:
#include <sys/stat.h>
// test whether pathname is a folder
static BOOL IsFolder(LPCTSTR pathname)
{
  struct stat st;
  return stat(pathname, &st)==0 
    && (st.st_mode & _S_IFDIR);
}
Note, however, that just because the path name supplied is not a folder, doesn't mean you can therefore conclude it's a file! The item might be some other shell object like My Network Places (Network Neighborhood) or My Computer.
      I wrote a program, OpenDlg, to demonstrate how to do a preview dialog (see Figure 2). OpenDlg lets you select multiple items; the preview pane shows the first few lines of a .txt file if only one is selected. OpenDlg also has a Debug window that lists the selected items with "(FOLDER)" next to the ones that are folders. Figure 3 shows OpenDlg in action.

Figure 3 OpenDlg in Action
Figure 3 OpenDlg in Action

      OpenDlg uses a nifty helper class, CFileDlgHelper. To use it, all you have to do is instantiate and call Init.
class CMyOpenDlg ... {
protected:
  CFileDlgHelper m_dlghelper;
};
BOOL CMyOpenDlg::OnInitDialog()
{
  m_dlghelper.Init(this)
•••
}
Once initialized, you can use CFileDlgHelper to get the list control as well as item names and the "is folder" property. For example:
CListCtrl* plc = m_dlghelper.GetListCtrl();
POSITION pos = plc->     
  GetFirstSelectedItemPosition();
while (pos) {
  int i = plc->GetNextSelectedItem(pos);
  if (fdh.IsItemFolder(i)) {
    // display name with "(FOLDER)"...
  } else {
    // don't
  }
}
      If the selected item is a folder, OpenDlg blanks the preview pane, thus fixing the preview problem that Mike asked about. Of course, if you people weren't so behind the times, using Windows 2000 instead of Windows XP, you wouldn't have this problem! In Windows XP, OnFileNameChange/CDN_SELCHANGE returns the correct path name for files and folders. But you can still use CFileDlgHelper to get the list control, item names, or other cool stuff described in the next question, and you still need IsFolder to check for folders.

Q I tried to customize your CMyOpenDlg class, which appeared in the January 1998 issue of MSJ, but have had little success in making the Press me! button do something useful. I use OFN_ALLOWMULTISELECT to allow multi-file selection and specify *.txt as one of the filters. I want to add a Select All button to select all .txt files.
      Also, I want to use ON_UPDATE_COMMAND_UI to disable this button if there are no .txt files in the current directory. I've tried handling WM_KICKIDLE to activate idle processing so my ON_UPDATE_COMMAND_UI gets called, but to no avail. And within the button handler, I don't know how to update the contents of the listbox to indicate they are selected.
Graham Pearson

A Well, here we go again! If you understood the discussion in the previous questions, you know how to solve the last part of your problem—selecting all the items in the list control. Just use CFileDlgHelper to get the list control, and select all items with a .txt extension.
void CMyOpenDlg::OnSelectAll()
{
  CListCtrl* plc = m_dlghelper.GetListCtrl();
  for (int i=0; i<plc->GetItemCount(); i++) {
    CString fn = plc->GetItemText(i,0);
    if (IsTextFileName(fn)) {
      plc->SetItemState(i,LVIS_SELECTED,
        LVIS_SELECTED);
    }
  }
  plc->SetFocus();
}
      In fact, my OpenDlg program has a Select All button that does just this. As for ON_UPDATE_COMMAND_UI, that's a little more work. You will recall (you will recall!) from MFC 102 that UI update normally happens when your main message loop goes into idle state—that is, when there are no messages in the queue. But dialogs are different; when you run a modal dialog, MFC starts another message loop. When there are no messages waiting, CWnd::DoModal sends your dialog a WM_KICKIDLE message. So the normal way to hook up your dialog for idle UI processing is like so:
LRESULT CMyDialog::OnKickIdle(WPARAM wp, LPARAM lp)
{
  UpdateDialogControls(this, TRUE);
  return 0;
}
      CWnd::UpdateDialogControls sends the magic CN_UPDATE_COMMAND_UI message to your dialog, triggering your ON_UPDATE_COMMAND_UI handlers. But alas, this scheme doesn't work for the file open dialog. Why not? Because the CFileDialog override for DoModal doesn't run a message loop the normal way. Instead, it calls ::GetOpenFileName (or ::GetSaveFileName). These API functions run their own message loops, and there's no way to get inside them to do idle processing. Or is there?
      Whenever a modal dialog goes idle waiting for a message to arrive, the dialog sends its owner a WM_ENTERIDLE message. Hey, this is just the ticket to do the UI update thing! There are only a few details to fret. First, Windows sends WM_ENTERIDLE to the dialog's owner—in this case, your main frame—so you have to trap the message there. Next, Windows continues to send WM_ENTERIDLE as long as the dialog remains idle, whereas you only need to call UpdateDialogControls once. No problem, you can do the usual set-a-flag thing. And when do you reset the flag? Whenever the UI state could potentially change, which is after the dialog gets a WM_COMMAND or WM_NOTIFY message. So you have to trap those messages too, in the dialog.
      Since all this is such a bother, I encapsulated it into a new class, CFileDialogHelper. Once you call CFileDialogHelper from your dialog's OnInitDialog, you don't have to worry your little brain about a thing; all your ON_UPDATE_COMMAND_UI handlers magically work. How does CFileDialogHelper do it? By using my ubiquitous CSubclassWnd class, the one that lets you subclass windows in the Windows sense, by installing a new window proc ahead of the old one. In fact, CFileDialogHelper uses two CSubclassWnds: one to trap WM_ENTERIDLE sent to the dialog's parent, and another to trap WM_COMMAND or WM_NOTIFY sent to the dialog itself. When the main window gets WM_ENTERIDLE, CFileDialogOwnerHook intercepts it and updates your dialog controls.
LRESULT CFileDialogOwnerHook::WindowProc(...)
{
  if (msg==WM_ENTERIDLE) {
    if (m_pHelper->m_bUpdateUI) {
      m_pDlg->UpdateDialogControls(m_pDlg, FALSE);
      m_pHelper->m_bUpdateUI=FALSE;
    }
  }
  return CSubclassWnd::WindowProc(msg, wp, lp);
}
And when the dialog gets WM_NOTIFY or WM_COMMAND, CFileDialogHook resets the flag.
LRESULT CFileDialogHook::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
{
  if (msg==WM_COMMAND || msg==WM_NOTIFY) {
    m_pHelper->m_bUpdateUI = TRUE;
  }
  return CSubclassWnd::WindowProc(msg, wp, lp);
}
What could be easier? Once you know the voodoo. Note that there are two CSubclassWnd-derived classes, CFileDialogOwnerHook and CFileDialogHook; one to hook the main frame and one for the dialog itself, both hidden inside CFileDlgHelper. With CFileDlgHelper, the UI update handler for Select All looks just like you'd expect:
void CMyOpenDlg::OnUpdateSelectAll(CCmdUI* pCmdUI)
{
  CFileDlgHelper& fdh = m_dlghelper;
  CListCtrl* plc = fdh.GetListCtrl();
  for (int i=0; i<plc->GetItemCount(); i++) {
    if (IsTextFileName(fdh.GetItemName(i))) {
      pCmdUI->Enable(TRUE);
      return;
    }
  }
  pCmdUI->Enable(FALSE);
}
It uses the same IsTextFileName in the first snippet for OnSelectAll. IsTextFileName looks for a name that ends in .txt. But does this really work?
      There is a problem, a fatal flaw and fly in the ointment of OpenDlg. If the user has customized Explorer to hide extensions for known file types (see Figure 4), then .txt doesn't appear in the list control. CFileDlgHelper::GetItemName returns foo instead of foo.txt. In fact, if extensions are hidden, foo.txt, foo.jpg, and foo.doc all appear as foo. (Go ahead and try it.) So how do you know if foo is really foo or foo.txt?

Figure 4 Hiding Extensions for Known File Types
Figure 4 Hiding Extensions for Known File Types

      I'm sure there's an answer to this question, but to find out what it is, you'll have to read next month's column. Same bat time... Same bat channel...

Send questions and comments for Paul to cppqa@microsoft.com.
Paul DiLascia is a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++(Addison-Wesley, 1992). Paul's Web site is http://www.dilascia.com.

Page view tracker