Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / MFC

CTreeGridCtrl - A Grid Control with Tree Like Functionalities

4.91/5 (20 votes)
10 Nov 2010CPOL3 min read 175.5K   7K  
Chris Maunder's grid control enhanced to have tree like features
screenshot.JPG

Introduction

This article mainly shows the usage of a tree grid control which is inherited from Chris Maunder's famous CGridCtrl. Ok folks, this is hopefully my last article related to the grid controls.

Background

In one my projects, I was required to come up with a Super Grid Control. I have used the ideas used there to develop a Grid Control inherited class which has similar features to a tree control.

There is another article on CodeProject related to this http://www.codeproject.com/KB/miscctrl/gridtreectrl.aspx, but I couldn't get into terms with the usage of the Tree concept used there. I needed something more similar with regards to inserting and deleting items in a tree.

Main Concept

The main concept of giving the tree feature to the grid is playing with its cells. What I did was develop a class which contains the tree related data structures such as tree item (similar to TVITEM), and tree item's children and its states, etc. inside a class CTreeNode. There is also another class with some APIs calling of which will give the space needed to draw a tree line and tree buttons, etc. and from that information draw the necessary parts of a tree (the lines and buttons) in the extra space allocated beside the normal drawing of cell.

I named the class CTreeCell and it looks something like this:

C++
class CTreeCell
{
 friend class CTreeGridCtrl; 
public:

 // Construction
 CTreeCell(CGridCell* cell); 
 
 // Determine the location of the specified point relative to the 
 //client area of a tree list control
 //
 //  point    : Specifies the client coordinates of the point to test
 //  rectCell    : Specifies the rectangle of the cell
 //  bClickedOnBtnImgArea : Output parameter for determining 
 // the point relative to the client area is in the button image area
 //
 // Returns TRUE if successful, otherwise FALSE
 virtual BOOL TreeBtnHitTest(CPoint point, CRect rectCell, BOOL& bClickedOnBtnImgArea);

 // Computes the width and height of a cell on the attribute device context
 //
 //  pDC   : Specifies the device context
 //  pSize  : Output parameter to receive the cell extent
 //  pImgExtent : Output parament to receive the image extent
 //  pTextExtent : Output parament to receive the text extent
 //
 // Return the dimensions of the cell (in logical units) in a CSize object
 virtual void OnGetTreeCellExtent(CDC* pDC, CSize& size);

 // Draw the item
 //
 //  pDC   : Specifies the device context
 //  nItem  : Specifies the row index of the cell
 //  nSubItem  : Specifies the column index of the cell
 //  rect   : Specifies the rectangle which might be modified inside the method
 //  bEraseBkgnd : Specifies whether the cell erase the background when repaint
 //
 // Returns TRUE if successful, otherwise FALSE
 virtual BOOL OnDrawingTreeCell(CDC* pDC, int nItem, int nSubItem, 
	CRect& rect,  BOOL bEraseBkgnd = TRUE);

 // Enable the edit mode of the cell
 //
 //  nItem : Specifies the row index of the cell
 //  nSubItem : Specifies the column index of the cell
 //  rect  : Specifies the rectangle which might be modified inside the method
 //  point : Specifies the point
 //  nID  : Specifies the in-place edit control ID
 //  nChar : Specifies the character
 //
 // Returns TRUE if successful, otherwise FALSE
 virtual BOOL OnEditingTreeCell(int nItem, int nSubItem, 
	CRect& rect, CPoint  point, UINT nID, UINT nChar);

 // Measure the tree button rectangle
 //
 //  rect : Specifies the rectangle
 //
 // Returns TRUE if successful, otherwise FALSE
 virtual BOOL MeasureTreeButtonRect(CRect& rect);
 
 // Retrieve the tree buttons horizontal margin
 //
 // Returns the tree buttons horizontal margin
 virtual int GetTreeButtonMarginHorz();


 // Retrieve the tree buttons vertical margin
 //
 // Returns the tree buttons vertical margin
 virtual int GetTreeButtonMarginVert();

