Directory Selection Components in MFC
Have you ever written an application which asks the user to select a directory? Have you ever wanted to provide them a nice, neat way to do that without forcing them to manually type in the path? Well, now you can!! I have created two controls that provide you the bacic functionality to make a dialog like the one above. Yes, this is a screen shot of my demo application that uses the controls I created. It was patterned off of the "Choose Directory" dialog that appears when you select a directory when creating a new project in Microsoft* Visual C++ 5.0. (To see this for yourself, choose "File | New" from the menu, then click on the "..." button to the right of the "Location" edit box.)
The controls I implemented are:
- CDirectoryTree: CDirectoryTree is based off of CTreeCtrl and automatically fills itself in with the directory information. It does not display any files underneath the directories, only the directories themselves. As users change directories, only the current path to the directory and its children will be displayed. This prevents the control from appearing cluttered, and the user can easily see the direct path from the root down to the current directory. This is the same behavior as exhibited by the "Choose Directory" dialog in MSVC++ 5.0.
- CDriveCombo: CDriveCombo is based off of CComboBox and adds a function which will find all the logical drives on the system, finds the volume name for each found drive, and populates itself with that information. It then determines the current drive and highlightes that item. Included are functions to test if the drive selected by the user is ready, and to revert the selection to the last known good drive.
This project is somewhat similar to the one in the article posted by Tom Werner Halvorsrxd on January 08 1999, but it has a completely different look and feel and is intended to provide a neat, orderly way for users to be able to select a complete, correct path name. It improves on the listing of logical drives on the system by providing the volume label (if it exists), and it provides a hierarchical view of the path from the root to the current directory. By the addition of a read-only edit box, the entire path is displayed in plain text for easy reading.
All the directions on including these components into your project are also included in the file Readme.txt in the source archive.
First, include the appropriate files for the controls you want to use. CDirectoryTree is implemented in files DirectoryTree.cpp and DirectoryTree.h, and CDriveCombo is implemented in files DriveCombo.cpp and DriveCombo.h. You will also have to include the four file folder bitmaps from my demo project, or you will have to provide four bitmaps of your own design. The bitmaps from this project are named closed.bmp, closedSel.bmp, open.bmp, and openSel.bmp.
Next, create your dialog with the controls you want to use. Using Class Wizard, create member variables for those controls using the appropriate class. If you want to have an edit box or static text to display the path as a readable string, include an edit control on the dialog and create a member variable for it. For example:
CDirectoryTree m_DirTree; CDriveCombo m_DriveList; CEdit m_DirName;Now, you must initialize the image list associated with the CDirectoryTree object. Create a CImageList object and add the four bitmaps you added to the project to that object. There are two ways you can do this. One is to add all the bitmaps in this predefined order:
- Closed File Folder image
- Selected File Folder image
- Open File Folder image
- Selected File Folder image
// Set up the image list for the tree control
CImageList * pImageList;
pImageList = new CImageList();
pImageList->Create(16, 16, ILC_COLOR, 4, 4);
// Load the bitmaps. This must be done before calling Initialize()
// on the CDirectoryTree object.
// NOTE: IDB_FIRST_TREE_ICON = ID of the first bitmap resource,
// IDB_LASE_TREE_ICON = ID of the last bitmap resource.
// Of course, you can choose any method you want to build this list.
CBitmap bBitmap;
for (int nID = IDB_FIRST_TREE_ICON; nID <= IDB_LAST_TREE_ICON; nID++)
{
bBitmap.LoadMappedBitmap(nID);
pImageList->Add(&bBitmap, (COLORREF)0x000000);
bBitmap.DeleteObject();
}
m_DirTree.SetBitmapList(pImageList);
// If the bitmaps were loaded in a non-standard order, call this function.
// Otherwise, the default order will be used.
m_DirTree.SetBitmapOrder(1,3,2,0);
Next, you must initialize the controls before displaying them, preferably in your
application's initialization routine (such as OnInitDialog()). To initialize the controls,
simply call their member function Initialize(). To initialize the edit control with the
current directory, call the member function CString CDirectoryTree::GetCurrentDir().
For example:
m_DriveList.Initialize(); m_DirTree.Initialize(); m_DirName.SetWindowText(m_DirTree.GetCurrentDir());At this point, you'll be able to get a dialog that appears just like the one in the screen shot at the top of this article. Now, you just need to enable communication between the controls.
To be notified that the user has selected a new drive, use the Class Wizard to map a function to the CBN_SELCHANGE message. You can use all the standard CComboBox functions to retrieve the letter of the drive chosen by the user. When the user selects a new drive from the CDriveCombo control, the new drive letter must be communicated to the CDirectoryTree control and the control must be reinitialized. Call the function SetCurrentDrive(char), passing in the letter of the new drive, to communicate the change to the control. Then, call Initialize() to reinitialize the control. For example:
// Handler for the CBN_SELCHANGE message
void CMyDialog::OnSelchangeDriveCombo()
{
int nSelected = 0;
CString szText;
// Determine the new drive letter and test to see if it's valid.
nSelected = m_DriveList.GetCurSel();
if (!m_DriveList.IsDriveReady(nSelected))
{
// The drive wasn't valid!! Revert to the previous one
m_DriveList.ResetDrive(m_DirTree.GetCurrentDrive());
return;
}
// Get the drive letter and communicate it to the tree.
m_DriveList.GetLBText(nSelected, szText);
m_DirTree.SetCurrentDrive(szText[0]);
// Re-initialize the tree to show the new drive's directories.
m_DirTree.Initialize();
m_DirName.SetWindowText(m_DirTree.GetCurrentDir());
}
When the user chooses a new directory, the edit box must also be updated to display the
new, complete path. When the user double-clicks on a new directory name, a user-defined
message is passed to the control's parent. To catch this message, implement the function
PreTranslateMessage() in your application and look for message ID
WM_USER_PATHCHANGED (defined in DirectoryTree.h). DO NOT IMPLEMENT YOUR OWN
HANDLER AND MESSAGE MAP ENTRY. I am not sure why, but doing so will cause an
Access Violation error and will crash your application. However, catching the
message within PreTranslateMessage() has been very stable and reliable. Once
you have caught this message, you know that the path has changed. To get the
new path string, use function CString CDirectoryTree::GetCurrentDir(). For example:
// Implementation of PreTranslateMessage()
BOOL CChooseDirDlg::PreTranslateMessage(MSG* pMsg)
{
if (WM_USER_PATHCHANGED == pMsg->message)
{
OnPathChanged();
return TRUE;
}
return CDialog::PreTranslateMessage(pMsg);
}
// Handler for the user-defined message WM_USER_PATHCHANGED
void CChooseDirDlg::OnPathChanged()
{
m_DirName.SetWindowText(m_DirTree.GetCurrentDir());
}

