在下拉列表框中显示树状结构

news/2024/6/18 13:08:50

  在下拉列表框中显示树状结构 收藏
    作者:星轨(oRbIt)
    E_Mail :inte2000@163.com
    转载请注明原作者,否则请勿转载

      在下拉列表框中显示树状结构 下拉列表框(或称组合列表框)是在Windows系统下开发软件最常用的控件之一,该控件适用性强且使用简单,因而深受开发人员喜爱。一个典型的用户界面就是使用下拉列表框(或称组合列表框)显示数据库中相关表名或某个表中的某个字段的全部数据,用户通过鼠标选择某一项后进行相关操作。在这种情况下使用下拉列表框对数据选择可以简化操作,用户也比较喜欢这种方式,但是简单的下拉列表框无法表达数据之间的相互关系,比如两个数据项之间的父子关系或包容关系等。开发人员通常希望能在简化用户操作的同时向用户提供这些信息以方便用户决定下一步操作。针对这种情况我们使用MFC开发了一个支持显示树状结构的CComboBox派生类CWzComboBox,并且该派生类已经成功应用到我们的软件中,现在我们将代码公开,希望对广大开发人员有所帮助,也希望大家共同完善它。(演示程序运行示例如图1所示)

 

图1 演示程序运行实例

      首先我们对CwzComboBox类的自定义函数和相关的数据结构做简要的说明。要使列表框能够显示树状结构,首先应该设计一个数据结构用于记录列表中各个项之间的关系。我们定义了一个结构用于存储节点之间的关系,不妨将之称为信息数据结构,如下所示:


typedef struct tagITEMDATA
{
  WORD wParantOriginIdx;
  WORD wOriginIdx;
  BYTE cType;
  BYTE cLevel;
}ITEMDATA,*LPITEMDATA;
 
结构成员wParantOriginIdx和wOriginIdx分别用以存储该项的父项和该项本身在列表中的原始索引位置,因为添加或删除列表项时会导致列表项之间的顺序改变,也就是索引位置会改变,所以只能通过各项的原始索引位置确定它们之间的关系。成员cType用于存储节点对应的图标在图标列表(ImageList)中的索引位置,成员cLevel用于存储该项的级别,处于根结点的项cLevel是0,子项的cLevel都比父项的大,该成员主要用在显示列表项时确定项的水平起始位置,从而使各项看起来呈树状结构。

        下面就详细说明一下类CWzComboBox的设计。基类CComboBox的原来的公有方法 AddString()和InsertString()不支持附加信息不能再使用,DeleteString()无法正确的释放附加信息所占用的内存所以也不能使用,为了防止开发人员误用他们引起代码崩溃,我们重载了这三个函数: 


virtual int AddString(LPCTSTR lpszString) { return -1; }
virtual int InsertString(int nIndex, LPCTSTR lpszString) { return -1; }
virtual int DeleteString(int nIndex) { return -1; } 
仅仅返回-1表示函数执行失败。手工加入AddCTString()和DeleteCTString()两个函数用于列表项的添加和删除。AddCTString()向第一个参数wParentIdx指定的项添加一个子项,函数运行时首先判断wParentIdx是否是nRootIndex(定义常量nRootIndex等于(WORD)-1),如果是nRootIndex就将该项设为根项,即令cLevel为0。


WORD CWzComboBox::AddCTString(WORD wParentIdx,BYTE cType,LPCTSTR lpszString)
{
  int idx = -1;
  if(wParentIdx == nRootIndex)//添加根项
  {
    idx = CComboBox::AddString(lpszString);//调用基类的函数添加该项到列表中
    LPITEMDATA pData = new ITEMDATA;//建立相应的项信息结构
    ASSERT(pData != NULL);     pData->cLevel = 0;//根为0级
    pData->cType = cType;
    pData->wOriginIdx = (WORD)idx;//该项当前的索引位置
    pData->wParantOriginIdx = wParentIdx;//父项的索引位置,就是nRootIndex
    SetItemData(idx,(DWORD)pData);
  }
  else//不是添加根项
  {
    int ParentCurrentIdx = CurrentIdxFromOriginIdx((int)wParentIdx);//根据父项的原始索引 位置找到父项当前的索引位置
    LPITEMDATA pParentData = (LPITEMDATA)GetItemData(ParentCurrentIdx);//读出项信息结构
    int count = GetChildCount(pParentData->wOriginIdx);//得到父项的子项个数
    int pos = wParentIdx + count + 1;//子项增加一个
    idx = CComboBox::InsertString(pos,lpszString);
    LPITEMDATA pData = new ITEMDATA;//建立子项的信息结构
    ASSERT(pData != NULL);
    pData->cLevel = pParentData->cLevel + 1;//比父项的级数大1
    pData->cType = cType;
    pData->wOriginIdx = (WORD)idx;
    pData->wParantOriginIdx = pParentData->wOriginIdx;
    SetItemData(idx,(DWORD)pData);
  }
  return (WORD)idx;//返回索引位置
}
 
