Tree Views

[Previous] [Next]

MFC's CTreeView class enables programmers to create views similar to the one featured in the left pane of Windows Explorer. Tree views display treelike structures containing items composed of text and images. Items can have subitems, and collections of subitems, or subtrees, can be expanded and collapsed to display and hide the information contained therein. Tree views are ideal for depicting data that's inherently hierarchical, such as the directory structure of a hard disk. If you do even a moderate amount of Windows programming, you'll probably find plenty of uses for tree views.

CTreeView is a relatively simple class because it derives most of its functionality from the tree view control, which is one of the members of the common controls library Microsoft Windows 95 introduced to the world. In MFC, CTreeCtrl provides the programmatic interface to tree view controls. A tree view is programmed by calling CTreeCtrl functions on the underlying tree view control. The CTreeView function GetTreeCtrl returns a CTreeCtrl reference to that control. Thus, to determine how many items a tree view contains, you don't use a CTreeView function; instead, you call CTreeCtrl::GetCount, like this:

 UINT nCount = GetTreeCtrl ().GetCount ();

This paradigm—call a member function of the view to acquire a reference to the corresponding control—is shared by all of MFC's CCtrlView-derived classes.

Initializing a Tree View

A tree view control supports several special window styles that influence its appearance and operation. Six of those styles are available on all systems running Windows 95 or later or Microsoft Windows NT 3.51 or later; additional styles are available on systems on which Internet Explorer 3.0 is installed, and even more styles are supported on systems equipped with Internet Explorer 4.0 or later. (For a discussion of the interdependencies between the common controls and Internet Explorer, see Chapter 16.) You can apply any of the supported styles to a tree view by ORing them into the style field of the CREATESTRUCT structure passed to PreCreateWindow. The six styles available to all tree views are listed in the following table.

Tree View Styles

Style Description
TVS_HASLINES Adds lines connecting subitems to their parents.
TVS_LINESATROOT Adds lines connecting items at the top level, or root, of the hierarchy. This style is valid only if TVS_HASLINES is also specified.
TVS_HASBUTTONS Adds buttons containing plus or minus signs to items that have subitems. Clicking a button expands or collapses the associated subtree.
TVS_EDITLABELS Enables in-place label editing notifications.
TVS_DISABLEDRAGDROP Disables drag-and-drop notifications.
TVS_SHOWSELALWAYS Specifies that the item that's currently selected should always be highlighted. By default, the highlight is removed when the control loses the input focus.

Each item in a tree view control consists of a text string (also known as a label) and optionally an image from an image list. The image list is another of the control types introduced in Windows 95. In MFC, image lists are represented by instances of the class CImageList. Think of an image list as a collection of like-sized bitmaps in which each bitmap is identified by a 0-based index. The statements

 CImageList il; il.Create (IDB_IMAGES, 16, 1, RGB (255, 0, 255));

create an image list from a bitmap resource (ID=IDB_IMAGES) containing one or more images. Each image is 16 pixels wide, as indicated by Create's second parameter. The COLORREF value in the final parameter specifies that magenta is the image lists's transparency color. When images from the image list are displayed in a tree view, only the nonmagenta pixels will be displayed.

If you want to include images as well as text in a tree view, you must create and initialize an image list and use CTreeCtrl::SetImageList to assign it to the tree view. If il is a CImageList object, the statement

 GetTreeCtrl ().SetImageList (&il, TVSIL_NORMAL);

associates the image list with the control. TVSIL_NORMAL tells the tree view that the images in the image list will be used to represent both selected and unselected items. You can assign a separate TVSIL_STATE image list to the tree view to represent items that assume application-defined states. Note that the image list must not be destroyed before the tree view is destroyed; if it is, the images will disappear from the control.

