Visual C++程序开发范例宝典(软件工程师典藏版)
上QQ阅读APP看书,第一时间看更新

2.7 树视图控件典型实例

树视图控件可以用来显示具有层次结构的数据,例如组织树、索引项、磁盘中的文件或成等级结构的数据库。下面通过几个实例介绍树视图控件的应用。

实例066 多级数据库带多级数据库树状结构数据显示

本实例是一个提高效率、人性化的程序

实例位置:光盘\mingrisoft\02\066

实例说明

树视图控件最大的特点就是可以非常清晰地显示数据之间的层次关系。本实例向大家介绍如何利用树状结构显示数据信息。运行程序,单击节点前的,展开下一级节点,如图2.27所示。

图2.27 多级数据库树状结构数据显示

技术要点

在使用树视图控件时,需要通过SetImageList函数来关联图像列表,使用InsertItem函数向树型控件中插入数据,利用GetNextItem函数遍历数据。

(1)InsertItem函数。InsertItem函数用于插入节点。语法如下:

            HTREEITEM InsertItem( LPTVINSERTSTRUCT lpInsertStruct );
            HTREEITEM InsertItem(UINT nMask, LPCTSTR lpszItem, int nImage, int nSelectedImage
            , UINT nState, UINT nStateMask, LPARAM lParam, HTREEITEM hParent, HTREEITEM hInsertAfter );
            HTREEITEM InsertItem( LPCTSTR lpszItem, HTREEITEM hParent = TVI_ROOT,
            HTREEITEM hInsertAfter = TVI_LAST );
            HTREEITEM InsertItem( LPCTSTR lpszItem, int nImage, int nSelectedImage,
            HTREEITEM hParent = TVI_ROOT, HTREEITEM hInsertAfter = TVI_LAST);

参数说明:

● lpInsertStruct:是TVINSERTSTRUCT结构指针,TVINSERTSTRUCT结构中包含了插入操作的详细信息。

● nMask:标识节点的哪些信息被设置。

● lpszItem:确定节点的文本。

● nImage:确定节点的图像索引。

● nSelectedImage:确定节点选中时的图像索引。

● nState:确定节点的状态。

● nStateMask:确定节点的哪些状态被设置。

● lParam:用于指定关联节点的附加信息。

● hParent:标识父节点句柄。

● hInsertAfter:标识新插入节点后面的节点句柄。

(2)GetNextItem函数。GetNextItem函数根据当前节点获取下一个节点。语法如下:

HTREEITEM GetNextItem( HTREEITEM hItem, UINT nCode );

参数说明:

● hItem:当前节点句柄。

● nCode:标识如何查找下一个节点,可选值如表2.2所示。

表2.2 nCode参数可选值表

实现过程

(1)新建一个基于对话框的应用程序。

(2)向工程中导入4个图标资源,并向对话框中添加1个树视图控件。

(3)主要程序代码如下:

        //定义图像列表,设置根节点
        Load_dep();                                              //自定义函数,取出数据库中的数据
        m_treeImageList.Create(16,16,ILC_MASK,4,1);              //创建图像列表
        m_treeImageList.Add(theApp.LoadIcon(IDI_ICON1));         //加载图标资源
        m_treeImageList.Add(theApp.LoadIcon(IDI_ICON2));
        m_treeImageList.Add(theApp.LoadIcon(IDI_ICON3));
        m_treeImageList.Add(theApp.LoadIcon(IDI_ICON4));
        HICON hIcon=::LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
        m_tree.SetImageList(&m_treeImageList,LVSIL_NORMAL);      //关联图像列表
        m_root=m_tree.InsertItem("国家",0,0);                    //插入根节点
        AddtoTree(m_root,0);
        m_tree.Expand(m_root,TVE_EXPAND);                        //展开节点
        //向树视图控件插入数据
        void CDJtreeDlg::AddtoTree(HTREEITEM m_node,int UPID)
        {
        HTREEITEM m_child;
        for(int i=0;i<a_upid.GetSize();i++)
        {
          if(UPID==atoi(a_upid.GetAt(i)))
          {
          switch(atoi(a_lx.GetAt(i)))
          {
            case 1:
              m_child = m_tree.InsertItem(a_name.GetAt(i),2,2,m_node);
              AddtoTree(m_child,atoi(a_id.GetAt(i)));
              break;
            case 2:
              m_child = m_tree.InsertItem(a_name.GetAt(i),1,1,m_node);
              AddtoTree(m_child,atoi(a_id.GetAt(i)));
              break;
            case 3:
              m_child = m_tree.InsertItem(a_name.GetAt(i),3,3,m_node);
              AddtoTree(m_child,atoi(a_id.GetAt(i)));
              break;
              }
            }
            }
        }

举一反三

根据本实例,读者可以:

实现学生分级管理。

实例067 节点拖动功能的树控件

本实例是一个提高基础技能的程序

实例位置:光盘\mingrisoft\02\067

实例说明

在程序中使用树控件通常描述一些具有层次关系的数据。例如,使用树控件显示企业的人事信息,显示地域信息等。有时,由于一些原因这些数据的层次关系会发生改变,如果能够利用鼠标拖动树控件中的节点来修改数据的层次关系,会极大地简化用户操作流程。运行程序,拖动树控件节点,如图2.28所示。

图2.28 节点拖动功能的树控件

技术要点

想要在树控件中实现节点的拖动,首先需要调用Create DragImage方法创建一个拖动的图像列表(CImageList),然后调用图像列表的BeginDrag方法开始一个拖拽操作,调用图像列表的DragEnter方法锁定窗口更新,在拖动过程中显示图像。接着在鼠标移动的过程中调用图像列表的DragMove方法移动图像,显示拖动的效果。最后调用图像列表的DragLeave方法解除对窗口的锁定,隐藏拖动的图像,调用图像列表的EndDrag方法结束拖曳操作。下面介绍这些方法的使用。

(1)CreateDragImage方法。树控件的CreateDragImage方法为指定的节点创建一个拖动的位图,并且为该位图创建一个图像列表,将位图添加到图像列表中。语法如下:

CImageList* CreateDragImage(HTREEITEM hItem);

参数说明:

● hItem:表示树控件中指定的节点。

返回值:方法返回值是图像列表指针。

(2)BeginDrag方法。图像列表CImageList的BeginDrag方法用于开始拖动一个图像。语法如下:

BOOL BeginDrag(int nImage, CPoint ptHotSpot);

参数说明:

● nImage:表示拖动的图像索引。

● ptHotSpot:表示开始拖动的起点坐标。

返回值:如果方法执行成功,返回值为TRUE,否则为FALSE。

(3)DragEnter方法。图像列表的DragEnter方法用于在拖动过程中锁定窗口更新,显示拖动的图像。语法如下:

static BOOL PASCAL DragEnter(CWnd* pWndLock, CPoint point);

参数说明:

● pWndLock:表示需要锁定的窗口对象。

● point:表示显示拖动图像的位置。

返回值:如果方法执行成功,返回值为TRUE,否则为FALSE。

(4)DragMove方法。图像列表的DragMove方法用于在拖动过程中移动图像到指定的位置。语法如下:

static BOOL PASCAL DragMove(CPoint pt);

参数说明:

● pt:表示将图像移动到新的位置。

返回值:如果方法执行成功,返回值为TRUE,否则为FALSE。

(5)DragLeave方法。图像列表的DragLeave方法用于解除对窗口的更新,隐藏拖动的图像。语法如下:

static BOOL PASCAL DragLeave(CWnd* pWndLock);

参数说明:

● pWndLock:表示之前锁定更新的窗口对象。

返回值:如果方法执行成功,返回值为TRUE,否则为FALSE。

(6)EndDrag方法。图像列表的EndDrag方法用于结束拖拽操作。语法如下:

static void PASCAL EndDrag( );

实现过程

(1)新建一个基于对话框的应用程序。

(2)向对话框中添加树控件,设置树控件的属性如图2.29所示。

(3)从CTreeCtrl派生一个子类CDragTree,利用类向导将树控件命名为“m_DragTree”,其类型为“CDragTree”。

(4)向CDragTree类中添加成员变量。代码如下:

        CImageList*  m_pDragImages;                     //拖动的图像列表
        BOOL m_bDrag;                                //是否进行拖动
        HTREEITEM  m_hBeginDrag;                     //拖动的起点

图2.29 树控件属性设置

(5)向CDragTree类中添加CopyNodes方法,在拖动节点时,进行节点的移动,将源节点及其子节点复制到目标节点下。代码如下:

        void CDragTree::CopyNodes(HTREEITEM hDesItem, HTREEITEM hSrcItem)
        {
            if(hDesItem==NULL||hSrcItem==NULL)               //验证参数
            {
                return;
            }
            TVITEM tvItem;                                    //定义项目信息
            tvItem.mask=TVIF_TEXT|TVIF_IMAGE;                //设置返回标记
            tvItem.hItem = hSrcItem;
            char chText[MAX_PATH] = {0};
            tvItem.pszText = chText;
            tvItem.cchTextMax = MAX_PATH;
            GetItem(&tvItem);                                //获取项目信息
            TVINSERTSTRUCT tvInsert;                          //定义插入操作的数据结构
            tvInsert.hParent = hDesItem;
            tvInsert.item = tvItem;
            HTREEITEM hInsert=InsertItem(&tvInsert);              //插入项目
            HTREEITEM hChild=GetChildItem(hSrcItem);              //获取子节点
            while(hChild!=NULL)                                  //遍历子节点
            {
                  tvItem.mask = TVIF_TEXT|TVIF_IMAGE;
                  tvItem.hItem = hChild;
                  tvItem.pszText = chText;
                  tvItem.cchTextMax = MAX_PATH;
                  GetItem(&tvItem);
                  tvInsert.hParent = hInsert;
                  tvInsert.item = tvItem;
                  CopyNodes(hInsert,hChild);                     //递归调用
                  hChild=GetNextSiblingItem(hChild);             //查找下一个兄弟节点
            }
        }