删除某项时首先遍历整个列表,删除所有子项,释放为子项信息结构分配的内存,最后删除该项并释放该项信息结构占用的内存。根据树状结构的特性,某项的所有子项的索引位置都在该项和该项的下一个兄弟项之间,所以删除函数的任务就是删除该项后所有级数比该项高的项,直到遇到第一个级数与该项相同或更小的项。外层的while循环用来删除子项,直到标志bFind是FALSE才退出,内层的for循环从当前项的索引位置开始到列表的结尾搜索当前项的子项,只要找到就将bFind赋值为TRUE并删除该项,然后跳出for循环,继续外层的while循环。

 

int CWzComboBox::DeleteCTString(int index)//参数为当前项索引位置
{
  LPITEMDATA pdata = (LPITEMDATA)GetItemData(index);
  BOOL bFind = TRUE; //删除该项的所有子项,包括子项的子项
  while(bFind)
  {
    bFind = FALSE;//只要找到子项就设为TRUE,结束或遇到一个级数相同的项就保持FALSE,结束标 志
    int count = GetCount();//得到总的项数
    for(int i = index + 1; i < count; i++)//子项的索引位置总在该项之后
    {
      LPITEMDATA p = (LPITEMDATA)GetItemData(i);
      if(p->cLevel > pdata->cLevel)//子项的Level比父项的Level大
      {
        bFind = TRUE;//找到一个子项,可能还有
        CComboBox::DeleteString(i);
        break;//删除一个就跳出循环
      }
    }
  }
  //现在删除当前项
  CComboBox::DeleteString(index);
  return GetCount();
}
 在删除函数中用到了GetChildCount()和CurrentIdxFromOriginIdx()两个函数,这两个函数被声明为保护成员,不允许直接访问。GetChildCount()返回指定索引位置的项的所有子项,包括子项的子项,实现的方法很简单,就是利用前面提到的树状结构的特性,统计该项后所有级数比该项高的项的个数,直到遇到第一个级数与该项相同或更小的项。CurrentIdxFromOriginIdx()函数根据某项的原始索引位置查找并返回其当前位置索引位置,实现的方法也很简单,就是遍历整个列表,比较每一项的原始索引位置,找到相同的就返回其在列表中的当前索引位置。 以上是对CwzComboBox类的自定义函数和相关的数据结构的说明,现在要解释一下与MFC有关的函数。首先就自绘控件做简要的说明,事实上所有的控件都是“自绘”的,Windows系统的窗口在需要重画窗口内的控件时会向该控件发送WM_DRAWITEM消息,该消息有两个参数,第一个参数是控件的ID,第二个参数是一个指向DRAWITEMSTRUCT结构的指针,DRAWITEMSTRUCT结构含有控件绘制自己所需的显示设备上下文、位置、控件的状态和其他属性,控件就利用这些参数绘制自己。MFC对控件进行了封装,将WM_DRAWITEM消息其映射为虚函数DrawItem,从而简化了编程的复杂性。所以,要实现列表框显示自定义的树状结构只需重载DrawItem函数就可以了。当系统需要绘制列表框时就会多次调用DrawItem来画列表框的每一项。CwzComboBox类重载的DrawItem仅仅就是根据各项的信息结构在相应的位置画出该项,所以不需做太多说明。另一个需要重载的函数是DeleteItem,这也是基类的虚函数,重载此函数的目的是为了使系统删除列表项的同时能释放为该项信息结构分配的存储空间。我们自定义删除函数并没有直接释放信息结构占用的存储空间而是调用了基类的DeleteString函数,由DeleteString函数触发DeleteItem,这样做就简化了开发人员对列表项的维护工作。当下拉列表框被打开时会触发CBN_DROPDOWN事件,CwzComboBox类响应这个事件,计算使列表框中所有项能够完全显示所需的最小宽度值,并根据这个最小宽度值重设列表框的宽度,从而使其看起来有视觉效果好一些。    
        CwzComboBox类的的使用非常简单,看一下例子代码就知道了。此代码最早是发布在www.codeproject.com网站,所以项目的资源都英文的,注释也是英文的。www.vchelp.net网站也有转载,另外还可以使用以下URL:http://www.winmsg.com/download/cwzcombox.zip下载代码和演示程序。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/orbit/archive/2005/08/08/448458.aspx