CTreeCtrl::InsertItem adds an item to a tree view control. Items are identified by HTREEITEM handles, and one of the parameters input to InsertItem is the HTREEITEM handle of the item's parent. A subitem is created when an item is added to a tree view and parented to another item. Root items—items in the uppermost level of the tree—are created by specifying TVI_ROOT as the parent. The following code sample initializes a tree view with the names of two 1970s rock groups along with subtrees listing some of their albums:

 // Root items first, with automatic sorting. HTREEITEM hEagles = GetTreeCtrl ().InsertItem (_T ("Eagles"),     TVI_ROOT, TVI_SORT); HTREEITEM hDoobies = GetTreeCtrl ().InsertItem (_T ("Doobie Brothers"),     TVI_ROOT, TVI_SORT); // Eagles subitems second (no sorting). GetTreeCtrl ().InsertItem (_T ("Eagles"), hEagles); GetTreeCtrl ().InsertItem (_T ("On the Border"), hEagles); GetTreeCtrl ().InsertItem (_T ("Hotel California"), hEagles); GetTreeCtrl ().InsertItem (_T ("The Long Run"), hEagles); // Doobie subitems third (no sorting). GetTreeCtrl ().InsertItem (_T ("Toulouse Street"), hDoobies); GetTreeCtrl ().InsertItem (_T ("The Captain and Me"), hDoobies); GetTreeCtrl ().InsertItem (_T ("Stampede"), hDoobies); 

Passing a TVI_SORT flag to InsertItem automatically sorts items added to the tree with respect to other items in the same subtree. The default is TVI_LAST, which simply adds the item to the end of the list. You can also specify TVI_FIRST to add an item to the head of the list.

That's one way to add items to a tree view control. You also have several other options for adding items because CTreeCtrl provides four different versions of InsertItem. Let's take the example in the previous paragraph a little further and assume that you'd like to include images as well as text in the tree view items. Suppose you've created an image list that contains two images. Image 0 depicts a guitar, and image 1 depicts an album cover. You'd like guitars to appear alongside the names of the rock groups and album images to appear next to album titles. Here's what the code to initialize the control looks like:

 // Add the image list to the control. GetTreeCtrl ().SetImageList (pImageList, TVSIL_NORMAL); // Root items first, with automatic sorting HTREEITEM hEagles = GetTreeCtrl ().InsertItem (_T ("Eagles"),     0, 0, TVI_ROOT, TVI_SORT); HTREEITEM hDoobies = GetTreeCtrl ().InsertItem (_T ("Doobie Brothers"),     0, 0, TVI_ROOT, TVI_SORT); // Eagles subitems second (no sorting) GetTreeCtrl ().InsertItem (_T ("Eagles"), 1, 1, hEagles); GetTreeCtrl ().InsertItem (_T ("On the Border"), 1, 1, hEagles); GetTreeCtrl ().InsertItem (_T ("Hotel California"), 1, 1, hEagles); GetTreeCtrl ().InsertItem (_T ("The Long Run"), 1, 1, hEagles); // Doobie subitems third (no sorting) GetTreeCtrl ().InsertItem (_T ("Toulouse Street"), 1, 1, hDoobies); GetTreeCtrl ().InsertItem (_T ("The Captain and Me"), 1, 1, hDoobies); GetTreeCtrl ().InsertItem (_T ("Stampede"), 1, 1, hDoobies);

The second and third parameters passed to this form of InsertItem are image indexes. The first specifies the image the tree view will display when the item isn't selected, and the second specifies the image it will display when the item is selected. Specifying the same index for both means that the same image will be used to represent the item in both states. The tree view control in the left pane of Windows Explorer uses an image depicting a closed folder for nonselected folder items and an open folder for selected folder items. Thus, if you move the highlight up and down with the arrow keys, a folder "opens" when you highlight it and closes when you highlight another item.

Tree View Member Functions and Notifications

CTreeCtrl provides a wide range of member functions for manipulating the underlying tree view control and acquiring information about its items. DeleteItem, for example, removes an item from the control, and DeleteAllItems removes all the items. Expand expands or collapses a subtree. SetItemText changes an item's label; GetItemText retrieves it. SortChildren sorts the items in a subtree. You name it, and there's probably a CTreeCtrl function for doing it.

The key to nearly every one of these functions is an HTREEITEM handle identifying the item that's the target of the operation. If you'd like, you can save the handles returned by InsertItem in an array or a linked list or some other structure so that you can reference them again later. You can retrieve the handle of the selected item with CTreeCtrl::GetSelectedItem. And if necessary, you can start with the first item in a tree view control and enumerate items one by one using GetParentItem, GetChildItem, GetNextItem, GetNextSiblingItem, and other CTreeCtrl functions.

