Our community of experts have been thoroughly vetted for their expertise and industry experience. Experts with Gold status have received one of our highest-level Expert Awards, which recognize experts for their valuable contributions.
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
ByModified (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:
#pragma once// CFileDlgExclass CFileDlgEx : public CFileDialog{ DECLARE_DYNAMIC(CFileDlgEx)public: CFileDlgEx(BOOL bOpenFileDialog, // TRUE for FileOpen, FALSE for FileSaveAs LPCTSTR lpszDefExt = NULL, LPCTSTR lpszFileName = NULL, DWORD dwFlags = OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, LPCTSTR lpszFilter = NULL, CWnd* pParentWnd = NULL); virtual ~CFileDlgEx(); typedef enum { // enumerated values for WM_COMMAND wParam VwNone= 0, VwIcons= 0x7029, VwList= 0x702b, VwDetails= 0x702c, VwTiles= 0x702e, VwThumbnails= 0x7031, } ViewType; typedef enum { ByNone= 0, ByName= 0x7602, BySize= 0x7603, ByType= 0x7604, ByModified= 0x7605, ByAttributes= 0x7608, // note, there are others... } SortBy; virtual void OnFileNameChange( ); // the only member override ViewType m_eView; // some new class variables SortBy m_eSort; bool m_fSortDescending;protected: bool m_fFirstPass; DECLARE_MESSAGE_MAP()};
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.
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
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= 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!
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
Our community of experts have been thoroughly vetted for their expertise and industry experience. Experts with Gold status have received one of our highest-level Expert Awards, which recognize experts for their valuable contributions.
When in the open/save dialog, right click and arrange by name/type/modified/..., then hold the Ctrl key and X out of the dialog box. Every time you use it, from closing out forward, it will present the setting you closed out with.
I want to add an edit box at the left top corner of the CFileDialog. And When user enters any string in it, I want to filter the list only with those which matches the string entered.
I do not want to use the explorer style of CFileDialog. I need to achieve the above functionality in Old Style CFileDialog.
I have tried the following way:
When user enters any text in search field, we invoke a event handler through which search results will be displayed in the list control of CFileDialog.
Retrieve the list control and delete all elements which do not match with search input.
Our community of experts have been thoroughly vetted for their expertise and industry experience. Experts with Gold status have received one of our highest-level Expert Awards, which recognize experts for their valuable contributions.
My first guess is that your l_pListCtrl->GetItemText() is failing. That is often a problem with techniques that request text across process boundaries (it's ususally not a problem when requesting a single integer). The way to find out is to breakpoint and review the value in l_csItemText. If, when you breakpoint on that line, you are seeing the expected text, then it might relate to the use of 8-bit vs. 16-bit text characters.
-- Dan
BTW, the best way to get an answer here at EE is to ask a question in the regular Q/A section rather than in an Article comment.
Comments (7)
Commented:
When in the open/save dialog, right click and arrange by name/type/modified/..., then hold the Ctrl key and X out of the dialog box. Every time you use it, from closing out forward, it will present the setting you closed out with.
Commented:
Commented:
I do not want to use the explorer style of CFileDialog. I need to achieve the above functionality in Old Style CFileDialog.
I have tried the following way:
When user enters any text in search field, we invoke a event handler through which search results will be displayed in the list control of CFileDialog.
Retrieve the list control and delete all elements which do not match with search input.
Open in new window
But somwhow it always deletes the last element irrespective of the index i pass.
Is there anyway i can get this work as i required.
Thanks
Author
Commented:-- Dan
BTW, the best way to get an answer here at EE is to ask a question in the regular Q/A section rather than in an Article comment.
Commented:
As you mentioned. I will put it up as a seperate question
View More