?
Solved

Iterating CTreeCtrl items

Posted on 1998-07-27
8
Medium Priority
?
2,998 Views
Last Modified: 2013-11-20
How can I walk a list of CTreeCtrl items?  I've written a class (say, CCustomerInfo) that is SetItemData()'d to a tree control item, and I need to save the list of items to disk.  The items show up, and I can access them with GetSelectedItem(), but the other GetXXXXX methods aren't working for me.  

Any suggestions?  Some example code would be appreciated!

0
Comment
Question by:chrisbill
  • 3
  • 2
  • 2
  • +1
8 Comments
 
LVL 4

Expert Comment

by:erajoj
ID: 1319801
This code is from www.CodeGuru.com (Zafir Anjum):

void CTreeCtrlX::Serialize(CArchive& ar)
{
        if (ar.IsStoring())
        {      
                // storing code
                HTREEITEM hti = GetRootItem();
                while( hti )
                {
                        int indent = GetIndentLevel( hti );
                        while( indent-- )
                                ar.WriteString( "\t" );
                        ar.WriteString( GetItemText( hti ) + "\r\n");
                        hti = GetNextItem( hti );
                }
               
        }
        else
        {      
                // loading code
                CString sLine;
                if( !ar.ReadString( sLine ) )
                        return;

                HTREEITEM hti = NULL;
                int indent, baseindent = 0;
                while( sLine[baseindent] == '\t' )
                        baseindent++;
                do
                {
                        if( sLine.GetLength() == 0 )
                                continue;
                        for( indent = 0; sLine[indent] == '\t'; indent++ )
                                ;               // We don't need a body
                        sLine = sLine.Right( sLine.GetLength() - indent );
                        indent -= baseindent;

                        HTREEITEM parent;
                        int previndent = GetIndentLevel( hti );
                        if( indent ==  previndent)
                                parent = GetParentItem( hti );
                        else if( indent > previndent )
                                parent = hti;
                        else
                        {
                                int nLevelsUp = previndent - indent;
                                parent = GetParentItem( hti );
                                while( nLevelsUp-- )
                                        parent = GetParentItem( parent );
                        }
                        hti = InsertItem( sLine, parent ? parent : TVI_ROOT, TVI_LAST );
                }while( ar.ReadString( sLine ) );

        }
}



int CTreeCtrlX::GetIndentLevel( HTREEITEM hItem )
{
        int iIndent = 0;

        while( (hItem = GetParentItem( hItem )) != NULL )
                iIndent++;
        return iIndent;
}


// GetNextItem  - Get next item as if outline was completely expanded
// Returns              - The item immediately below the reference item
// hItem                - The reference item
HTREEITEM CTreeCtrlX::GetNextItem( HTREEITEM hItem )
{
        HTREEITEM       hti;

        if( ItemHasChildren( hItem ) )
                return GetChildItem( hItem );           // return first child
        else{
                // return next sibling item
                // Go up the tree to find a parent's sibling if needed.
                while( (hti = GetNextSiblingItem( hItem )) == NULL ){
                        if( (hItem = GetParentItem( hItem ) ) == NULL )
                                return NULL;
                }
        }
        return hti;
}

/// John
0
 
LVL 8

Expert Comment

by:MikeP090797
ID: 1319802
Why can't you use GetItem, with the iItem of the LV_ITEM set to the index of the item you want to get?
0
 
LVL 8

Expert Comment

by:MikeP090797
ID: 1319803
Sorry, didn't saw the answer when posting the comment
0
Technology Partners: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 

Author Comment

