?
Solved

Iterating CTreeCtrl items

Posted on 1998-07-27
8
Medium Priority
?
2,872 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
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 

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

On Demand Webinar: Networking for the Cloud Era

Did you know SD-WANs can improve network connectivity? Check out this webinar to learn how an SD-WAN simplified, one-click tool can help you migrate and manage data in the cloud.

Question has a verified solution.

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

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…
Introduction: Dialogs (1) modal - maintaining the database. Continuing from the ninth article about sudoku.   You might have heard of modal and modeless dialogs.  Here with this Sudoku application will we use one of each type: a modal dialog …
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.
Monitoring a network: how to monitor network services and why? Michael Kulchisky, MCSE, MCSA, MCP, VTSP, VSP, CCSP outlines the philosophy behind service monitoring and why a handshake validation is critical in network monitoring. Software utilized …

752 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