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:

  1. Closed File Folder image
  2. Selected File Folder image
  3. Open File Folder image
  4. Selected File Folder image

The other is to add the bitmaps to the CImageList object in any order you wish, but this will
add one more function call you’ll have to make. Once the bitmap resources have been added to
the CImageList object, call the function CDirectoryTree::SetBitmapOrder() to initialize the
image list in the CDirectoryTree object. If you added the images in a non-standard order, you
will also need to call the function CDirectoryTree::SetBitmapOrder(). (See the function
header in DirectoryTree.cpp for a description of the function paramters.) For example:

// 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());
}

Download demo executable – 7 KB

Download source – 24 KB

More by Author

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Must Read