Solved

Iterating CTreeCtrl items

Posted on 1998-07-27
8
2,801 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
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 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
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!

 

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 50 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

Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

Question has a verified solution.

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

Have you tried to learn about Unicode, UTF-8, and multibyte text encoding and all the articles are just too "academic" or too technical? This article aims to make the whole topic easy for just about anyone to understand.
In this post we will learn different types of Android Layout and some basics of an Android App.
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.
Do you want to know how to make a graph with Microsoft Access? First, create a query with the data for the chart. Then make a blank form and add a chart control. This video also shows how to change what data is displayed on the graph as well as form…

719 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