 // Retrieve the tree buttons right padding
 //
 // Returns the tree buttons right padding
 virtual int GetTreeBtnRightPadding();

 // Draw the tree item buttons
 //
 //  pDC   : Specifies the device context
 //  rcButtonArea : Specifies the rectangle of button area
 //
 virtual void DrawTreeItemButton(CDC* pDC, CRect rcButtonArea);

 // This is calculated using the horizontal and vertical Tree Button Margin
 //
 //  rect : Output parameter for retrieving the rectangle
 //
 // Returns TRUE if successful, otherwise FALSE
 virtual BOOL MeasureTreeButtonImageRect(CRect& rect);

 // Retrieve the tree item
 //
 // Return the tree item
 GTV_HTREENODE GetTreeItem();
 
 // Gets the item index
 int GetItemID();

 // Retrieve the tree item level (level 0 is the top parents, 
 // level 1 is the first level child, 
 // level 2 is the second level child and so on..)
 //
 // Return the tree level
 int GetTreeItemLevel();
 
 // Enable/Disable the tree node expanded mode
 //
 //  bExpanded : Specifies the tree node expanded mode
 //
 void SetExpanded(BOOL bExpanded = TRUE);
 
 // Determine whether the tree node is expanded or not
 //
 // Return TRUE if tree node is expanded, otherwise FALSE
 BOOL IsExpanded();

 // Determine whether the tree node button shown or not
 //
 // Return TRUE if tree node button is shown, otherwise FALSE
 BOOL HasButton();

 // Retrieve the Cell state
 //
 // Return the cell state
 //
 DWORD GetState() const;

 // Determine the Cell is enabled
 //
 // Returns TRUE if the cell is enabled, otherwise FALSE
 BOOL IsEnabled() const;

 // Retrieve the margin of the Cell
 //
 // Return the margin of the cell
 UINT GetMargin() const;

protected:
 // Measure the tree button rectangle
 //
 //  rect    : Output parameter for retrieving the buttons rectangle
 //  nTreeItemLevel : Specifies the tree item level
 //
 // Returns TRUE if successful, otherwise FALSE
 virtual BOOL OnMeasureTreeButtonRect(CRect& rect, int nTreeItemLevel);

 // Copy the value of member variables from a given tree cell
 //
 //  cell : Specifies the cell
 //
 // Return TRUE if successful, otherwise FALSE
 virtual BOOL CopyFromTreeCell(const CTreeCell& cell);

 // Hold the point of where vertical line to child should start
 CPoint m_ptVertLineOrigin;

 // Hold the tree item
 GTV_HTREENODE m_hTreeItem;

 // Hold the tree item level
 int m_nTreeItemLevel; 

 // Hold the tree item row index
 int m_nItem;

 // Hold the corresponding GridCtrl Cell which will gain the tree cell attributes
 // To be used sparingly (only to get cell states)
 CGridCell* m_pGridCell;
};

I hope the comments in the APIs are clear enough to understand what are the purposes of the APIs in the class.

So the main idea is, a CGridCellBase derived class will also derive this class to have the tree features incorporated in the cell.

For example:

C++
//  CTreeGridCell class definition
class CTreeGridCellCheck : public CGridCellCheck, public CTreeCell
{ 
DECLARE_DYNCREATE(CTreeGridCellCheck)

public:

 // Construction
 CTreeGridCellCheck(); 
 
 // Computes the width and height of a cell on the attribute device context
 //
 //  pDC   : Specifies the device context
 //
 // Return the dimensions of the cell (in logical units) in a CSize object
 virtual CSize GetCellExtent(CDC* pDC);

 // Draw the item
 //
 //  pDC   : Specifies the device context
 //  nItem  : Specifies the row index of the cell
 //  nSubItem  : Specifies the column index of the cell
 //  rect   : Specifies the rectangle
 //  bEraseBkgnd : Specifies whether the cell erase the background when repaint
 //
 // Returns TRUE if successful, otherwise FALSE
 virtual BOOL Draw(CDC* pDC, int nItem, int nSubItem, 
	CRect rect,  BOOL bEraseBkgnd = TRUE); 