Once items are added to it, a tree view is capable of processing most user input on its own. The user can browse the items in the tree by expanding and collapsing branches and can make selections by pointing and clicking. You can add even more capabilities to a tree view (or customize its default response to conventional input) by processing the notifications shown in the following table. Notifications come in the form of WM_NOTIFY messages, and in most cases, lParam points to an NM_TREEVIEW structure containing additional information about the event that prompted the message. Here are just a few uses for tree view notifications:

  • Enable in-place label editing so that the user can edit text in a tree view
  • Update item text and images dynamically by passing LPSTR_TEXTCALL-BACK and I_IMAGECALLBACK parameters to InsertItem and processing TVN_GETDISPINFO notifications
  • Customize the control's response to keyboard input by processing TVN-_KEYDOWN notifications
  • Support drag-and-drop operations

There are more uses (of course!), but this short list should give you an idea of the wide-ranging flexibility of a tree view control.

Tree View Notifications

Notification Sent When
TVN_BEGINDRAG A drag-and-drop operation is begun with the left mouse button. Not sent if the control has the style TVS_DISABLEDRAGDROP.
TVN_BEGINRDRAG A drag-and-drop operation is begun with the right mouse button. Not sent if the control has the style TVS_DISABLEDRAGDROP.
TVN_BEGINLABELEDIT A label editing operation is begun. Sent only if the control has the style TVS_EDITLABELS.
TVN_ENDLABELEDIT A label editing operation is completed. Sent only if the control has the style TVS_EDITLABELS.
TVN_GETDISPINFO The control needs additional information to display an item. Sent if the item text is LPSTR_TEXTCALLBACK or the image index is I_IMAGECALLBACK.
TVN_DELETEITEM An item is deleted.
TVN_ITEMEXPANDED A subtree has expanded or collapsed.
TVN_ITEMEXPANDING A subtree is about to expand or collapse.
TVN_KEYDOWN A key is pressed while the control has the input focus.
TVN_SELCHANGED The selection has changed.
TVN_SELCHANGING The selection is about to change.
TVN_SETDISPINFO The information in a TV_DISPINFO structure needs to be updated.

The DriveTree Application

The DriveTree application shown in Figure 10-6 uses a CTreeView-derived class named CDriveView to provide an interactive view of the host PC's drive and directory structure. CDriveView::OnInitialUpdate uses SetImageList to import an image list containing bitmaps for different drive types and then calls a helper function named AddDrives to initialize the drive list. AddDrives uses the Win32 ::GetLogical- Drives function to identify the logical drives in the system. For each drive, it calls CDriveView::AddDriveItem to add a "drive item"—a tree view item representing a drive—to the tree's uppermost level. ::GetLogicalDrives returns a DWORD value with "on" bits identifying the valid logical drives, where bit 0 corresponds to drive A:, bit 1 to drive B:, and so on. AddDrives needs just a few lines of code to enumerate the drives in the system and create a drive item for each. (See Figure 10-7.)

click to view at full size.

Figure 10-6. The DriveTree window.

AddDriveItem uses CTreeCtrl::InsertItem to add drive items to the tree. For each drive item that it adds, it also adds a "dummy" subitem so that a plus sign will appear next to the drive item. To determine a drive's type—floppy drive, hard disk, and so on—so that it can assign the drive an image from the image list, AddDriveItem uses the ::GetDriveType API function. Given a string specifying the path to a drive's root directory, ::GetDriveType returns a UINT value identifying the drive type. The possible return values are listed below.

Return Value Meaning
DRIVE_UNKNOWN The drive type is unknown.
DRIVE_NO_ROOT_DIR The drive lacks a root directory.
DRIVE_REMOVABLE The drive is removable (returned for floppy drives and other removable-media drives such as Zip drives).
DRIVE_FIXED The drive is fixed (returned for hard disks).
DRIVE_REMOTE The drive is remote (returned for network drives).
DRIVE_CDROM The drive is a CD-ROM drive.
DRIVE_RAMDISK The drive is a RAM disk.

AddDriveItem uses a switch-case block to handle each of the possible return values. A series of ILI values defined near the top of DriveView.cpp correlates drive types and image indexes.

