CTreeListCtrl: The easiest TreeView with columns

Sample Image

Environment: VC6, NT4 SP3, Comctl32.dll 4.72.3110.1

Motivation

In http://www.codeguru.com, you can find some implementations of tree controls with columns. All of this implementation have the same approach: they reimplement the drawing code for a tree view control and, of course, they have to manage the column info for each node in the tree. What's wrong with this approach ? Even if I can find it implemented in codeguru, I don't like the idea of having to reimplement the drawing stuff only because the original tree view control does not support columns, and also Microsoft changes the GUI quickly, so you will find yourself re-writing or adding new drawing characteristics to this code.

Due to my need of a tree control with columns, and without any implementation which I like it, I decided to developed my own version, and my approach was really different. Knowing some of the new characteristics of the common controls that Microsoft ships with every new IE version, I thought, Could be possible to use a listview control in report mode to simulate a tree control? The answer was: yes, you can use the "indent" property for each list view item and the state image list to simulate a tree control with columns support.

Now that you known my approach, you can download the source code and see how simple it is. This is not a tree with lots of functionality and amazing characteristics, it is only the backbone of a tree with columns implementation. Even with the reduced set of functionality, this approach lets you incorporate any technique regarding the listview control.

How to insert CTreeListCtrl into your project?

To incorporate the tree control in your project you only need to simple steps:

  1. Insert CTreeListCtrl.h and CTreeListCtrl.cpp into you project.
  2. Import the State.bmp as a resource. Give it the IDB_STATE identifier.

Now you are ready to use it.

Using the CTreeListCtrl

The current implementation offers the minimum functionality to be a "read-only" tree and the tree expects you to add the items as they will result from a "preorder" iteration to the whole tree. This means that if you have all the nodes of the tree expanded, the order in which they appear is the order in which you have to insert them. Let's see an example.

Let's say you have a Hard disk partition with two folders: Program Files, and Winnt. Under Program Files, you have tree folders: Microsoft Office, Microsoft Visual Studio and InstallShield. Under Winnt you have two folders: profiles and system32, and under the profiles folder you have the folders All Users, Administrator and Davidc. You have to insert the items in the tree as follows:

  1. C:\
  2. Program Files
  3. Microsoft Office
  4. Microsoft Visual Studio
  5. InstallShield
  6. Winnt
  7. profiles
  8. All Users
  9. Administrator
  10. system32

Now that let's see the main steps to use the tree:

  1. Declare a member variable (e.g. m_tree) in the parent window class.
  2. In the WM_CREATE or WM_INITDIALOG message function handler, create the tree window. It can also be used through DDX_Control.
  3. Insert as many columns as you need as you will do with a listview control.
  4. Set the image list for the control.
  5. When you have to add items to the tree, for each item you must use the function AddItem to add it (with the information of the column 0) setting its level, and then call SetItemText for each other column.

Here is the code used to create the tree you can see in the sample image.



// Step one: Window creation.

m_tree.Create
  (
  WS_BORDER | WS_CHILD | WS_VISIBLE | LVS_REPORT | 
   LVS_SINGLESEL | LVS_SHOWSELALWAYS, 
  CRect(12, 12, 288, 228), 
  this, 
  0x100
  );


// Step two: We insert two columns: Folder name, and folder size

LVCOLUMN   column;

column.mask  = LVCF_FMT | LVCF_IMAGE | LVCF_TEXT | LVCF_WIDTH;

column.fmt      = LVCFMT_LEFT;
column.cx       = 200;
column.pszText  = _T("Folder");
column.iSubItem = 0;
m_tree.InsertColumn(0, &column);

column.fmt      = LVCFMT_RIGHT;
column.cx       = 75;
column.pszText  = _T("Size");
column.iSubItem = 1;
m_tree.InsertColumn(1, &column);


// Step three: We create and set the image list

m_il.Create(IDB_FOLDERS, 16, 1, RGB(255, 0, 255));
m_tree.SetImageList(&m_il, LVSIL_SMALL);


// Step four:  we insert the tree contents.
//             The information in the arrays asFolders,
//             asSizes and anLevels should be obtained
//             as a result of a "process": selecting a
//             group of records, checking the hard disk,
//             etc.

CString  asFolders[] = 
      { 
      "C:", "Program Files", "Microsoft Office",
      "Microsoft Visual Studio", "InstallShield",
      "Winnt", "profiles", "All Users", "Administrator" ,
      "system32"
      };

CString  asSizes[]= 
      { "100", "60", "10", "20", "30", "40", "5",
        "2", "1" , "35"
      };
int      anLevels[] = { 0, 1, 2, 2, 2, 1, 2, 3, 3, 2 };

for (int i = 0; i < sizeof(asFolders) / 
                   sizeof(asFolders[0]); i++)
{
  int iItem;
  iItem = m_tree.AddItem(asFolders[i], i % 3, anLevels[i]);
  m_tree.SetItemText(iItem, 1, asSizes[i]);
}

Future work

There are some characteristics that can or need to be added to the tree. At least:

  • Reimplement DeleteItem to delete the node and its sons.
  • Support for inserting items at any position.
  • Sample application

    The application for which I needed this tree is a program that inspects a drive partition and shows you the size of each folder. With this information you can see which folder is eating your hard disk space and delete it -if you can (e.g. do not delete Winnt).

    Here you can see a screen snapshot:


    Sample Image
    Click here for larger image

    Downloads

    CheckDirectorySizes application - 116 KB
    Download demo project - 13.4 Kb
    Download source - 3.44 Kb