 // Enable the edit mode of the cell
 //
 //  nItem : Specifies the row index of the cell
 //  nSubItem : Specifies the column index of the cell
 //  rect  : Specifies the rectangle
 //  point : Specifies the point
 //  nID  : Specifies the in-place edit control ID
 //  nChar : Specifies the character
 //
 // Returns TRUE if successful, otherwise FALSE
 virtual BOOL Edit(int nItem, int nSubItem, CRect rect, 
	CPoint  point, UINT nID, UINT nChar);

 virtual void OnClick(CRect cellRect, CPoint PointCellRelative);

 virtual void  SetCoords(int nRow, int /*nCol*/) 
 {
  m_nItem = nRow;
 }

protected:

// Invoked after coping the value of member variables from a given cell
 //
 //  cell : Specifies the cell
 // 
 virtual void OnCopy(const CGridCellBase& cell);
};

And you'd only override the required virtual functions of the CGridCellBase as follows:

Derive a CTreeGridCell Class from CGridCell Class

For simplicity, instead of a check cell, let me give an example of a regular cell:

C++
CTreeGridCell::CTreeGridCell() : CTreeCell(this)
{
 
}
C++
CSize CTreeGridCell::GetCellExtent(CDC* pDC)
{
 CSize size = CGridCell::GetCellExtent(pDC);
 OnGetTreeCellExtent(pDC, size);
 return size;
}
C++
BOOL CTreeGridCell::Draw(CDC* pDC, int nItem, int nSubItem, 
	CRect rect,  BOOL bEraseBkgnd /*=TRUE*/)
{
 BOOL bRet = FALSE;
 if(OnDrawingTreeCell(pDC, nItem, nSubItem, rect, bEraseBkgnd))
  bRet = CGridCell::Draw(pDC, nItem, nSubItem, rect, bEraseBkgnd);
 
 return bRet;
}
C++
BOOL CTreeGridCell::Edit(int nItem, int nSubItem, 
	CRect rect, CPoint  point, UINT nID, UINT nChar)
{
 BOOL bRet = FALSE;
 if(OnEditingTreeCell(nItem, nSubItem, rect, point, nID, nChar))
  bRet = CGridCell::Edit(nItem, nSubItem, rect, point, nID, nChar);
 return bRet;
}
C++
void CTreeGridCell::OnCopy(const CGridCellBase& cell)
{
 const CTreeCell* pCell = dynamic_cast<const CTreeCell*>(&cell);
 if(pCell)
 {
  this->CopyFromTreeCell(*pCell);
 } 
}

I had to make some slight changes in the original CGridCtrl and its cells such as calling of OnCopy while overriding the = operator of a cell and in OnDraw and some relevant methods which will be evident if the source code is browsed and made a windiff between my changed CGridCtrl and the original code.

The most important control here is CGridCtrl inherited class CTreeGridCtrl. Those who are over interested can look up the implementation of the APIs in this class. But let me come to the point which is most important and that is using the code.

The CTreeGridCtrl Useful APIs at a Glance

C++
// Insert a Tree Item
//
//  strHeading : Specifies the default text of the inserted item
//  hParent  : Specifies the parent node of the row
//  hInsertAfter : Specifies the sibling node after which the row will be inserted
//  bRefresh  : Specifies whether the list will be refreshed immediately
//
// Returns the handle to the inserted node
GTV_HTREENODE InsertItem(LPCTSTR strHeading, GTV_HTREENODE hParent = GLVI_ROOT,
    GTV_HTREENODE hInsertAfter = GLVI_LAST , BOOL bRefresh = TRUE);