Much of the remaining code in DriveView.cpp is devoted to processing TVN_ITEMEXPANDING notifications. For performance reasons, CDriveView doesn't initialize itself with items representing every directory on every drive. Instead, it adds directory items to a subtree just before the subtree is displayed and removes them when the subtree is collapsed. If a collapsed subtree contains at least one directory, a single child item is inserted so that a plus sign will appear next to the subtree. That child item is never seen because it's deleted before the subtree is expanded and replaced with items representing actual directories. An ON_NOTIFY_REFLECT entry in the message map reflects TVN_ITEMEXPANDING notifications so that CDriveView can handle them itself. The notification handler OnItemExpanding either adds items to the subtree or removes them, depending on whether the action field of the NM_TREEVIEW structure indicates that the subtree is about to expand or collapse. OnItemExpanding uses the helper function AddDirectories to populate a branch of the tree with items. AddDirectories, in turn, uses the ::FindFirstFile and ::FindNextFile functions discussed in Chapter 6 to enumerate directories.

Removing the Document Name from the Title Bar

DriveTree doesn't use its document object at all. Its File menu doesn't include document-handling commands. And it doesn't display a document name in its title bar because it doesn't make sense to display a document name when the application doesn't support the loading and saving of documents. But because MFC automatically adds the document name to the title bar, you must take special steps to prevent MFC from inserting a document name.

You'll find the code responsible for removing the document name in the frame window class. CMainFrame::PreCreateWindow contains the statement

 cs.style &= ~FWS_ADDTOTITLE;

FWS_ADDTOTITLE is a special window style specific to MFC that's included in frame windows by default. Windows that have this style have document names added to their window titles; windows that lack this style don't. By stripping the FWS_ADDTOTITLE bit from the window style in PreCreateWindow, CMainFrame prevents the framework from modifying its window title. You can use this technique to remove the document name from the title bar of any document/view application.

MainFrm.h

 // MainFrm.h : interface of the CMainFrame class // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_MAINFRM_H__090B3829_959D_11D2_8E53_006008A82731__INCLUDED_) #define AFX_MAINFRM_H__090B3829_959D_11D2_8E53_006008A82731__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CMainFrame : public CFrameWnd {      protected: // create from serialization only     CMainFrame();     DECLARE_DYNCREATE(CMainFrame) // Attributes public: // Operations public: // Overrides     // ClassWizard generated virtual function overrides     //{{AFX_VIRTUAL(CMainFrame)     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);     //}}AFX_VIRTUAL // Implementation public:     virtual ~CMainFrame(); #ifdef _DEBUG     virtual void AssertValid() const;     virtual void Dump(CDumpContext& dc) const; #endif // Generated message map functions protected:     //{{AFX_MSG(CMainFrame)        // NOTE - the ClassWizard will add and remove member functions here.        //    DO NOT EDIT what you see in these blocks of generated code!     //}}AFX_MSG     DECLARE_MESSAGE_MAP() }; ////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately // before the previous line. #endif  // !defined( //     AFX_MAINFRM_H__090B3829_959D_11D2_8E53_006008A82731__INCLUDED_) 

MainFrm.cpp

 // MainFrm.cpp : implementation of the CMainFrame class // #include "stdafx.h" #include "DriveTree.h" #include "MainFrm.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ////////////////////////////////////////////////////////////////////// // CMainFrame IMPLEMENT_DYNCREATE(CMainFrame, CFrameWnd) BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)     //{{AFX_MSG_MAP(CMainFrame)         // NOTE - the ClassWizard will add and remove mapping macros here.         //    DO NOT EDIT what you see in these blocks of generated code !     //}}AFX_MSG_MAP END_MESSAGE_MAP() ////////////////////////////////////////////////////////////////////// // CMainFrame construction/destruction CMainFrame::CMainFrame() { } CMainFrame::~CMainFrame() { } BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) {     if( !CFrameWnd::PreCreateWindow(cs) )         return FALSE;     cs.style &= ~FWS_ADDTOTITLE;     return TRUE; } ////////////////////////////////////////////////////////////////////// // CMainFrame diagnostics #ifdef _DEBUG void CMainFrame::AssertValid() const {     CFrameWnd::AssertValid(); } void CMainFrame::Dump(CDumpContext& dc) const {     CFrameWnd::Dump(dc); } #endif //_DEBUG ////////////////////////////////////////////////////////////////////// // CMainFrame message handlers 

