How can I pre-select a view type of Thumbnails (or Details, etc.) when I use the Common Controls GetOpenFileName or GetSaveFileName API calls?
This question comes up occasionally here at Experts-Exchange and even a Google search will turn up lots of "answers" that say "It can't be done!" True, there is no API to control these things, but with a bit of spelunking with Spy++ and some trial-and-error hacking around, I found that it is possible. I created a C++ object, CFileDlgEx -- derived from the MFC CFileDialog class -- that does exactly that.
Note: The program code here does use MFC, but it should be useful even if you are
using Win32 API only, or Visual Basic, or any programming language that can access
Windows API calls.
With CFileDlgEx, you can pre-select the desired view:
- VwIcons Icon View
- VwList List View
- Details Detail View ("Report view" format)
- VwTiles Tile View (info about each file)
- VwThumbnails A small Image for graphics files
...and you can even select a desired sort order:
- ByName
- BySize
- ByType
- B
yModified (Newest first, etc.)
How is it done?
Microsoft must have had to do some complicated behind-the-scenes work to make the Common File Open/Save Dialogs work transparently. The HWND of the dialog box is actually a child of the real dialog. And once you climb up to its parent (the real dialog), you'll find that messages don't work quite as expected. There are WM_COMMAND messages that control View Type and Arrange By, but they don't go to the dialog, they go to one of its embedded child controls -- A SHELLDLL_DefView that wraps a standard list control (a SysList32 class window).
A sequence like:
...should get you the right window. Even knowing all of that, you are likely to run into strange problems -- the embedded target window doesn't exist when you expect it to. For instance, if you try to obtain the desired HWND in an OnDialogInit handler or an override of CFileDialog::OnInitDone, it will basically say "Huh?" -- the window has not been created yet.
However, an override of OnFileNameChange does the trick. Apparently the relevant notification gets made as a final step, just before the window is displayed -- when all the pieces are in place. At that point, it is valid to send the WM_COMMAND messages that will control the all-important listview.
Setting the Sort Order
I discovered the WM_COMMAND parameter values by monitoring the messagges with Spy++. The same WPARAM is used whether you click a column header (in Details view) or right-click and select an Arrange Icons By option. But there was a little catch...
Setting the sort order turns out to be tricker than it might seem. When you work with these settings manually (in Detail View) you only have the option of toggling the sort order, and sure enough, the available WM_COMMAND messages all mean, "Select that column and sort by it. If already selected, then toggle the current order between ascending or descending." That's awkward because I wanted to be able to specify a sort criteria and select the ascending or descending option.
I eventually hit upon this clever trick:
If you first sort by one column, then sort by another, the sort of the second is always Ascending (A-Z, old-to-new, etc.) So... to pre-set Ascending, first sort by a different column, then sort by the desired column. To pre-set Descending (which is especially useful for newest-to-oldest sorts), do the same thing but also just send the sortBy command again.
I ended up doing a "dummy sort" of ByAttributes -- a column that is not commonly displayed. But I don't think the choice is really important... Even on extremely large directories, the Windows sorting logic appears to be nearly instantaneous.
The FileDlgEx header file:
The FileDlgEx CPP file:
To use this class, just add the two files to your project. Use CFileDlgEx just like CFileDialog, but before calling the DoModal() member function, set values into m_eView, m_eSort, and optionally, m_fSortDescending. The valid values for m_eView and m_eSort are in the header file.
Sample testing function:
Notes:
- When in a mode other than Detail View, there is no sort toggling -- the sort always goes A-Z, old-to-new (try it manually by right-clicking and choosing "Arrange Icons By"). It might be possible to switch to Detail View, set the desired sort order, then switch to another view, but I had no use for that capability and I left that as an exercise to the reader.
- I found no way to query the dialog to find out what type of view or sort order that the user preferred. Thus, I have no way to remember the "current" setting and re-use it later. In my production code, I had to provide a global preference setting in an "options" dialog, and use those settings. If you find a way to obtain the final View Type and Sort values (i.e., after the user has made changes interactively in the dialog), please let me know.
- This code has been tried and verified on WinXP and Win2000. There's a chance that it will fail on Vista. It probably won't crash and burn, though.
- I mentioned that this technique could be used in different programming languages, but I did not specify how to do that... So, here goes:
1) Use your language/library's GetOpenFilename function. You should find that it lets you process events that are associated with the common dialog.
2) Write a handler for the "OnFileNameChange" event. In that handler, use Win API functions to get its parent and to get the child control of that parent that has a control ID of 0x0461 (1121, decimal).
3) Send that control window WM_COMMAND messages using the WPARAM values I've provided.
Late note: I was given a link to VB project that sets the initial view. If you are coding in VB, you can use the source code and techniques there and (optionally) add the additional code to pre-set the sort order based on what I've described in this article
References:
CFileDialog Class (MFC)
http://msdn.microsoft.com/
GetOpenFileName Function (Win32 API)
http://msdn.microsoft.com/
GetSaveFileName Function (Win32 API)
http://msdn.microsoft.com/
GetParent Function (Win32 API)
http://msdn.microsoft.com/
GetDlgItem Function (Win32 API)
http://msdn.microsoft.com/
SendMessage Function (Win32 API)
http://msdn.microsoft.com/
=-=-=-=-=-=-=-=-=-=-=-=-=-
If you liked this article and want to see more from this author, please click the Yes button near the:
Was this article helpful?
label that is just below and to the right of this text. Thanks!
=-=-=-=-=-=-=-=-=-=-=-=-=-
by: alainbryden on 2009-09-01 at 08:04:01ID: 3149
Good trick. You did some solid work on this one. I'll log it away for next time I want to do something fancy with file browsing.
Alain