C++
// Insert a Tree Item
//
//  pItem  : Specifies the pointer to a GV_ITEM structure that
//  defines the attributes of an item
//  hParent  : Specifies the parent node of the row
//  hInsertAfter : Specifies the sibling node after which the row will be inserted
//  bRefresh  : Specifies whether the list will be refreshed immediately
//
// Returns the handle to the inserted node
GTV_HTREENODE InsertItem( GV_ITEM* pItem, GTV_HTREENODE hParent = GLVI_ROOT,
    GTV_HTREENODE hInsertAfter = GLVI_LAST, BOOL bRefresh = TRUE);
C++
// Insert a row
//
//  strHeading : Specifies the default text of the inserted item
//  nItem  : Specifies the position of the row (-1 to indicate at the end of the list)
//  bRefresh  : Specifies the whether the list will be refreshed immediately
//
// Returns the index of the inserted row, -1 if failed
int InsertItem(LPCTSTR strHeading, int nItem, BOOL bRefresh);
C++
// Insert a row
//
//  pItem : Specifies the pointer to a GV_ITEM structure that
//  defines the attributes of an item
//  bRefresh : Specifies the whether the list will be refreshed immediately
//
// Returns the index of the inserted row, -1 if failed
int InsertItem(GV_ITEM* pItem, BOOL bRefresh);
C++
// Delete a Tree Item as well as the corresponding row
//
//  hNode : Specifies the node to be deleted
//
// Returns TRUE if successful, otherwise FALSE
BOOL DeleteItem(GTV_HTREENODE hNode);
C++
// Delete a row
//
//  nItem : Specifies the row to be deleted
//
// Returns TRUE if successful, otherwise FALSE
BOOL DeleteItem(int nItem);
C++
// Delete all rows including tree nodes information
//
// Returns TRUE if successful, otherwise FALSE
BOOL DeleteAllItems();
C++
// Expand a tree node to display its child
//
//  hNode : Specifies the node to be expanded
//
// Returns TRUE if successful, otherwise FALSE
BOOL ExpandTreeNode(GTV_HTREENODE hNode);
C++
// Collapse a tree node to hide its child
//
//  hNode : Specifies the node to be collapsed
//
// Returns TRUE if successful, otherwise FALSE
BOOL CollapseTreeNode(GTV_HTREENODE hNode);
C++
// Specifies whether the expand/collapse button should be shown next to a tree node
//
//  hNode : Specifies the node
//  bShow : Specifies whether to show the button
//
void ShowTreeNodeButton(GTV_HTREENODE hNode, BOOL bShow = TRUE);
C++
// Gets whether the expand/collapse button should be shown next to a tree node
//
// Returns TRUE if tree node button is shown, FALSE otherwise
BOOL IsShownTreeNodeButton(GTV_HTREENODE hNode);
C++
// Move a row (and its children) under a new parent node
//
//  hNode  : Specifies the node to be moved
//  hNewParent : Specifies the parent node
//  hInsertAfter : Specifies the sibling node after which the row will be inserted
//
// Returns TRUE if successful, otherwise FALSE
BOOL MoveTreeNode(GTV_HTREENODE hNode, GTV_HTREENODE hNewParent,
   GTV_HTREENODE hInsertAfter = GLVI_LAST);