DriveView.h

 // DriveTreeView.h : interface of the CDriveView class // ////////////////////////////////////////////////////////////////////// #if !defined(AFX_DRIVETREEVIEW_H__090B382D_959D_11D2_8E53_006008A82731__INCLUDED_) #define AFX_DRIVETREEVIEW_H__090B382D_959D_11D2_8E53_006008A82731__INCLUDED_ #if _MSC_VER > 1000 #pragma once #endif // _MSC_VER > 1000 class CDriveView : public CTreeView { protected: // create from serialization only     CDriveView();     DECLARE_DYNCREATE(CDriveView) // Attributes public:     CDriveTreeDoc* GetDocument(); // Operations public: // Overrides     // ClassWizard generated virtual function overrides     //{{AFX_VIRTUAL(CDriveView)     public:     virtual void OnDraw(CDC* pDC);  // overridden to draw this view     virtual BOOL PreCreateWindow(CREATESTRUCT& cs);     protected:     virtual void OnInitialUpdate(); // called first time after construct     //}}AFX_VIRTUAL // Implementation public:     virtual ~CDriveView(); #ifdef _DEBUG     virtual void AssertValid() const;     virtual void Dump(CDumpContext& dc) const; #endif protected: // Generated message map functions protected:     BOOL AddDriveItem (LPCTSTR pszDrive);     int AddDirectories (HTREEITEM hItem, LPCTSTR pszPath);     void DeleteAllChildren (HTREEITEM hItem);     void DeleteFirstChild (HTREEITEM hItem);     CString GetPathFromItem (HTREEITEM hItem);     BOOL SetButtonState (HTREEITEM hItem, LPCTSTR pszPath);     int AddDrives ();     CImageList m_ilDrives;     //{{AFX_MSG(CDriveView)     afx_msg void OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult);     //}}AFX_MSG     DECLARE_MESSAGE_MAP() }; #ifndef _DEBUG  // debug version in DriveTreeView.cpp inline CDriveTreeDoc* CDriveView::GetDocument()    { return (CDriveTreeDoc*)m_pDocument; } #endif /////////////////////////////////////////////////////////////////////////// //{{AFX_INSERT_LOCATION}} // Microsoft Visual C++ will insert additional declarations immediately // before the previous line. #endif  // !defined( //     AFX_DRIVETREEVIEW_H__090B382D_959D_11D2_8E53_006008A82731__INCLUDED_) 

DriveView.cpp

 // DriveTreeView.cpp : implementation of the CDriveView class // #include "stdafx.h" #include "DriveTree.h" #include "DriveTreeDoc.h" #include "DriveView.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif // Image indexes #define ILI_HARD_DISK       0 #define ILI_FLOPPY          1 #define ILI_CD_ROM          2 #define ILI_NET_DRIVE       3 #define ILI_CLOSED_FOLDER   4 #define ILI_OPEN_FOLDER     5 ////////////////////////////////////////////////////////////////////// // CDriveView IMPLEMENT_DYNCREATE(CDriveView, CTreeView) BEGIN_MESSAGE_MAP(CDriveView, CTreeView)     //{{AFX_MSG_MAP(CDriveView)     ON_NOTIFY_REFLECT(TVN_ITEMEXPANDING, OnItemExpanding)     //}}AFX_MSG_MAP END_MESSAGE_MAP() ////////////////////////////////////////////////////////////////////// // CDriveView construction/destruction CDriveView::CDriveView() { } CDriveView::~CDriveView() { } BOOL CDriveView::PreCreateWindow(CREATESTRUCT& cs) {     if (!CTreeView::PreCreateWindow (cs))         return FALSE;     cs.style ¦= TVS_HASLINES ¦ TVS_LINESATROOT ¦ TVS_HASBUTTONS ¦         TVS_SHOWSELALWAYS;     return TRUE; } ////////////////////////////////////////////////////////////////////// // CDriveView drawing void CDriveView::OnDraw(CDC* pDC) {     CDriveTreeDoc* pDoc = GetDocument();     ASSERT_VALID(pDoc);     // TODO: add draw code for native data here } void CDriveView::OnInitialUpdate() {     CTreeView::OnInitialUpdate();     //     // Initialize the image list.     //     m_ilDrives.Create (IDB_DRIVEIMAGES, 16, 1, RGB (255, 0, 255));     GetTreeCtrl ().SetImageList (&m_ilDrives, TVSIL_NORMAL);     //     // Populate the tree view with drive items.     //     AddDrives ();     //     // Show the folders on the current drive.     //     TCHAR szPath[MAX_PATH];     ::GetCurrentDirectory (sizeof (szPath) / sizeof (TCHAR), szPath);     CString strPath = szPath;     strPath = strPath.Left (3);     HTREEITEM hItem = GetTreeCtrl ().GetNextItem (NULL, TVGN_ROOT);     while (hItem != NULL) {         if (GetTreeCtrl ().GetItemText (hItem) == strPath)             break;         hItem = GetTreeCtrl ().GetNextSiblingItem (hItem);     }     if (hItem != NULL) {         GetTreeCtrl ().Expand (hItem, TVE_EXPAND);         GetTreeCtrl ().Select (hItem, TVGN_CARET);     } } ////////////////////////////////////////////////////////////////////// // CDriveView diagnostics #ifdef _DEBUG void CDriveView::AssertValid() const {     CTreeView::AssertValid(); } void CDriveView::Dump(CDumpContext& dc) const {     CTreeView::Dump(dc); } CDriveTreeDoc* CDriveView::GetDocument() // non-debug version is inline {     ASSERT(m_pDocument->IsKindOf(RUNTIME_CLASS(CDriveTreeDoc)));     return (CDriveTreeDoc*)m_pDocument; } #endif //_DEBUG ////////////////////////////////////////////////////////////////////// // CDriveView message handlers int CDriveView::AddDrives() {     int nPos = 0;     int nDrivesAdded = 0;     CString string = _T ("?:\\");     DWORD dwDriveList = ::GetLogicalDrives ();     while (dwDriveList) {         if (dwDriveList & 1) {             string.SetAt (0, _T (`A') + nPos);             if (AddDriveItem (string))                 nDrivesAdded++;         }         dwDriveList >>= 1;         nPos++;     }     return nDrivesAdded; } BOOL CDriveView::AddDriveItem(LPCTSTR pszDrive) {     CString string;     HTREEITEM hItem;     UINT nType = ::GetDriveType (pszDrive);     switch (nType) {     case DRIVE_REMOVABLE:         hItem = GetTreeCtrl ().InsertItem (pszDrive, ILI_FLOPPY,             ILI_FLOPPY);         GetTreeCtrl ().InsertItem (_T (""), ILI_CLOSED_FOLDER,             ILI_CLOSED_FOLDER, hItem);         break;     case DRIVE_FIXED:     case DRIVE_RAMDISK:         hItem = GetTreeCtrl ().InsertItem (pszDrive, ILI_HARD_DISK,             ILI_HARD_DISK);         SetButtonState (hItem, pszDrive);         break;     case DRIVE_REMOTE:         hItem = GetTreeCtrl ().InsertItem (pszDrive, ILI_NET_DRIVE,             ILI_NET_DRIVE);         SetButtonState (hItem, pszDrive);         break;     case DRIVE_CDROM:         hItem = GetTreeCtrl ().InsertItem (pszDrive, ILI_CD_ROM,             ILI_CD_ROM);         GetTreeCtrl ().InsertItem (_T (""), ILI_CLOSED_FOLDER,             ILI_CLOSED_FOLDER, hItem);         break;     default:         return FALSE;     }     return TRUE; } BOOL CDriveView::SetButtonState(HTREEITEM hItem, LPCTSTR pszPath) {     HANDLE hFind;     WIN32_FIND_DATA fd;     BOOL bResult = FALSE;     CString strPath = pszPath;     if (strPath.Right (1) != _T ("\\"))         strPath += _T ("\\");     strPath += _T ("*.*");     if ((hFind = ::FindFirstFile (strPath, &fd)) == INVALID_HANDLE_VALUE)         return bResult; do {         if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {             CString strComp = (LPCTSTR) &fd.cFileName;             if ((strComp != _T (".")) && (strComp != _T (".."))) {                 GetTreeCtrl ().InsertItem (_T (""), ILI_CLOSED_FOLDER,                     ILI_CLOSED_FOLDER, hItem);                 bResult = TRUE;                 break;             }         }     } while (::FindNextFile (hFind, &fd));     ::FindClose (hFind);     return bResult; } void CDriveView::OnItemExpanding(NMHDR* pNMHDR, LRESULT* pResult)  {     NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;     HTREEITEM hItem = pNMTreeView->itemNew.hItem;     CString string = GetPathFromItem (hItem);     *pResult = FALSE;     if (pNMTreeView->action == TVE_EXPAND) {         DeleteFirstChild (hItem);         if (AddDirectories (hItem, string) == 0)             *pResult = TRUE;     }     else { // pNMTreeView->action == TVE_COLLAPSE         DeleteAllChildren (hItem);         if (GetTreeCtrl ().GetParentItem (hItem) == NULL)             GetTreeCtrl ().InsertItem (_T (""), ILI_CLOSED_FOLDER,                 ILI_CLOSED_FOLDER, hItem);         else             SetButtonState (hItem, string);     } } CString CDriveView::GetPathFromItem(HTREEITEM hItem) {     CString strResult = GetTreeCtrl ().GetItemText (hItem);     HTREEITEM hParent;     while ((hParent = GetTreeCtrl ().GetParentItem (hItem)) != NULL) {         CString string = GetTreeCtrl ().GetItemText (hParent);         if (string.Right (1) != _T ("\\"))             string += _T ("\\");         strResult = string + strResult;         hItem = hParent;     }     return strResult; } void CDriveView::DeleteFirstChild(HTREEITEM hItem) {     HTREEITEM hChildItem;     if ((hChildItem = GetTreeCtrl ().GetChildItem (hItem)) != NULL)         GetTreeCtrl ().DeleteItem (hChildItem); } void CDriveView::DeleteAllChildren(HTREEITEM hItem) {     HTREEITEM hChildItem;     if ((hChildItem = GetTreeCtrl ().GetChildItem (hItem)) == NULL)         return;     do {         HTREEITEM hNextItem =             GetTreeCtrl ().GetNextSiblingItem (hChildItem);         GetTreeCtrl ().DeleteItem (hChildItem);         hChildItem = hNextItem;     } while (hChildItem != NULL); } int CDriveView::AddDirectories(HTREEITEM hItem, LPCTSTR pszPath) {     HANDLE hFind;     WIN32_FIND_DATA fd;     HTREEITEM hNewItem;     int nCount = 0;     CString strPath = pszPath;     if (strPath.Right (1) != _T ("\\"))         strPath += _T ("\\");     strPath += _T ("*.*");     if ((hFind = ::FindFirstFile (strPath, &fd)) == INVALID_HANDLE_VALUE) {         if (GetTreeCtrl ().GetParentItem (hItem) == NULL)             GetTreeCtrl ().InsertItem (_T (""), ILI_CLOSED_FOLDER,                 ILI_CLOSED_FOLDER, hItem);         return 0;     }    do {         if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {             CString strComp = (LPCTSTR) &fd.cFileName;             if ((strComp != _T (".")) && (strComp != _T (".."))) {                 hNewItem =                     GetTreeCtrl ().InsertItem ((LPCTSTR) &fd.cFileName,                     ILI_CLOSED_FOLDER, ILI_OPEN_FOLDER, hItem);                 CString strNewPath = pszPath;                 if (strNewPath.Right (1) != _T ("\\"))                     strNewPath += _T ("\\");                 strNewPath += (LPCTSTR) &fd.cFileName;                 SetButtonState (hNewItem, strNewPath);                 nCount++;             }         }     } while (::FindNextFile (hFind, &fd));     ::FindClose (hFind);     return nCount; } 



Programming Windows with MFC
Programming Windows with MFC, Second Edition
ISBN: 1572316950
EAN: 2147483647
Year: 1999
Pages: 101
Authors: Jeff Prosise

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net