(6)向CDragTree类中添加OnBegindrag方法,在开始拖动节点时调用。代码如下:

        void CDragTree::OnBegindrag(NMHDR* pNMHDR, LRESULT* pResult)
        {
            NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
            HTREEITEM hItem=pNMTreeView->itemNew.hItem;            //获取开始拖动的节点
            if(hItem==GetRootItem())                              //不允许拖动根节点
            {
                  *pResult = 0;
                  return;
            }
            m_hBeginDrag=hItem;                                   //记录开始拖动的项目
            m_pDragImages=CreateDragImage(hItem);                 //创建拖动的图像列表
            CPoint dragPT;                                         //记录起始点
            dragPT.x = pNMTreeView->ptDrag.x;
            dragPT.y = pNMTreeView->ptDrag.y;
            if (m_pDragImages != NULL)
            {
                  m_pDragImages->BeginDrag(0,CPoint(8,8));        //开始拖动图像
                  ClientToScreen(&dragPT);                        //转换客户坐标到屏幕坐标
                  m_pDragImages->DragEnter(this,dragPT);          //锁定窗口更新,在拖动的过程中显示拖动的图像
                  SetCapture();                                   //开始鼠标捕捉
                  m_bDrag = TRUE;
            }
            *pResult = 0;
        }

(7)在CDragTree类的消息映射部分添加映射宏,在用户拖动节点时执行OnBegindrag方法。代码如下:

ON_NOTIFY_REFLECT(TVN_BEGINDRAG, OnBegindrag)

(8)当鼠标在树控件上移动时,如果当前拖动节点,则设置拖动图像的位置。代码如下:

        void CDragTree::OnMouseMove(UINT nFlags, CPoint point)
        {
            if(m_bDrag)                                         //处于拖动状态
            {
                  HTREEITEM    hItem;
                  UINT    nHitFlags;
                  CRect        clientRC;
                  GetClientRect(&clientRC);                     //获取客户区域
                  m_pDragImages->DragMove(point);               //设置拖动的图像位置
                  //鼠标经过时高亮显示
                  if( (hItem = HitTest(point, &nHitFlags)) != NULL )
                  {
                    CImageList::DragShowNolock(FALSE);          //隐藏拖动的图像
                    SelectDropTarget(hItem);                    //设置选中的项目
                    CImageList::DragShowNolock(TRUE);           //显示拖动的图像
                  }
            }
            else
                  CTreeCtrl::OnMouseMove(nFlags, point);
        }

(9)处理释放鼠标按钮的单击事件,如果用户正在进行拖动操作,此时将结束拖动操作。代码如下:

            void CDragTree::OnLButtonUp(UINT nFlags,CPoint point)
            {
            if(m_bDrag)                                    //处于拖动状态
            {
                    m_bDrag=FALSE;
                CImageList::DragLeave(this);                   //解除对树控件的锁定,隐藏拖动的图像
                CImageList::EndDrag();                         //结束图像拖动
                ReleaseCapture();                              //释放鼠标捕捉
                delete m_pDragImages;                           //释放图像列表
                m_pDragImages = NULL;
                CRect winRC;
                GetWindowRect(&winRC);                         //获取窗口区域
                HTREEITEM hItem;
                if((hItem = HitTest(point, &nFlags)) != NULL)
                {
                    //进行拖动处理
                    //如果目标项目与开始拖动的项目相同或者目标项目仍是开始项目的父节点,不进行处理
                    if (m_hBeginDrag != hItem && hItem != GetParentItem(m_hBeginDrag))
                    {
                        CopyNodes(hItem,m_hBeginDrag);         //进行节点的复制
                        DeleteItem(m_hBeginDrag);              //删除源节点
                    }
                    Invalidate();
                    SelectDropTarget(NULL);
                    m_hBeginDrag = NULL;
                }
            }
        }

举一反三

根据本实例,读者可以:

动态修改树控件节点。

实例068 复选功能的树状结构

本实例可以方便操作、提高效率

实例位置:光盘\mingrisoft\02\068

实例说明

图2.30 带复选功能的树状结构

