65.9K
CodeProject 正在变化。 阅读更多。
Home

CTreeView 迭代器

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.92/5 (22投票s)

2002年3月28日

4分钟阅读

viewsIcon

161510

downloadIcon

5459

一个迭代器,用于解析子树并在每个项目/节点上执行函数

Sample Image

引言

你是否发现自己编写了许多次相同的代码段来解析子树,因为你想对项目执行的操作不同。为了避免这样的代码,我编写了CTreeViewIterator类。该类包含一个名为ApplyFunction的方法,用于在子树的每个项目/节点上执行一个函数。

实现

我编写的类专用于类似 Explorer 的应用程序(那些左侧带有CTreeView类的应用程序)。在这一部分,我们将看到如何在一般情况下使用CTreeViewIterator。在 Adaptation 部分,我们将看到如何修改它以用于其他情况。

当您使用向导生成应用程序时,Visual C++ 不会给您选择CTreeView类实现的选项。因此,您的应用程序中必须有一个CLeftView。在LeftView.cpp文件中,在include部分添加以下行。

#include "TreeViewIterator.h"

您现在可以在该模块中使用CTreeViewIterator类了。

您必须定义一个函数,该函数将项目/节点作为参数接收。这个函数将是外部的,不属于您的CLeftView类,即使它定义在同一个模块中。如果您不在该模块中定义它,请不要忘记创建一个定义,使其在该模块中可见。如果出于任何原因,您想将其设置为CLeftView类的一个方法,请阅读 Adaptation 部分。该函数的签名是预定义的。它必须是这样的

int ExternalFunction ( CLeftView *tvTreeView,  /* Handle on the tree view*/
                       HTREEITEM  tiItem )     /* Item in the tree */ 
  • 此函数必须返回一个整数,设置为1表示成功,否则为0
  • 第一个参数是要传递的CLeftView*。它将接收一个指向CLeftView的句柄,以便您使用。
  • 第二个参数是要传递的HTREEITEM。它将接收一个指向当前正在解析的项目/节点的句柄。

例如,我们考虑这个函数

//*******************************************************************
//  
//   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);
}

这里的目标是将子树存储为string中的一个列表。所以,外部函数需要做的就是获取名称并将其连接到完整的列表中。为此,它将使用CLeftView中创建的一个名为sFullList的属性(类型为CString)。由于我们获得了指向CLeftView的句柄作为第一个参数,只要该属性是public的(否则,我们就需要使用public方法,如getset),就没有问题。使用此方法,您可以根据需要更新树项目/节点或CLeftView

现在您有了要应用于每个项目/节点的函数,您必须调用它。为此,您需要首先实现迭代器,然后使用正确的参数调用ApplyFunction方法。

ApplyFunction的签名是

int CTreeViewIterator::ApplyFunction ( 
    CLeftView   *tvView,            /* Handler on the tree view */ 
    HTREEITEM    tiStart,           /* Item to start with */ 
    FuncPtrView  fptrFunction  )    /* Function to launch */
  • 此函数返回一个整数:1表示成功,解析子树时出现问题为-1,调用外部函数时出现问题为0
  • 第一个参数是CLeftView派生自CTreeView的句柄。
  • 第二个参数是作为解析子树的根的项目/节点的句柄。
  • fptrFunction是指向您先前创建的外部函数的函数指针。

使用我们之前的外部函数,我们得到了这段代码

//*******************************************************************
//
//  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;   
}

OnSelChanged方法在用户在treeview中选择了一个新项目/节点时触发。在这里,我们捕获了它以添加我们的函数调用。正如您所见,我们首先使用以下代码添加了迭代器的实现

CTreeViewIterator  *ptrTree = (CTreeViewIterator *)  tTree;

事实上,我们可以将CTreeCtrl上的迭代器视为一个带有特殊方法的 shell。因此,在ApplyFunction方法内部,我们将解析CTreeCtrl的内容。

我们现在需要获取参数

  • 第一个参数,即CLeftView的句柄很容易获得,它就是这个变量。
  • 第二个参数是项目/节点的句柄。由于我们在捕获新选择的方法中,我们将使用两行从参数pNMHDR中提取项目地址
    NM_TREEVIEW *pNMTreeView =   (NM_TREEVIEW *) pNMHDR; // to cast the event param
    pNMTreeView->itemNew.hItem // to refer to the new selected item
  • 最后一个参数是我们创建的外部函数的句柄。

这样,我们就得到了以下调用

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

由于此事件可能会被捕获多次,因此我们在解析子树之前初始化变量sFullList

ApplyFunction完成后,我们使用UpdateAllViews方法使用生成的string更新其他视图。

这样,我们就完成了。捕获UpdateAllViews事件不是这里的主题,但您可以查看示例项目以获得一些想法。

适应性

本节将向您解释如何升级CTreeViewIterator以适应您自己的情况。我将其写成一个 FAQ。我会根据您提出的案例进行更新。那么,您的问题是什么?

问题:我的CTreeView类没有命名为CLeftView

回答:在以下位置更改CTreeView类名

  • 函数指针的签名
  • ApplyFunction的定义
  • ApplyFunction的实现

问题:我想将调用的函数设置为我的CLeftView类的一个方法。

回答:您只需更改函数指针的签名以添加您的类。例如,可以尝试这样做

  • 将外部函数更改为CLeftView类中的一个名为DisplayItempublic方法。
  • 将签名更改为
    typedef int ( CLeftView::*FuncPtrView ) ( CLeftView * tTree, HTREEITEM tiItem );
  • ApplyFunction方法的调用更改为
    ptrTree->ApplyFunction (this, pNMTreeView->itemNew.hItem, &CLeftView::DisplayItem );
  • ApplyFunction实现中,将函数调用更改为
    ( tvView->*fptrFunction ) ( tvView, tiCurrItem )

许可证

本文未附加明确的许可证,但可能在文章文本或下载文件本身中包含使用条款。如有疑问,请通过下面的讨论区联系作者。

作者可能使用的许可证列表可以在此处找到。

© . All rights reserved.