各位高手,我最近正在做的一个项目中使用到了CTreeCtrl这个控件,需要当点击展开按钮时动态去服务器端数据库请求数据。需要在该分类下没有子分类的情况下显示那个展开按钮
而我们都知道:使用CTreeCtrl的InsertItem()这个方法添加Item时如果该项下没有子项,那这个Item前就不会有那个展开按钮(就是那个+号)。
于是我今天查了下MSDN发现可以通过将TVITEM的cChildren设置为I_CHILDRENCALLBACK强制让该项显示展开按钮。然后设置消息响应函数处理由点击展开按钮所处发的TVN_GETDISPINFO 消息。
可是我不知道为什么我设置的消息响应函数没法添加子项目,困惑了我一天。我希望能有高手能帮我解答下。以下是我的消息响应函数void CMutiTreeCtrl::OnTvnGetdispinfo(NMHDR *pNMHDR, LRESULT *pResult)
{
LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
if((pTVDispInfo->item.mask & TVIF_CHILDREN )   ==   0) 
{
            HTREEITEM hSelected = GetSelectedItem();
            TV_INSERTSTRUCT tvinsert;
    tvinsert.hParent=hSelected;
    tvinsert.hInsertAfter=TVI_LAST;
    tvinsert.item.mask=TVIF_TEXT;
    tvinsert.item.hItem=NULL;
    tvinsert.item.cchTextMax=6;
            tvinsert.item.lParam=0;
            tvinsert.item.pszText="展开后动态添加的节点";
            *pResult = 1;
}
else *pResult =0;
}我不知道我的问题说清楚没。请教各位,希望能得到解答,谢谢。

解决方案 »

  1.   

    看了你的问题,我才知道CTREECTRL原来还有这个参数,嘿嘿又学到一点。
    于是我在机子上试了一下,下面是我的代码。
    TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
          //if(pTVDispInfo->item.cChildren == I_CHILDRENCALLBACK)//我觉得应该这样判断,可结果为什么不对呢?
    if(pTVDispInfo->item.mask & TVIF_CHILDREN )//你这里是不是判断反了
    {
       //去除Parent I_CHILDRENCALLBACK属性
       TVITEM tvParent = pTVDispInfo->item;
       tvParent.cChildren=1;
       m_tree.SetItem(&tvParent);
       HTREEITEM hChild =m_tree.InsertItem("Child",tvParent.hItem);//加入child

               //设置Child I_CHILDRENCALLBACK属性
       TVITEM tvChild;
       ZeroMemory(&tvChild,sizeof(TVITEM));
       tvChild.mask =TVIF_CHILDREN|TVIF_HANDLE;
       tvChild.hItem = hChild;
       tvChild.cChildren =I_CHILDRENCALLBACK;
       m_tree.SetItem(&tvChild);
    }
    TRACE("%d\n",m_nCount++);
    *pResult = 0;
    1,被设为I_CHILDRENCALLBACK的项,即使mouse放在上面都会产生TVN_GETDISPINFO,我TRACE了一下,随便在上面动动mouse 就有上百个TVN_GETDISPINFO
    所以我在接受到第一个TVN_GETDISPINFO时就去除了当前项的I_CHILDRENCALLBACK属性,而把它设成1,表示有child
    2,可能是因为我对TVN_GETDISPINFO不了解吧,我感觉这个属性不好用,还不如直接Insert dummy child 方便.
    //早知道不学五笔了,好多字都想不起来怎么打了,麻烦
      

  2.   

    我也是看MSDN无意看到的。我觉得这个方法比较新鲜所以试看想用另一种方法实现。以前用的方法也就是楼上二位说的这种方法随便插入些子节点。或者是不是应该去响应OnItemExpanding()去实际的插入子节点?而响应这个TVN_DISPINFO又是干什么用的?
      

  3.   

    试一试这样:
    OnTvnGetdispinfo 函数里面就加一句话:
    pTVDispInfo->item.cChildren = 1;如下:
    void CXXX::OnTvnGetdispinfo(NMHDR *pNMHDR, LRESULT *pResult)
    {
    LPNMTVDISPINFO pTVDispInfo = reinterpret_cast<LPNMTVDISPINFO>(pNMHDR);
    pTVDispInfo->item.cChildren = 1;
    *pResult = 0;
    }然后在 TVN_ITEMEXPANDING 消息里面添加节点。
      

  4.   

    void CMyTreeView::PopulateTree()
    {
    CTreeCtrl& ctlTree = (CTreeCtrl&) GetTreeCtrl(); HTREEITEM hRoot = ctlTree.GetRootItem(); TV_INSERTSTRUCT itInsert = {0}; CString strText;
    strText = _T("Start"); itInsert.item.pszText = const_cast<LPTSTR>((LPCTSTR)strText);
    itInsert.item.state = TVIS_BOLD;
    itInsert.item.mask = TVIF_CHILDREN | TVIF_HANDLE | TVIF_TEXT |
    TVIF_STATE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
    itInsert.item.stateMask = TVIS_BOLD | TVIS_STATEIMAGEMASK;
    itInsert.item.cChildren = I_CHILDRENCALLBACK;
    itInsert.hParent = hRoot;
    HTREEITEM hItem = ctlTree.InsertItem(&itInsert); UpdateWindow();
    }
    void CMyTreeView::OnTvnItemexpanding(NMHDR *pNMHDR, LRESULT *pResult)
    {
    CWaitCursor curWait;
    SetRedraw(FALSE); LPNMTREEVIEW pNMTreeView = reinterpret_cast<LPNMTREEVIEW>(pNMHDR);
    // TODO: Add your control notification handler code here HTREEITEM hItem = pNMTreeView->itemNew.hItem; CTreeCtrl& ctlTree = (CTreeCtrl&) GetTreeCtrl();

    // remove all subitems
    HTREEITEM hRemove = ctlTree.GetChildItem(hItem);
    while(hRemove)
    {
    ctlTree.DeleteItem(hRemove);
    hRemove = ctlTree.GetChildItem(hItem);
    } CString strText = _T("这是动态增加的"); AddMySubItems(hItem, strText); // 需要自己实现的函数 SetRedraw(TRUE);
    Invalidate(); *pResult = 0;
    }
    void CMyTreeView::OnGetdispinfo(NMHDR* pNMHDR, LRESULT* pResult) 
    {
    TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;
    // TODO: Add your control notification handler code here
    pTVDispInfo->item.cChildren = 1;
    *pResult = 0;
    }
    以上代码是可运行的,但须略作修改。不妨一试。
      

  5.   

    如上两位直接在OnGetdispinfo中把cChildren设为1,我觉得欠妥。
    要知道鼠标不经意地划过item时,都会调用OnGetdispinfo,要是如两位那样,那不如在当初InsertItem时就把cChildren设为1了。
      

  6.   

    TVN_GETDISPINFO NotificationResIf the cChildren member of the item's TVITEM structure is the I_CHILDRENCALLBACK value, the control sends this notification to retrieve a value that indicates whether the item has child items. In this case, the mask member of lptvdi will have the TVIF_CHILDREN flag set. 
      

  7.   

    谢谢各位。问题在各位帮助下解决了。我在codeproject也提问了。得到的答案也是一样的。只是没有向各位高手一样贴出具体的代码。在这里把回复帖出来。做个记号,希望以后对同样遇到这个问题的兄弟有所帮助。TVN_GETDISPINFO 
    "Requests that a tree-view control's parent window 
    provide information needed to display or sort an item."You shouldn't be inserting items in response to this notification.
    You also shouldn't be calling GetSelectedItem().
    The TVN_ITEMEXPANDING notification is maybe a better place to insert
    the child items.
    Mark感谢高手帮忙。现在就去结帖