在树状结构的节点前添加复选框,可以使用户很方便地对相关信息进行检索。运行程序,选择节点前的复选框,单击“确定”按钮,在RichEdit控件中显示选择的节点信息,如图2.30所示。

技术要点

本实例利用了树视图控件的Check Boxes属性,在树状节点前添加复选框,然后使用GetCheck函数判断复选框是否被选中。GetCheck函数原型如下:

BOOL GetCheck( HTREEITEM hItem ) ;

参数说明:

● hItem:节点句柄。

实现过程

(1)新建一个基于对话框的应用程序。

(2)向窗体中添加一个树视图控件和一个RichEdit控件,用鼠标右键单击树视图控件,在弹出的快捷菜单中选择Properties选项,选择Check Boxes属性。

(3)主要程序代码如下:

        void CFXtreeDlg::OnOK()
        {
        HTREEITEM item;
        item=m_tree.GetRootItem();                  //获得根节点
        str="";
        if(m_tree.GetCheck(item)!=0)                //如果根节点被选中
        {
            str+=m_tree.GetItemText(item);             //获得根节点文本
            str+="\n";
            m_rich.SetWindowText(str);
            }
            OuttoTree(item);
            //CDialog::OnOK();
        }
        //递归判断节点前复选框是否选中
        void CFXtreeDlg::OuttoTree(HTREEITEM m_node)
        {
            HTREEITEM m_child;
            m_node=m_tree.GetChildItem(m_node);        //获得子节点
            if (m_node != NULL)
            {
            while (m_node!= NULL)
            {
              if(m_tree.GetCheck(m_node)!=0)           //判断当前节点是否选中
              {
            str+=m_tree.GetItemText(m_node);
            str+="\n";
            m_rich.SetWindowText(str);
              }
              m_child = m_node;
              OuttoTree(m_child);
              m_node=m_tree.GetNextItem(m_node,TVGN_NEXT); //查找下一个节点
            }
            }
        }

举一反三

根据本实例,读者可以:

实现在餐饮管理系统中显示某一餐台或包房的用餐信息。

实例069 三态效果树控件

本实例可以方便操作、提高效率

实例位置:光盘\mingrisoft\02\069

实例说明

许多应用软件的树形控件具有漂亮的复选框,这是如何实现的呢?本实例利用位图设计了一个具有三态效果复选框的树形控件,如图2.31所示。

技术要点

实际上,树形控件中的复选框是通过图像状态索引绘制的。首先设计一个位图,其中包含视图项的各个状态,将位图添加到CImageList中,然后调用CTreeCtrl的SetImageList方法设置图像状态索引。

图2.31 三态效果树控件

实现过程

(1)新建一个基于对话框的应用程序。

(2)向窗体中添加一个树视图控件,用鼠标右键单击树视图控件,在弹出的快捷菜单中选择Properties选项,为树控件设置属性。