http://www.niftyadmin.cn/n/3102311.html

相关文章

驱动对象与设备对象

驱动对象与设备对象&#xff08;转&#xff09; 这里所说的驱动对象是一种数据结构&#xff0c; 在DDK 中名为DRIVER_OBJECT。任何驱动程序都对应一个DRIVER_OBJECT.如何获得本人所写的驱动对应的DRIVER_OBJECT 呢&#xff1f;驱动程序的入口函数为DriverEntry,因此&#xff0c…

基于C++语言开发的Windows环境微型操作系统

一 需求分析 用高级语言编写程序&#xff0c;模拟实现一个简单功能的操作系统。 实现作业调度&#xff08;先来先服务&#xff09;、进程调度功能&#xff08;时间片轮转&#xff09; 实现内存管理功能&#xff08;连续分配&#xff09; 实现文件系统功能&#xff08;选做内…

驱动对象 设备对象 设备栈 乱杂谈

驱动对象 设备对象 设备栈 乱杂谈 作者: JIURL 主页: http://jiurl.yeah.net -------------------------------------------------------------------------------- 用有限的几句话就舒舒服服的建立起对驱动对象和设备对象的概念是不可能的。刚开始是…

基于VC++的MFC框架实现的飞机大战小游戏

一、类介绍 1.1 程序使用到的MFC类库中主要的类 CDC类 CRect类 CBitmap类 CImageList类 mfc框架&#xff1a;app类、wnd类、doc类、view类 1.2 项目包含的对象类 8个游戏类&#xff1a; enemy&#xff08;敌人&#xff09; bomb&#xff08;敌人子弹&#xff09; miss…

(WSS)WSS3.0安装文档库组件后恢复默认安全设置,上下文菜单消失了,想请教下各位老大是咋回事(问题有了突破)...

最近装了WSS3.0并且安装了文档库这个模板&#xff0c;原本我windows 2003的机器在鼠标移动到文档项目的名称列下某行的空白处会出现一个显亮的方框&#xff0c;左键单击后会出现一个上下文菜单&#xff0c;如下图&#xff1a; 后来因为其他机器&#xff08;有win7的&#xff0c…

基于C语言的Linux环境下socket编程

一 需求分析 柏克莱套接字&#xff0c;又称为BSD 套接字是一种应用程序接口&#xff0c;用于网际插座与Unix域套接字&#xff0c;包括了一个用C语言写成的应用程序开发库&#xff0c;主要用于实现进程间通讯&#xff0c;在计算机网络通讯方面被广泛使用。 使用Berkeley套接字…

Windows下使用标准Shell接口遍历文件和文件夹

Windows下使用标准Shell接口遍历文件和文件夹(1) 在Windows中我们经常需要遍历一个文件夹或者遍历一个磁盘。本文介绍如何使用标准的Shell接口进行遍历。在介绍过程中会逐步的实现一个类似FileZilla的TreeViewListView的界面。我最近为psftp做界面的时候简单了解了一下这方面的…

基于Qt和OpenCV实现彩色图和灰度图的转换

一、实验目的与要求 1.1 目的 熟悉Qt可视化开发&#xff0c;理解C的面向对象思想 熟悉Qt和Opencv开发环境搭建 了解Qt消息机制 初步理解Opencv的用法 学会使用c异常处理 1.2 要求 使用Qt编写一程序&#xff0c;点击按钮从电脑目录选择jpg图片&#xff0c;显示在界面上 再…