Click here to Skip to main content
Click here to Skip to main content

CTreeView Iterator

By , 27 Mar 2002
 

Sample Image

Introduction

Don't you find you have written the same segment of code many times to parse a subtree because the action you wanted to launch on the items was different. In order to avoid such code, I've written CTreeViewIterator class. This class contains a method called ApplyFunction to launch a function on each item/node of a subtree.

Implementation

The class I've written is dedicated to Explorer like applications (those with a CTreeView class on the left side). In that section, we're going to see how to use CTreeViewIterator in that general case. In the section Adaptation, we'll see how to modify it to use it in other cases.

When you generate your application using the wizard, Visual C++ doesn't give you the capability to choose the CTreeView class implementation. Therefore, you have to have a CLeftView in your application. In the LeftView.cpp file, add the following line in the include section.

#include "TreeViewIterator.h"

You're now able to use the CTreeViewIterator class in that module.

You must define the function that will receive the item/node as parameter. This function will be external to your CLeftView class even if defined in the same module. If you don't define it in this module, do not forget to create a definition to set it visible from this module. If for any reason, you do want to set it as a method of your CLeftView class, please read the Adaptation section. The signature of this function is predefined. It must be like this:

int ExternalFunction ( CLeftView *tvTreeView,  /* Handle on the tree view*/
                       HTREEITEM  tiItem )     /* Item in the tree */ 
  • This function must return an integer set to 1 if ok, 0 else.
  • The first parameter is to be a CLeftView*. It will receive a handle on the CLeftView to let you use. 
  • The second parameter is to be a HTREEITEM. It will receive a handle on the currently parsed item/node.

For example, let's consider this function:

//*******************************************************************
//  
//   FUNCTION: ExternalDisplayItem
//  
//   RETURNS:  int
//  
//   COMMENTS: External function to display the subtree as a list
//
//******************************************************************* 
int ExternalDisplayItem (   
    CLeftView  *tvTree,     /* Handle on the tree */   
    HTREEITEM   tiItem )    /* Item in the tree */
{   
    CTreeCtrl  &tTree= tvTree->GetTreeCtrl (); 

    // store the name
    tvTree->sFullList += tTree.GetItemText(tiItem) + "\r\n";

    return ( 1);
}

The goal here, is to store the subtree as a list in a string. So, the only thing the external function has to do is getting the name and concatenating it to the full list. To do so, it will use an attribute created in the CLeftView called sFullList (type CString). Since we got a handle on the CLeftView as first parameter, there's no problem till the attribute is public (if not, we should have use public methods like get & set). Using this method, you may update the tree item/node or the CLeftView the way you want.  

Now you have the function to apply to each item/node, you have to call it. To do so, you'll have to implement first the iterator and then, to call the ApplyFunction method with the right parameters.  

The ApplyFunction's signature is:

int CTreeViewIterator::ApplyFunction ( 
    CLeftView   *tvView,            /* Handler on the tree view */ 
    HTREEITEM    tiStart,           /* Item to start with */ 
    FuncPtrView  fptrFunction  )    /* Function to launch */
  • This function returns an integer set to 1 if ok, -1 if problem when parsing subtree, 0 if problem when calling the external function.
  • The first parameter is a handle on the CLeftView derived from CTreeView.
  • The second parameter is a handle on the item/node to consider as the root when parsing the subtree.
  • fptrFunction is a function pointer to the external function you previously created.

Using our precedent external function, we got this code:

//*******************************************************************
//
//  FUNCTION:   OnSelchanged
//
//  RETURNS:    void
//
//  COMMENTS:   Current selected item has changed
//
//
//*******************************************************************
void CLeftView::OnSelchanged (
    NMHDR   *pNMHDR,    /* handle on event values */
    LRESULT *pResult )  /* handle on event return value */
{
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
    NM_TREEVIEW *pNMTreeView = (NM_TREEVIEW *) pNMHDR;
    CTreeCtrl   &tTree = this->GetTreeCtrl ();
    /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

    // create the iterator
    CTreeViewIterator   *ptrTree =   (CTreeViewIterator *) &tTree; 
    
    // call the  function
    sFullList= ""; 
    ptrTree->ApplyFunction (this, pNMTreeView->itemNew.hItem, 
                            &ExternalDisplayItem ); 
    
    GetDocument()->UpdateAllViews ( this, 1L, (CObject *) &sFullList  );

    *pResult = 0;
   
}

The method OnSelChanged is launched when the user has select a new item/node in the treeview. Here, we have catched it to add our function call. As you can see, we first add the iterator implementation with:

CTreeViewIterator  *ptrTree = (CTreeViewIterator *)  tTree;

In fact, we may consider the iterator on the CTreeCtrl as a shell with a special method. Therefore, within ApplyFunction method, we will see the content of the CTreeCtrl to parse.

We now have to get the parameters:

  • The first one, the handle on the CLeftView is easy to get, it's this variable.
  • The second is the handle on the item/node. Since we are in the method that catch the new selection, we gonna extract the item address within the parameter pNMHDR using two lines:
    NM_TREEVIEW *pNMTreeView =   (NM_TREEVIEW *) pNMHDR; // to cast the event param
    pNMTreeView->itemNew.hItem // to refer to the new selected item
  • The last parameter is the handle on the external function we created.

That way, we obtain the following call:

ptrTree->ApplyFunction (this, pNMTreeView->itemNew.hItem, &ExternalDisplayItem ); 

Since this event may be caught many times, we initialize the variable sFullList before we parse the subtree.

When the ApplyFunction is finished, we update the other views with the generated string using the method UpdateAllViews.

Here we are, catching the UpdateAllViews event isn't the subject here but you may have a look at the sample project to get an idea.

Adaptation

That section is to explain you how to upgrade the CTreeViewIterator to adapt it to your own case. I wrote it as a FAQ. I'll update it with the cases you will propose. So, what's your problem?

Question: My CTreeView class is not named CLeftView.

Answer: Change the CTreeView classname in :

  • The function pointer's signature.
  • The ApplyFunction definition.
  • The ApplyFunction implementation.

Question: I want to set the called function a method of my class CLeftView.

Answer: You just have to change the function pointer's signature to add your class. For example, try this:

  • Change the External function to a public method called DisplayItem in CLeftView class.
  • Change the signature to :
    typedef int ( CLeftView::*FuncPtrView ) ( CLeftView * tTree, HTREEITEM tiItem );
  • Change the call of ApplyFunction method to :
    ptrTree->ApplyFunction (this, pNMTreeView->itemNew.hItem, &CLeftView::DisplayItem );
  • In ApplyFunction implementation, change the function call to:
    ( tvView->*fptrFunction ) ( tvView, tiCurrItem )

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

Julien Martino
Web Developer
France France
Member
After years passed in services companies, I now work for Euromaster, an international group dedicated to vehicules maintenance. I also develop for my own usage. I both use Java and C++ languages and now, I integrate XML documents and XSL syntax in my applications even if SQL Databases like Oracle remain my main data sources

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHow to set ActiveViewmemberdeodiaus13 Jan '06 - 10:45 
I have something similar to your application.
Instead, I have 2 nested splitter views.
Now, if I click on the CLeftView, I want to set it up so that I get all remaining keyboard inputs. For example, I might wish to change the text in the tree itself. I can do this if I SetFocus in the
CLeftView::OnMouseActivate( CWnd* pDesktopWnd, UINT nHitTest, UINT message)
{
#ifdef XWin_focus_policy
// you could force it by
::SetCapture(this);
// but then you have to manage and pass around the focus yourself
//whenever you are inside the client rect
#endif
CTreeView::OnMouseActivate();
}
method but my mouse curser still behaves as if the CEditView has the focus (arrow key cursor icon instead of the I cursor icon).
 