(3)主要程序代码如下:

            void CBitTreeCtl::OnLButtonDown(UINT nFlags,CPoint point)
            {
            HTREEITEM hItemInfo =HitTest(point, &m_Flags);
            nFlags = m_Flags;
            //TVHT_ONITEMSTATEICON表示用户定义的视图项的图标状态
            if ( m_Flags &TVHT_ONITEMSTATEICON )
            {
                //State: 0无状态, 1没有选择, 2部分选择, 3全部选择
                //12~15位表示视图项的图像状态索引
                UINT State = GetItemState( hItemInfo, TVIS_STATEIMAGEMASK ) >> 12;
                State=(State==3)?1:3;
                SetItemState( hItemInfo, INDEXTOSTATEIMAGEMASK(State), TVIS_STATEIMAGEMASK );
            }
            else
                CTreeCtrl::OnLButtonDown(nFlags, point);
        }
        //设置节点状态
        BOOL CBitTreeCtl::SetItemState(HTREEITEM hItem, UINT State, UINT StateMask, BOOL IsSearch)
        {
            BOOL ret=CTreeCtrl::SetItemState( hItem, State, StateMask );
            UINT nState =State >> 12;
            if(nState!=0)
            {
                if(IsSearch)
                    RansackChild(hItem, nState);
                RansackParentAndChild(hItem,nState);
            }
            return ret;
        }
        //遍历子节点
        void CBitTreeCtl::RansackChild(HTREEITEM hItem, UINT State)
        {
            HTREEITEM hChildItem,hBrotherItem;
            //查找子节点
            hChildItem=GetChildItem(hItem);
            if(hChildItem!=NULL)
            {
                //将所有子节点的状态设置与父节点相同
                CTreeCtrl::SetItemState( hChildItem, INDEXTOSTATEIMAGEMASK(State), TVIS_STATEIMAGEMASK );
                //再递归处理子节点的子节点
                RansackChild(hChildItem, State);
                //搜索子节点的兄弟节点
                hBrotherItem=GetNextSiblingItem(hChildItem);
                while (hBrotherItem)
                {
                    //设置子节点的兄弟节点状态与当前节点的状态一致
                    int nState = GetItemState( hBrotherItem, TVIS_STATEIMAGEMASK ) >> 12;
                    if(nState!=0)
                    {
                        CTreeCtrl::SetItemState( hBrotherItem, INDEXTOSTATEIMAGEMASK(State),
                            TVIS_STATEIMAGEMASK );
                    }
                    //再递归处理兄弟节点
                    RansackChild(hBrotherItem, State);
                    hBrotherItem=GetNextSiblingItem(hBrotherItem);
                }
            }
        }
        void CBitTreeCtl::RansackParentAndChild(HTREEITEM hItem, UINT State)
        {
            HTREEITEM hNextItem,hPrevItem,hParentItem;
            //查找父节点,没有就结束
            hParentItem=GetParentItem(hItem);
            if(hParentItem!=NULL)
            {
                UINT nState1=State;//设初始值,防止没有兄弟节点时出错
                //查找当前节点的所有兄弟节点,如果所有兄弟节点状态都相同
                //设置父节点的状态
                //查找当前节点下面的兄弟节点的状态
                hNextItem=GetNextSiblingItem(hItem);
                while(hNextItem!=NULL)
                {
                    nState1 = GetItemState( hNextItem, TVIS_STATEIMAGEMASK ) >> 12;
                    if(nState1!=State && nState1!=0) break;
                    else hNextItem=GetNextSiblingItem(hNextItem);
                }
                if(nState1==State)
                {
                    //查找当前节点上面的兄弟节点的状态
                        hPrevItem=GetPrevSiblingItem(hItem);
                        while(hPrevItem!=NULL)
                    {
                            nState1=GetItemState(hPrevItem,TVIS_STATEIMAGEMASK)>>12;
                        if(nState1!=State && nState1!=0) break;
                        else hPrevItem=GetPrevSiblingItem
                              (hPrevItem);
                    }
                  }
                  if(nState1==State || nState1==0)
                  {
                        nState1=GetItemState(hParentItem,TVIS_STATEIMAGEMASK)>>12;
                    if(nState1!=0)
                    {
                        //如果状态一致,则父节点的状态与当前节点的状态一致
                            CTreeCtrl::SetItemState(hParentItem,INDEXTOSTATEIMAGEMASK(State),
                              TVIS_STATEIMAGEMASK );
                    }
                    //再递归处理父节点的兄弟节点和其父节点
                        RansackParentAndChild(hParentItem,State);
                  }
                  else
                  {
                    //状态不一致,则当前节点的父节点、父节点的父节点……状态均为第三态
                        hParentItem=GetParentItem(hItem);
                        while(hParentItem!=NULL)
                    {
                            nState1=GetItemState(hParentItem,TVIS_STATEIMAGEMASK)>>12;
                        if(nState1!=0)
                        {
                              CTreeCtrl::SetItemState( hParentItem, INDEXTOSTATEIMAGEMASK(2),
                                TVIS_STATEIMAGEMASK );
                        }
                            hParentItem=GetParentItem(hParentItem);
                    }
                  }
            }
            }

举一反三

根据本实例,读者可以:

自绘树控件。

实例070 修改树控件节点连线颜色

这是一个可以提高基础技能的实例

实例位置:光盘\mingrisoft\02\070

实例说明

在使用一些软件时,经常可以看到漂亮的树控件,而且这些树控件的节点连线都不是默认的黑色,本实例就实现了修改树控件的节点连线颜色的功能。程序运行结果如图2.32所示。

图2.32 修改树控件节点连线颜色

技术要点

树控件默认的节点连线、加号减号标记均是黑色的,为了修改默认的黑色,需要将树控件发送一个消息。在Visual C++ 2005中,树控件提供了SetLin eColor方法用于修改节点连线的颜色,但是在Visual C++ 6.0中没有提供该访问,只能采用原始发送消息的方式实现。发送的消息值为TV_FIR ST + 40,其中该消息的第2个参数lParam表示的是设置的颜色。下面的代码演示了设置节点连线的颜色。

实现过程

(1)新建一个基于对话框的应用程序。

(2)以CTree类为基类派生一个CLineTree类。

(3)在对话框上添加树视图控件,设置控件属性,并添加CLineTree类的成员变量m_Tree。

(4)主要程序代码如下:

        CLineTree::CLineTree()
        {
            m_clText = RGB(58, 79, 107);
            m_clBk = RGB(234, 242, 255);
            m_clLine = RGB(0, 0, 255);
        }
        void CLineTree::PreSubclassWindow()
        {
            //设置文本颜色
            SetTextColor(m_clText);
            //设置背景颜色
            SetBkColor(m_clBk);
            //设置线条颜色
            SendMessage(TV_FIRST + 40, 0, (LPARAM)m_clLine);
            CTreeCtrl::PreSubclassWindow();
        }
        }

举一反三

根据本实例,读者可以:

绘制树控件的背景及文本颜色。

实例071 位图背景树控件

这是一个可以启发思维的实例

实例位置:光盘\mingrisoft\02\071

实例说明

用户在使用一些软件时,有时会看到界面中的树状结构具有位图背景。本实例就设计了具有位图背景的树控件,实例运行结果如图2.33所示。

图2.33 位图背景树控件

技术要点

为了实现具有背景位图的树控件,首先要获得树形控件原始图像,并将原始图像绘制在一个画布对象上,然后再定义一个画布对象,将背景图片绘制在该画布对象上,最后调用BitBlt方法将两个画布对象进行运算绘制在树形控件上,这样就完成了具有背景位图的树控件的设计。

实现过程

(1)新建一个基于对话框的应用程序。

(2)以CTree类为基类派生一个CBitmapTree类。

(3)向对话框中添加一个树控件、一个静态文本控件和一个按钮控件,为树控件和静态文本控件添加成员变量m_Tree和m_Source。

(4)以CDC类为基类派生一个CMemDC类,代码如下:

        class CMemDC : public CDC
        {
            private:
            CBitmap*      m_Bmp;                                    //位图对象指针
            CBitmap*      m_OldBmp;                                       //位图对象指针
            CDC*          m_pDC;                                          //设备上下文
            CRect         m_Rect;                                         //CRect对象
            public:
            CMemDC(CDC*pDC,const CRect&rect):CDC()                         //构造函数
            {
                  CreateCompatibleDC(pDC);                                //创建与内存兼容的设备上下文
                  m_Bmp = new CBitmap;
                  m_Bmp->CreateCompatibleBitmap(pDC,rect.Width(),rect.Height()); //初始化内存兼容位图
                  m_OldBmp=SelectObject(m_Bmp);                           //选入位图
                  m_pDC=pDC;                                              //获得设备上下文
                  m_Rect=rect;                                            //获得矩形区域
            }
            ~CMemDC()                                                    //析构函数
            {
                  m_pDC->BitBlt(m_Rect.left, m_Rect.top, m_Rect.Width(), m_Rect.Height(),
                          this,m_Rect.left,m_Rect.top,SRCCOPY);           //绘制位图
                  SelectObject(m_OldBmp);
                  if(m_Bmp != NULL)
                          delete m_Bmp;
            }
            };

(5)在CBitmapTree类的头文件中声明变量,代码如下:

            CBitmap        m_Bitmap;                        //位图对象
            HANDLE         m_hBmp;                          //保存位图资源句柄
            CString        m_Path;                          //保存位图路径

(6)在CBitmapTree类中,处理按钮的WM_PAINT事件,在该事件的处理函数中为树控件绘制位图背景,代码如下:

        void CBitmapTree::OnPaint()
        {
            CPaintDC dc(this);
            m_hBmp=LoadImage(NULL,m_Path,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);                 //加载位图资源
            CRect rect;
            GetClientRect(&rect);                                                           //获得客户区域
            m_Bitmap.Attach(m_hBmp);
            CDC memdc;
            memdc.CreateCompatibleDC(&dc);                                                  //创建内存兼容设备上下文
            CBitmap bitmap;
            bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());                  //初始化位图
            memdc.SelectObject(&bitmap);                                                    //选入位图
            CWnd::DefWindowProc(WM_PAINT,(WPARAM)memdc.m_hDC,0);                            //获取原始画布
            CMemDC tempDC(&dc,rect);                                                         //构造CMemDC对象
            CBrush brush;
            brush.CreatePatternBrush(&m_Bitmap);                                            //创建位图画刷
            tempDC.FillRect(rect,&brush);                                                   //用位图画刷填充客户区域
            //将原始图片与背景进行组合
            tempDC.BitBlt(rect.left, rect.top, rect.Width(), rect.Height(),&memdc, rect.left, rect.top,SRCAND);
            brush.DeleteObject();                                                           //删除画刷对象
            m_Bitmap.Detach();                                                              //分离位图对象
        }

(7)在CBitmapTree类中,添加自定义函数SetTreeBK,该函数用于获得位图资源路径,代码如下:

        BOOL CBitmapTree::SetTreeBK(CString path)
        {
            m_Path = path;
            Invalidate();
            return true;
        }

(8)在CBitmapTree类中,处理按钮的TVN_ITEMEXPANDING事件,在该事件的处理函数中重绘树控件背景,代码如下:

        void CBitmapTree::OnItemexpanding(NMHDR* pNMHDR, LRESULT* pResult)
        {
            NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
            SetRedraw(FALSE);           //重绘树控件背景
            *pResult = 0;
        }

(9)在CBitmapTree类中,处理按钮的TVN_ITEMEXPANDING事件,在该事件的处理函数中禁止重绘树控件背景,代码如下:

            void CBitmapTree::OnItemexpanded(NMHDR*pNMHDR,LRESULT*pResult)
            {
            NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
            SetRedraw();                //不进行重绘
            *pResult = 0;
        }

举一反三

根据本实例,读者可以:

在树型控件中显示树型提示框。

实例072 显示磁盘目录

这是一个可以提高基础技能的实例

实例位置:光盘\mingrisoft\02\072

图2.34 显示磁盘目录

实例说明

本实例实现用树型控件显示磁盘目录,运行程序,在树型控件中将显示磁盘的分区,通过双击树型控件的节点可以查看该节点下的子目录。程序运行结果如图2.34所示。

技术要点

本实例主要通过GetLogicalDriveStrings函数先获取磁盘分区,然后处理树型控件的双击消息,双击某个目录就是用循环查找该目录下的全部子目录。

实现过程

(1)新建一个基于对话框的应用程序。

(2)在对话框上添加树视图控件,设置ID属性为IDC_TRDISKTREE,添加成员变量m_trdisktree。

(3)OnInitDialog方法中完成树状结构根节点的初始化,代码如下:

        BOOL CDiskCataDlg::OnInitDialog()
        {
          CDialog::OnInitDialog();
          …//此处代码省略
        imlst.Create(16,16,ILC_COLOR32|ILC_MASK,0,0);                           //创建图像列表
          m_trdisktree.SetImageList(&imlst,TVSIL_NORMAL);                       //关联图像列表
          m_trdisktree.ModifyStyle(0L,TVS_HASLINES|TVS_LINESATROOT);            //修改控件属性
          size_t alldriver=::GetLogicalDriveStrings(0,NULL);                     //获取磁盘分区
          _TCHAR*driverstr;
          driverstr=new_TCHAR[alldriver+sizeof(_T(""))];
        if(GetLogicalDriveStrings(alldriver,driverstr)!=alldriver-1)            //获得磁盘目录
          return FALSE;
          _TCHAR*pdriverstr=driverstr;
          size_t driversize=strlen(pdriverstr);
          HTREEITEM disktree;
          while(driversize>0)
          {
          SHGetFileInfo(pdriverstr,0,&fileinfo,sizeof(fileinfo),
          SHGFI_ICON);                                                          //获取系统文件图标
          imindex=imlst.Add(fileinfo.hIcon);
          disktree=m_trdisktree.InsertItem(pdriverstr,imindex,imindex,
          TVI_ROOT,TVI_LAST);                                                   //插入到树控件中
          pdriverstr+=driversize+1;
          driversize=strlen(pdriverstr);
          }
        return TRUE;
        }

(4)添加对TVN_SELCHANGED消息的处理函数,该函数实现在用户选择树状结构中某项时列出该项的子项,代码如下:

        void CDiskCataDlg::OnSelchangedTrdisktree(NMHDR* pNMHDR, LRESULT* pResult)
        {
        NM_TREEVIEW* pNMTreeView = (NM_TREEVIEW*)pNMHDR;
            CFileFind filefd;
            HTREEITEM parent;
            HTREEITEM rootitem=m_trdisktree.GetSelectedItem();       //获得当前选择的节点
            if(m_trdisktree.GetChildItem(rootitem))return;          //判断是否有子节点
            parent=rootitem;
            CString rootstr=m_trdisktree.GetItemText(rootitem);      //获得下一个节点
            CString temp;
            CString lstr;
            if(rootstr.Find("\\")==2)
            {
            lstr.Format("%s*.*",rootstr);
            }
            else
            {
              CString strparent;
              while(1)
              {
              parent=m_trdisktree.GetParentItem(parent);
              strparent=m_trdisktree.GetItemText(parent);
              if(strparent.Find("\\")==2)
            goto end;
              temp+=strparent;
              temp+="\\";
              }
        end:
            CString root=m_trdisktree.GetItemText(parent);
            lstr.Format("%s%s%s\\*.*",root,temp,rootstr);
            }
            BOOL bfinded=filefd.FindFile(lstr);
            //循环插入数据
            while(bfinded)
            {
              bfinded=filefd.FindNextFile();
              CString filepath;
            if(filefd.IsDirectory()&&!filefd.IsDots()){
              SHGetFileInfo(filefd.GetFilePath(),0,&fileinfo,sizeof(fileinfo),
              SHGFI_ICON);
            imindex=imlst.Add(fileinfo.hIcon);
              m_trdisktree.InsertItem(filefd.GetFileName(),imindex,imindex,rootitem);
              }
            }
            *pResult = 0;
        }

