
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,
HTREEITEM tiItem )
-
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:
int ExternalDisplayItem (
CLeftView *tvTree,
HTREEITEM tiItem )
{
CTreeCtrl &tTree= tvTree->GetTreeCtrl ();
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,
HTREEITEM tiStart,
FuncPtrView fptrFunction )
- 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:
void CLeftView::OnSelchanged (
NMHDR *pNMHDR,
LRESULT *pResult )
{
NM_TREEVIEW *pNMTreeView = (NM_TREEVIEW *) pNMHDR;
CTreeCtrl &tTree = this->GetTreeCtrl ();
CTreeViewIterator *ptrTree = (CTreeViewIterator *) &tTree;
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:
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 )
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