C++
// Retrieve the row index of a node
//
//  hNode : Specifies the node
//
// Returns the row index of the specified node
int FindRowIndex(GTV_HTREENODE hNode);
C++
// Retrieve the range of rows of the descendant nodes of a given node
//
//  hParentNode : Specifies the node
//  range  : Output parameter to retrieve the row range
//
// Returns TRUE if successful, otherwise FALSE
BOOL FindChildrenRowRange(GTV_HTREENODE hParentNode, CItemRange& range);
C++
// Set the vertical and horizontal colors of the tree hierarchy lines
//
//  clrTreeVertLine : Specifies the vertical line color
//  clrTreeHorzLine : Specifies the horizontal line color
//
void SetTreeLineColors(COLORREF clrTreeVertLine, COLORREF clrTreeHorzLine);
C++
// Retrieve the horizontal line color of the tree hierarchy lines
//
// Returns the color
COLORREF GetHorzTreeLineColor();
C++
// Retrieve the vertical line color of the tree hierarchy lines
//
// Returns the color
COLORREF GetVertTreeLineColor();
C++
// Sort based on a column
//
//  nColumn  : Specifies the column to use for sorting
//  bAscending : Specifies the sort direction
//  data   : Specifies the user data
//
// Returns TRUE if successful, otherwise FALSE
BOOL SortItems(int nColumn, BOOL bAscending, LPARAM data = 0);
C++
// Sort the tree list based on a column using compare function
//
//  pfnCompare : Specifies the function pointer to the compare function
//  nColumn  : Specifies the column to use for sorting
//  bAscending : Specifies the sort direction
//  data   : Specifies the user data
//
// Returns TRUE if successful, otherwise FALSE
BOOL SortItems(PFNLVCOMPARE pfnCompare, int nColumn, BOOL bAscending, LPARAM data = 0);
C++
// Sort the tree list based on a column by text (using a built-in text compare function)
//
//  nColumn  : Specifies the column to use for sorting
//  bAscending : Specifies the sort direction
//  data   : Specifies the user data
//
// Returns TRUE if successful, otherwise FALSE
BOOL SortTextItems(int nColumn, BOOL bAscending, LPARAM data = 0);
C++
// Sort items under a node based on a column
//
//  hParent  : Specifies the node
//  nColumn  : Specifies the column to use for sorting
//  bAscending : Specifies the sort direction
//  data   : Specifies the user data
//  bRecursive : Specifies if recursively all children will be sorted
//
// Returns TRUE if successful, otherwise FALSE
BOOL SortItems(GTV_HTREENODE hParent, int nColumn,
   BOOL bAscending, LPARAM data, BOOL bRecursive = TRUE);
C++
// Sort items under a node based on a column
//
//  hParent  : Specifies the node
//  pfnCompare : Specifies the callback compare function
//  nColumn  : Specifies the column to use for sorting
//  bAscending : Specifies the sort direction
//  data   : Specifies the user data
//  bRecursive : Specifies if recursively all children will be sorted
//
// Returns TRUE if successful, otherwise FALSE
BOOL SortItems(GTV_HTREENODE hParent,
   PFNLVCOMPARE pfnCompare, int nColumn, BOOL bAscending,
   LPARAM data = 0, BOOL bRecursive = TRUE);
C++
// Determine whether the specified tree node has child nodes
//
//  hNode : Specifies the handle of a tree node
//
// Returns TRUE if the tree node specified by hNode has child nodes, otherwise FALSE
BOOL NodeHasChildren(GTV_HTREENODE hNode);
C++
// Retrieve the tree ctrl node that is the child of a specified node
//
//  hNode : Specifies the handle of a tree node
//
// Returns the handle of the child node if successful; otherwise NULL
GTV_HTREENODE GetChildNode(GTV_HTREENODE hNode);
C++
// Retrieve the tree ctrl node that is the next sibling of a specified node
//
//  hNode : Specifies the handle of a tree node
//
// Returns the handle of the next sibling item if successful; otherwise NULL
GTV_HTREENODE GetNextSiblingNode(GTV_HTREENODE hNode);
C++
// Retrieve the tree ctrl node that is the previous sibling of a specified node
//
//  hNode : Specifies the handle of a tree node
//
// Returns the handle of the previous sibling node if successful; otherwise NULL
GTV_HTREENODE GetPrevSiblingNode(GTV_HTREENODE hNode);
C++
// Retrieve the parent of a specified node
//
//  hNode : Specifies the handle of a tree node
//
// Returns the handle of the parent item; otherwise NULL
GTV_HTREENODE GetParentNode(GTV_HTREENODE hNode);
C++
// Retrieves the root of the specified tree control
//
// Returns the handle of the root node; otherwise NULL
GTV_HTREENODE GetRootNode();
C++
// Retrieves the a node given a row
//
//  nItem : Specifies the row index
//
// Returns the handle of the root node; otherwise NULL
GTV_HTREENODE GetTreeNode(int nItem);
C++
// Retrieve the corresponding tree cell given a handle of a item
//
//  hItem : Specifies the handle of the item
//
// Return the pointer of the tree list control cell
CTreeCell* GetTreeCell(GTV_HTREENODE hItem);
C++
 // Traverse through all child nodes given a parent and 
 // send GTVN_ITEMTRAVERSE notifications along the way when coming across a node
 //
 //  hParentItem  : Specifies the handle of the parent item
 //  nRowIndex  : Specifies the row index of the parent item. 
 //  This is required for the fast traversal of node.
//(Use -1 if hParentItem == GetRootItem())
 //
 void TraverseNodes(GTV_HTREENODE hParentItem, int& nRowIndex);
C++
// Creates a number of top level tree items
BOOL SetItemCount(int nItems);
C++
int GetItemCount()
{
 return GetRowCount();
}
C++
// Create a array of items under a parent item given parent item row index
//
// Returns the row index of the last child inserted
int SetChildItemCount(int nParentItemIndex, int nItems);
C++
// Show or hide a row
//
//  nRow  : Specifies the row
//  bShow : Specifies whether to show or hide the row
//
// Returns TRUE if successful, otherwise FALSE
BOOL ShowRow(int nRow, BOOL bShow = TRUE);
C++
/*******************************************************************************/
// Set the default type of a tree column cell
// (combo, check-box, color, etc. with tree buttons)
//
//  cellType : Specifies the type (can be CT_DEFAULT or CT_CHECKBOX)
//
// Returns TRUE if successful, otherwise FALSE
BOOL SetTreeColumnCellTypeID(enum CellTypeID cellType);
C++
// Set the type of a cell (combo, check-box, color, etc. with tree buttons)
//
//  nRow  : Specifies the row index of the cell
//  nColumn : Specifies the column index of the cell
//  cellType : Specifies the type (can be CT_DEFAULT or CT_CHECKBOX)
//
// Returns TRUE if successful, otherwise FALSE
BOOL SetCellTypeID(int nRow, int nColumn, enum CellTypeID cellType);

// Retrieve the type of a cell (combo, check-box, color, etc.)
//
//  nRow  : Specifies the row index of the cell
//  nColumn : Specifies the column index of the cell
//
// Returns the type of the cell (combo, check-box, color, etc.)
CellTypeID GetCellTypeID(int nRow, int nColumn);
C++
// Set the type of a cell in the column (combo, check-box, color, etc.)
//
//  nColumn : Specifies the column
//  cellType : Specifies the type
//
// Returns TRUE if successful, otherwise FALSE
BOOL SetColumnCellTypeID(int nColumn, enum CellTypeID cellType);
C++
//Sets the cell text
void SetCellText(int row, int col, LPCTSTR pszText);
C++
//Sets the cell state
void SetCellState(int row, int col, DWORD nState);

//Gets the cell state
DWORD GetCellState(int row, int col);
C++
// Sets the tree column index
void SetTreeColumn(int nTreeCol)
{
 m_nTreeCol = nTreeCol;
}
C++
// Gets the tree column index
int GetTreeColumn()
{
 return m_nTreeCol;
}

Using the Code

Here is a look at how to initialize the tree:

C++
// Tree related stuff
GTV_HTREENODE hItem1;
GTV_HTREENODE hItem2;
GTV_HTREENODE hItem3;
GTV_HTREENODE hItem4;
GTV_HTREENODE hItem5;
GTV_HTREENODE hItemChild;
GTV_HTREENODE hItemChild1;
GTV_HTREENODE hItemChild2;
GTV_HTREENODE hItemChild3;
GTV_HTREENODE hItemChild4;
GTV_HTREENODE hItemChild2leveldown;
GTV_HTREENODE hItem3Child1;
GTV_HTREENODE hItem4Child;
GTV_HTREENODE hItem4Child1;
GTV_HTREENODE hItem2Child1;
C++
m_nFixCols = 1;
   m_nFixRows = 1;
   m_nCols = 1;
   m_nRows = 1;