举一反三

根据本实例,读者可以:

利用树型控件显示全国行政区域。

实例073 树型提示框

这是一个可以启发思维的实例

实例位置:光盘\mingrisoft\02\073

图2.35 树型提示框

实例说明

树型提示框是将比较长的字符串用树状结构显示其层次效果,对字符串的要求是要有相同的字符作为分隔符,例如一个目录比较深的文件夹名。本实例实现了树型提示框的功能。运行程序,在文本框的字符上单击鼠标右键,提示窗体就会显示,实例运行结果如图2.35所示。

技术要点

本实例的实现需要新建基类为CEdit的新类treetipedit,在新类中处理鼠标右键的消息,鼠标右键消息产生时就提取文本框中的字符,通过在长字符串中查找分隔符将其分成若干字符,然后将这若干字符按层次关系添加到树型控件中。

实现过程

(1)新建一个基于对话框的应用程序。

(2)在工程中添加基于CTreeCtrl的新类tipwnd,添加基于CEdit的新类treetipedit。

(3)在对话框上添加两个静态文本控件,设置Caption属性分别为“用鼠标右键单击编辑框”和“分隔符”;添加两个编辑框控件,设置ID属性分别为IDC_EDSTR和IDC_EDSEP,添加IDC_EDSTR的成员变量m_str,继承自类treetipedit。

(4)在treetipedit类中添加显示树型窗体及窗体内容的ShowTipWindow函数,代码如下:

void tipwnd::ShowTipWindow(CString strtip,CString strsepar,CWnd* pParent,CPoint ptmouse){
if ( ::IsWindow( m_hWnd ) )
return;
CRect rctWnd;
CWnd* pMain = CWnd::GetDesktopWindow();
pParent->GetWindowRect( &rctWnd );
//获得窗口区域
pMain->ScreenToClient( &rctWnd );
//转换为客户坐标
CPoint ptParent( ptmouse.x + rctWnd.left -3, ptmouse.y+ rctWnd.top -3 );
CRect rct( ptParent.x, ptParent.y, ptParent.x + 500, ptParent.y+ 300);
Create( WS_BORDER|TVS_HASLINES|TVS_NOTOOLTIPS ,rct, pMain,-1 ); //创建树控件
HTREEITEM parent=TVI_ROOT;
CString strleft;
int pos=strtip.Find(strsepar);
//查找分隔符
//按分隔符取出字符串插入到树结构中
if(pos<=0)return;
while(pos>0){
strleft=strtip.Left(pos);
strtip=strtip.Right(strtip.GetLength()-pos-strsepar.GetLength());parent=InsertItem(strleft,parent);
pos=strtip.Find(strsepar);
if(pos<0){
parent=InsertItem(strtip,parent);}}
Expand(TVI_ROOT,TVE_EXPAND);
//展开节点
SetItemState(parent,TVIS_SELECTED ,0);
//设置节点状态
Select(parent,TVGN_CARET);
MoveWindow(rct.left,rct.top,200,100,FALSE);
//移动树控件
SetFocus();
//设置焦点
SetWindowPos( &wndTopMost ,0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
DWORD style = GetWindowLong( m_hWnd, GWL_EXSTYLE );SetWindowLong( m_hWnd, GWL_EXSTYLE, style | WS_EX_TOPMOST ); //设置风格ShowWindow(SW_SHOW);
//显示控件
}

(5)在treetipedit类的鼠标右键按下事件中,显示提示窗口,代码如下:

void treetipedit::OnRButtonDown(UINT nFlags, CPoint point){
CString edittext;
GetWindowText(edittext);
//获得提示字符串
m_tipwnd.ShowTipWindow(edittext,strseparator,this,point);
//显示提示窗口
CEdit::OnRButtonDown(nFlags, point);
}

(6)在treetipedit类的鼠标左键按下事件中,关闭提示窗口,代码如下:

void treetipedit::OnLButtonDown(UINT nFlags, CPoint point)
{
m_tipwnd.ClostTipWinow();
//关闭提示窗口
CEdit::OnLButtonDown(nFlags, point);
}

(7)在OnInitDialog中设置编辑框中的字符串,代码如下:

          BOOL CTreeTipDlg::OnInitDialog()
          {
          CDialog::OnInitDialog();
          //…此处代码省略
          CString strtemp="\\";
          GetDlgItem(IDC_EDSEP)->SetWindowText(strtemp);                               //设置分隔符
          GetDlgItem(IDC_EDSTR)->SetWindowText("C:\\WINNT\\system32\\drivers\\etc");   //设置文本
          m_str.strseparator=strtemp;
          return TRUE;
          }

举一反三

根据本实例,读者可以:

在树型控件中显示树型提示框。