
Introduction
Usually, when we need to add file/folder-browse features to our projects, we
have to do quite some trivia: first we must create a CEdit which
will display the path name, then we need to create a CButton and
handle its ON_BN_CLICKED event to launch the
CFileDialog and copy the path name to the CEdit.
Furthermore, if we want to make it look nice, we will have to include some icon
or bitmap resources to decorate the CButton. It could cause some
headache if we have multiple dialogs which all need the file/folder-browse
feature on them.
I have been thinking that it would be nice if there was such a control in
MFC, which integrates a CEdit and a CButton on it,
handles the control events, pops up the CFileDialog and updates
CEdit text automatically. And it'd be even nicer if that control
has some built-in images, that is, it has its own drawing capability without
requiring us to provide any image resource to it. And of course, some tooltips
wouldn't hurt... Unfortunately, there's no such control in MFC, but fortunately
we can always make our own.
CBrowseCtrl is made for meeting the exact needs I described in
the above paragraphs. It derives from CButton, has an editbox, a
browse button and a tooltip on it. It can draw some built-in images on the
browse button as well, so you don't need to include any icon or bitmap resources
for this control. It handles button events by itself so whenever the user clicks
on the browse button, a file/folder dialog pops up.
How to Use
You need to add source files BrowseCtrl.h and BrowseCtrl.cpp to
your workspace first, and include BrowseCtrl.h wherever needed. To create
the control, you can either use CBrowseCtrl::Create to create one
at runtime, or draw a CButton on the dialog template and bind it
with a CBrowseCtrl variable.
Code Samples
Specifying Control Styles
You may access the control styles anytime to change its appearance and
behavior. Multiple style flags may be combined using the "|"
operator. The valid style flags are:
|
Flag
|
Effects
|
BC_CTL_ALLOWEDIT |
Allows user to type in the editbox |
BC_CTL_FOLDERSONLY |
Browse for folders instead of files |
BC_BTN_LEFT |
Display the browse button on the left-side of the
editbox |
BC_BTN_FLAT |
Use flat button style |
BC_BTN_ICON |
Use icon, no text will be displayed |
BC_ICO_ARROWFOLDER |
The arrow-folder icon, must be combined with
BC_BTN_ICON |
BC_ICO_FOLDER |
The folder icon, must be combined with
BC_BTN_ICON |
BC_ICO_EXPLORER |
The Windows Explorer icon, must be combined with
BC_BTN_ICON |
To set the "Windows Explorer" icon on the browse button, make the button
flat, and finally, make the control "Browse for Folders" only:
DWORD dwStyle = m_wndBrowseCtrl.GetButtonStyle();
dwStyle |= BC_BTN_ICON; dwStyle |= BC_BTN_FLAT; dwStyle &= ~BC_ICO_ARROWFOLDER; dwStyle &= ~BC_ICO_FOLDER;
dwStyle |= BC_ICO_EXPLORER; dwStyle |= BC_CTL_FOLDERSONLY; m_wndBrowseCtrl.SetButtonStyle(dwStyle);
I've got tired of images and want the browse button to display text, say,
"Click Me!", also make it display tooltip "Click to browse your files.":
m_wndBrowseCtrl.SetButtonStyle(m_wndBrowseCtrl.GetButtonStyle() & ~BC_BTN_ICON);
m_wndBrowseCtrl.SetButtonText(_T("Click Me!"));
m_wndBrowseCtrl.SetTooltipText(_T("Click to browse your files."));
Browsing for Files and Folders
CBrowseCtrl handles the button events by itself so whenever the
user clicks on the browse button, a file or folder dialog is popped up
automatically. You can, of course, programmatically handle it in your code also,
usually you may want to initialize the file dialog using whatever you would pass
into the CFileDialog constructor. CBrowseCtrl provides
a set of functions to allow doing the same initialization, you basically can
just treat the control as a dialog:
m_wndBrowseCtrl.SetOpenSave(TRUE); m_wndBrowseCtrl.SetDefExt(NULL); m_wndBrowseCtrl.SetFilter(_T("Text Files (*.txt)|*.txt|All Files (*.*)|*.*||"));
m_wndBrowseCtrl.SetFileFlags(OFN_FILEMUSTEXIST | OFN_HIDEREADONLY);
m_wndBrowseCtrl.SetPathName(_T("c:\\MyApp\\readme.txt"));
if (m_wndBrowseCtrl.DoModal() == IDOK)
MessageBox(m_wndBrowseCtrl.GetPathName());
To browse for folders:
m_wndBrowseCtrl.SetButtonStyle(m_wndBrowseCtrl.GetButtonStyle()
| BC_CTL_FOLDERSONLY);
m_wndBrowseCtrl.SetFolderFlags(BIF_EDITBOX);
m_wndBrowseCtrl.SetFolderDialogTitle(_T("Please select a folder"));
if (m_wndBrowseCtrl.DoModal() == IDOK)
MessageBox(m_wndBrowseCtrl.GetPathName());
And if the user has selected multiple files from the file dialog, you can
iterate through all path names by the following code:
if (m_wndBrowseCtrl.GetSelectedCount() > 1){
POSITION pos = m_wndBrowseCtrl.GetStartPosition();
while (pos != NULL)
{
CString sPathName = m_wndBrowseCtrl.GetNextPathName(pos);
}
}
Notifying the Parent Window
After the file/folder dialog is closed by the user, a notifying message is
sent to the parent window. The wParam is either IDOK
or IDCANCEL, the lParam is a pointer to this
CBrowseCtrl object.
To make the control notify the parent window, you need to provide
CBrowseCtrl a custom window message which is greater than 0. If the
message you provide is 0, the message will not be sent. If you do not provide
any message, no message will be sent either.
#define WM_BROWSE_NOTIFY WM_APP + 100
m_wndBrowseCtrl.SetNotifyMessageID(WM_BROWSE_NOTIFY);
Conclusion
So that's about it. It is a nice little control to have around, I hope it
could help you in some cases. Any suggestions are welcome, thanks for your
time.
History
Jan 08, 2004
Jan 09, 2004
- Improved drawing functions to properly handle the case in which the
control client area is too small to be drawn. In debug mode, it will provide a
message box to inform the developer about that issue.
- Improved the edit box so it acts better upon receiving and losing the
input focus.
- Updated the source code and demo project.
Jan 10, 2004
- Fixed a problem where the edit box is not properly redrawn upon certain
events. Thanks to JOHN11.
- Added method
CBrowseCtrl::GetSelectedCount which returns the
number of items the user has selected in the most recent file/folder dialog
that was terminated by IDOK.
- Improved the mouse/focus events monitoring functions.
- Fixed a drawing glitch which could occur when the user clicks on the edges
of the edit box.
- Changed the drawing-area calculating methods for performance improvement.
- Updated the source code and demo project.
Jan 14, 2004
- Updated
SetPathName and GetPathName member
functions.
- Altered the message sending method so the
lParam is now a
pointer to this CBrowseCtrl object.
- Updated the source code and demo project.
Jan 22, 2004
- Added methods to monitor whether the user has manually changed the
contents of the edit box when
BC_CTL_ALLOWEDIT is set. The return
value of GetPathName will also be properly affected.
- The window titles of file/folder dialogs can now be accessed by calling
SetDialogTitle and GetDialogTitle.
- The banner text of folder dialogs can now be accessed by calling
SetDialogBanner and GetDialogBanner.
- Added method
ModifyButtonStyle to allow convenient style
changing.
Feb 07, 2004
- Improved drawing functions so that images/text on the browse button are
partially drawn if there is not enough space.
May 22, 2004
- Updated drawing functions.
- Added functions:
GetDriveLetter, GetDirectory, GetFileName, GetFileTitle, GetFileExt.