Comments
icon`s flag
Posted by Legacy on 01/02/2001 12:00amOriginally posted by: hiro
ReplyThanx .....!
Posted by Legacy on 12/12/2000 12:00amOriginally posted by: Dillip Kumar Kara
I got what exactly I was looking for ..Thanx a lot
ReplyWanting the moon (showing greyed out files)
Posted by Legacy on 08/24/1999 12:00amOriginally posted by: Jeff Donner
Hi,
How about adding the ability to show the files beneath the directory, greyed out? A user might have several directories holding similar files, and might want reassurance that they've picked the right one by seeing the right set of files beneath it. Of course you can't select the files, but they're there, providing context for the user.
Just a suggestion. I haven't seen this implemented anywhere else.
Jeff
ReplyWith network capability see.....
Posted by Legacy on 07/28/1999 12:00amOriginally posted by: John McTainsh
The sample at /treeview/PathPicker.shtml
Replycan be used in this manner and also has network support. It can be uset to set an initial directory and browse the network neighbourhood.
Small suggestion ;-)
Posted by Legacy on 07/28/1999 12:00amOriginally posted by: George
Hi,
It is nice. I would use more nice icons thought(actually I did some time ago doing the same thing), this ones are looking more like VB, or Borland Delphi style. Anyway, I think that the root directory should be indicated like that: "C:\".
Also the whole thing have some flash when changing small amount of directories. It should not redraw all the contents.
BTW, there a cool class in the Shell programming section here using SHBrowseForFolder.
George
ReplyWhy didn't you include your project .dsp file?
Posted by Legacy on 07/19/1999 12:00amOriginally posted by: Jim Ryan
I extracted your source code but there is no project file (ChooseDir.dsp) in the archive. VC6.0 is looking for it in the workspace (ChooseDir.dsw).
Reply
Why don't...
Posted by Legacy on 06/18/1999 12:00amOriginally posted by: Paulo
ReplyHow to fix memory leak in DirectoryTree.cpp
Posted by Legacy on 06/08/1999 12:00amOriginally posted by: Diana Cook
ReplyWhy there is an Access Violation
Posted by Legacy on 05/20/1999 12:00amOriginally posted by: Henry
\\\\If you add a message map hander for the changed selection, it tries to select an item before the window is ready - put a flag in to tell it to exit the OnSelChanged method until its ready, e.g. when you get WM_INIT_DIALOG or set a timer.
That enables the program to begin - the 2nd problem is that the function cannot call Initialise a second time because it goes into an eternal loop. Here you really do need a timer.
HTH
ReplyHenry
/controls/choosedir.shtml
Posted by Legacy on 05/18/1999 12:00amOriginally posted by: Igor
Hi,
I downloaded (for several times) this nice "choosedir" piece of code, but *.dsp file is missing...
(or maybe something's wrong with me?!)
thanks,
ReplyIgor
Loading, Please Wait ...