by:chrisbill
ID: 1319804
I should have mentioned that I tried to use the extended tree control from codeguru.com, but I simply could not get it to work!  I derived a class from CTreeCtrl, but I had no way to make the CCustomerView use it. I tried rewriting GetTreeCtrl() to use my control in the view class, but I guess some vtbl methods (like Serialize) pointed into space :(



0
 
LVL 6

Expert Comment

by:snoegler
ID: 1319805
You could write a recursive function storing the items in an
array.

Take a look at this pseudo-code:

void myfunc(CTreeCtrl *control,
       HTREEITEM starthere,
       CArray<HTREEITEM,HTREEITEM>& myArr)
{
  HTREEITEM firstchild,currentchild;
  firstchild=control->GetNextItem(starthere,TVGN_CHILD);
  if(firstchild==NULL) return;
  currentchild=firstchild;
  for(;;) {
    myArr->Add(currentchild);
    if(control->ItemHasChildren(currentchild))
      // recurse if it has children
      myfunc(control,currentchild,myArr);

    currentchild=control->GetNextItem(currentchild,TVGN_NEXT);
    if(currentchild==NULL) break;
  }
}

After this, the CArray has received all HTREEITEMs of the
CTreeCtrl. Now you can fetch your information with the member
functions, like GetItemText(myArr[index]);
0
 
LVL 6

Expert Comment

by:snoegler
ID: 1319806
I forgot:
You have to start the recursion with

CArray<HTREEITEM,HTREEITEM> myArr;
myfunc(myControl,TVI_ROOT,myArr);
0
 

Author Comment

by:chrisbill
ID: 1319807
snoegler, I'm still interested in using my own CTreeCtrl inside a CTreeView.  I suppose I could always replace that first parameter of your function with my CTreeCtrl, but how can I stick it inside CTreeView?  I'd like to have the CTreeView serialize itself, and then serialize my CTreeCtrl.  
0
 
LVL 6

Accepted Solution

by:
snoegler earned 200 total points
ID: 1319808
- one possibility:
   To use your own CTreeView, you can simply use the code provided with the MFC:
   MFC contains the sources, so you can copy the MFC source, and replace its CTreeCtrl with
   your own CTreeCtrl.
- the other:
   If you override the GetTreeCtrl() function it should work. CTreeView does nothing other as
   casting itself to CTreeCtrl() - look in the sources. To avoid these vTable problems, why
   don't you use a non-virtual function as 'Serialize'? It will be called directly, so this problem is
   avoided.

To this storing issue:
That is now the complete code i used myself for the same purpose(the only difference is
that it was placed in a CTreeCtrl instead of CTreeView, but first take a look at it please)

void recurseThroughTree(CTreeCtrl *control,
                         HTREEITEM starthere,
                         CArchive& ar)
{
  if(ar.IsStoring()) {
    HTREEITEM firstchild,currentchild;
    firstchild=control->GetNextItem(starthere,TVGN_CHILD);
    if(firstchild==NULL) return;
    currentchild=firstchild;
    for(;;) {
      if(control->ItemHasChildren(currentchild))
            ar.Write("N",1); // This item is a 'node'
      else
            ar.Write("B",1); // This item is a 'branch'

      CString text=GetItemText(currentchild);
      int            length=text.GetLength()+1; // +1 for terminating '\0'

      ar.Write( length,sizeof(int));
      ar.Write( (LPCTSTR)text, length);

      if(control->ItemHasChildren(currentchild))
            // recurse if it has children
            recurseThroughTree(control,currentchild,ar);

      currentchild=control->GetNextItem(currentchild,TVGN_NEXT);
      if(currentchild==NULL) break;
    }
    if(starthere==TVI_ROOT) ar.Write("E",1); // End mark
  }      else
  {
    HTREEITEM      currentchild;
    for(;;) {
      char      nodeOrNot;  // 'N' as node, 'B' as branch
      int            length;
      CString      text;
      ar.Read(&nodeOrNot,1);

      if(nodeOrNot=='E') { // Is it the end?
        ASSERT(starthere==TVI_ROOT);
        return;
      }

      ar.Read(&length,sizeof(int));
      ar.Read(text.GetBuffer(length+1),length);
      text.ReleaseBuffer();

      currentchild=control->InsertItem(text,starthere);

      if(nodeOrNot=='B')
        recurseThroughTree(control,currentchild,ar);
    }
  }
}

void CTreeViewX::Serialize(CArchive& ar)
{
  // recurseThroughTree determinates itself if storing or not
  recurseThroughTree(&GetTreeCtrl(),TVI_ROOT,ar);
  // now you can proceed writing/reading other data
}
0

Featured Post

Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

In this article, I'll describe -- and show pictures of -- some of the significant additions that have been made available to programmers in the MFC Feature Pack for Visual C++ 2008.  These same feature are in the MFC libraries that come with Visual …
Introduction: Hints for the grid button.  Nested classes, templated collections.  Squash that darned bug! Continuing from the sixth article about sudoku.   Open the project in visual studio. First we will finish with the SUD_SETVALUE messa…
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …
Suggested Courses

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question