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.
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.
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; // Make sure to display icon instead of text dwStyle |= BC_BTN_FLAT; // Flat button dwStyle &= ~BC_ICO_ARROWFOLDER; // Remove other icons dwStyle &= ~BC_ICO_FOLDER; dwStyle |= BC_ICO_EXPLORER; // Set the explorer icon dwStyle |= BC_CTL_FOLDERSONLY; // Browse for folders only m_wndBrowseCtrl.SetButtonStyle(dwStyle); // Apply
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."));
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); // TRUE-"open", FALSE-"save as" m_wndBrowseCtrl.SetDefExt(NULL); // Do not use any default extension 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")); // default path if (m_wndBrowseCtrl.DoModal() == IDOK) MessageBox(m_wndBrowseCtrl.GetPathName());
To browse for folders:
m_wndBrowseCtrl.SetButtonStyle(m_wndBrowseCtrl.GetButtonStyle() | BC_CTL_FOLDERSONLY); // Display an editbox on the dialog 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)// Multiple files selected { POSITION pos = m_wndBrowseCtrl.GetStartPosition(); while (pos != NULL) { CString sPathName = m_wndBrowseCtrl.GetNextPathName(pos); // Process the path name... } }
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); // Now ready to receive the notification!
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.
CBrowseCtrl::GetSelectedCount
which returns the number of items the user has selected in the most recent file/folder dialog that was terminated by IDOK
.SetPathName
and GetPathName
member functions.lParam
is now a pointer to this CBrowseCtrl
object.BC_CTL_ALLOWEDIT
is set. The return value of GetPathName
will also be properly affected.SetDialogTitle
and GetDialogTitle
.SetDialogBanner
and GetDialogBanner
.ModifyButtonStyle
to allow convenient style changing. GetDriveLetter
, GetDirectory
, GetFileName
, GetFileTitle
, GetFileExt
.