C++
 TRY {
        m_Grid.SetFixedRowCount(m_nFixRows);
    m_Grid.SetFixedColumnCount(m_nFixCols);
   }
   CATCH (CMemoryException, e)
   {
    e->ReportError();
    return;
   }
      END_CATCH

m_Grid.SetRedraw(FALSE);
C++
m_Grid.InsertColumn(_T("Tree Column 0"));
m_Grid.InsertColumn(_T("Column 1"));
m_Grid.InsertColumn(_T("Column 2"));
m_Grid.InsertColumn(_T("Column 3"));
m_Grid.InsertColumn(_T("Column 4"));
m_Grid.InsertColumn(_T("Column 5"));

m_Grid.SetRedraw(TRUE);

hItem1 = m_Grid.InsertItem(_T("Root Item1"));
hItem2 = m_Grid.InsertItem(_T("Root ItemX2"));
hItem4 = m_Grid.InsertItem(_T("Root Item4"));
C++
// Better to keep the variables outside the loop so that
// they are not reinstantiated in every loop.
GV_ITEM Item;
Item.mask = GVIF_TEXT;
Item.row = 0;
Item.col = 0;
Item.strText = _T("Root Item3");
Item.iImage = rand()%m_ImageList.GetImageCount();
Item.mask  |= (GVIF_IMAGE);
hItem3 = m_Grid.InsertItem(&Item);

hItemChild = m_Grid.InsertItem(_T("Item1-Child 1"), hItem1);
hItemChild1 = hItemChild;
hItemChild2 = m_Grid.InsertItem(_T("Item1-Child 2"), hItem1);
hItemChild3 = m_Grid.InsertItem(_T("Item1-Child 3"), hItem1);
hItem2Child1 = m_Grid.InsertItem(_T("Item2-Child 1"), hItem2);
m_Grid.InsertItem(_T("Item1-Child 1-Child1"), hItemChild);
m_Grid.InsertItem(_T("2 level down"), hItemChild);
Item.mask &= ~GVIF_IMAGE;
Item.strText = _T("2 level down 2");
hItemChild2leveldown = m_Grid.InsertItem(&Item, hItemChild);
hItem3Child1 = m_Grid.InsertItem(_T("Item3-Child 1"), hItem3);

//**************************FILL / REFILL TABLE***************************************
// The Fill/Refill code can be in a separate function to be called whenever necessary
m_Grid.SetRedraw(FALSE);

CString str;
// fill rows/cols with Data
for (int row = 1; row < m_Grid.GetItemCount(); row++)
{
    for (int col = 2; col < m_Grid.GetColumnCount(); col++)
 {
  Item.mask = GVIF_TEXT;
     Item.row = row;
     Item.col = col;
  if (col < 1)
   str.Format(_T("Row %d"), row);
  else
   str.Format(_T("%d"),row*col);
  Item.strText = str;

  // Example of Setting Image and Background Color
  if (col == 4)
  {
   Item.iImage = rand()%m_ImageList.GetImageCount();
   Item.mask  |= (GVIF_IMAGE);
  }

  m_Grid.SetItem(&Item);

  if(row == 4 && col == 4)
  {
   m_Grid.SetCellTypeID(row, m_Grid.GetTreeColumn(), CT_CHECKBOX);
  }
    }
}
hItem5 = m_Grid.InsertItem(_T("Moved Item"));
m_Grid.MoveTreeNode(hItem5, hItem4);

GTV_HTREENODE is a typedef of a pointer to the CTreeNode class which contains the necessary tree children and tree state such as expanded/collapsed, etc. related information.

This also keeps the corresponding CGridCtrl row pointer to map between the Grid and the actual tree structure which is maintained by the CTreeGridCtrl.

Points of Interest

The sorting of the tree items is the most interesting part. I have given a provision in the CTreeGridCtrl to attach and detach a range of rows from the grid. Using this feature, I detach a set of child rows at the deepest level, sort it and then attach to the parent node and run the same process recursively also for the parent nodes and the parent of parent nodes up to the top level which ends below the root node.

History

  • 9th November, 2010: Article uploaded

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)