Generalhelp mysussAnonymous20 Apr '04 - 9:43 
how to load CTreeCtrl from database

GeneralRe: help mymemberJulien Martino28 Apr '04 - 1:24 
Sorry, I didn't see your message before. What do you mean by loading CTreeCtrl from a database????
Do you mean that you want a trigger to launch a tree control?
 
Julien Martino
Tel: +33 613 461 531
e-mail: julien.martino@caramail.com

GeneralGreat And Simple article but i need some more infomemberVikrant Vikrant22 Sep '03 - 10:20 
Great piece of simple and usefel code but i am confused over this code..
CTreeCtrl &tTree = this->GetTreeCtrl ();
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// create the iterator
CTreeViewIterator *ptrTree = (CTreeViewIterator *) &tTree;
How you are creating the iterator (it seems magic to me)
ie CTreeViewIterator and CTreeCtrl are not related and then how type casting is working ?
Plz Help me.

GeneralRe: Great And Simple article but i need some more infomemberJulien Martino23 Sep '03 - 23:06 
In fact, CTreeViewIterator and CTreeCtrl have a relationship since CTreeViewIterator is derivated from CTreeCtrl. It means that is a specialization of CTreeCtrl. It has all content of CTreeCtrl and is able to do more than CTreeCtrl. So, using that cast, I create a kind of mask on my pointer to the existing CTreeCtrl tTree.
 
Is it ok with that small explanation? If not, tell me and I'll try to help you.
 
Best regards,
 
Julien
 
PS: thanks for you voteBig Grin | :-D
 
Julien Martino
Tel: +33 613 461 531
e-mail: julien.martino@caramail.com

GeneralNice and simple to understandmemberMarcK19 Sep '03 - 2:45 
Used it in a typical quick hack prototype CTreeCtrl class. Got it working in 3 minutes Smile | :) . Thanx (nice clean and commented code also, in fact, a shame to use in a quich hack Wink | ;-)
GeneralRe: Nice and simple to understandmemberJulien Martino21 Sep '03 - 21:35 
Thanks a lot for your comment and the rate you gave to this article;). I enjoy if it helped youSmile | :)
 
Best regards,
 
Julien
 
Julien Martino
Tel: +33 613 461 531
e-mail: julien.martino@caramail.com

GeneralTreeView ItemLevelmemberUmakanth23 Jul '02 - 21:25 
I added a Items to the Microsoft Treeview Control ( Prarents and Childs ).
What i want is when i select a particular child or parent
i want at which level i was selected that item ?
 
Any help.
 
-Umakanth
GeneralRe: TreeView ItemLevelmemberJulien Martino24 Jul '02 - 21:16 
Do you mean you want to know the level of the selected item?
For example,
 
Item1
Item11
Item12
Item121
Item13
Item131
Item132
Item2
Item21

If Item131 is selected, level should be 3.
 
To do so, use the function GetParentItem. This function give you a handle on the parent of the item you pass as parameter. If the returned handle is null, it means that you reached the root. So, using a while syntax, you may count the times you may climb up in the tree from your current position (the selected item) and then, the counting result will be your level.
 
Does this answer to your question?
 
Regards,
 
Julien
 

 
Julien Martino
Tel: +33 688 474 838
e-mail: julien.martino@caramail.com

GeneralRe: TreeView ItemLevelsussumakanth25 Jul '02 - 19:36 
Thanks for your reply.
 
I require more clarification.
In the given example :
 
Item1
Item11
Item12
Item121
Item13
Item131
Item132
Item2
Item21
 
If Item131 is selected, level should be 3.
Now can we get the ID of this item ( i.e item 131 )using Microsoft Tree Control API ? If yes, how ? any help ?
 
Thank you.
-Umakanth

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130523.1 | Last Updated 28 Mar 2002
Article Copyright 2002 by Julien Martino
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid