MFC树形控件(CTreeCtrl)的保存与读取

其中 对控件中所有节点的操作是最主要的遍历每个节点取得信息这是非常重要的。这里我将我使用树控件的方法写下来,它虽然不完美但很完整,完成了从新增到删除,从保存到读取的全部操作过程。它将保存的结果存于程序目录下的x.ini文件中,你可以打开它来查看保存的信息。这里不但保存了重新构造树结构的信息也保存了附加信息,可以自由修改。最重要的是,这个示例能够支持树结构的任何操作,包括换顺序、增加、删除,这些操作不会影响任何节点的ID顺序。节点ID也许或许需要重点说一下,它保存了你构造的树结构的节点顺序,这并不指序号顺序,如果你另一个程序读取了这个结构,引用了序号为2的节点,然后你在程序中在这个节点之前新增一个节点的话,这个节点的序号就变成3了,等到另一个程序再读取的话就变成引用3了。但在这个示例中 并不是根据序号保存的,而是另一个ID编号,你可以随意操作它的顺序位置实际的ID并不会改变。

新增与删除都是非常简单的,唯一要说的是新增的方法,我使用了取最小空值的方法,也就是循环所有节点取出没有值的序号,新增的节点就使用那个序号。也就是说如果你之前有4个节点,你删除了2号节点 ,你再新增的时候 ID就为2 ,而不是5。保存的方法与读取相关联,所以怎么保存决定了如何读取。保存的时候我使用了递归遍历所有节点,每个节点需要保存结构信息,以便以后读取重新构造树结构 否则 你保存为了文件如何能还原以前树的结构?
这里还保存了节点名称,节点序号,节点ID(重要)。

保存:

UINT fileSum=0;//列表总数
int layer=1;//层次

queryTreeNode(m_tree,m_tree.GetRootItem(),fileSum,layer);

//最后还要保存总数
CString _str;
_str.Format("%d",fileSum);
WritePrivateProfileString("INFO","filesum",_str,appPathFile);


递归保存m_tree所有节点的操作
void   queryTreeNode(CTreeCtrl& m_tree,HTREEITEM   hTreeItem,UINT& fileSum,int& layer)  
{ 
    CString   strNode,_str,_id,_layer;   
    _str.Format("%d",1+fileSum++);
    strNode   =   m_tree.GetItemText(hTreeItem); 
    _id.Format("%d",m_tree.GetItemData(hTreeItem));
    _layer.Format("%d",layer);
    WritePrivateProfileString("NODE"+_str,"name",strNode,appPathFile);//保存了节点名称
    WritePrivateProfileString("NODE"+_str,"index",_str,appPathFile);//保存了节点序号
    WritePrivateProfileString("NODE"+_str,"id",_id,appPathFile);//保存了节点编号ID
    WritePrivateProfileString("NODE"+_str,"layer",_layer,appPathFile);//保存了节点结构信息(层)
   
//--------------
    //获取子项
    HTREEITEM hFirstChild   =  m_tree. GetChildItem(hTreeItem); 
    //如果子项不为空
    if(hFirstChild!=NULL)  
    {
        layer++;
        queryTreeNode(m_tree,hFirstChild,fileSum,layer);
        layer--;
    }
//--------------
    //获取兄项
    hFirstChild   =  m_tree. GetNextItem(hTreeItem,TVGN_NEXT); 
    //如果兄项不为空
    if(hFirstChild!=NULL)  
        queryTreeNode(m_tree,hFirstChild,fileSum,layer); 
}

最存好了结构就像下面这样的 相信能看懂

[NODE1]
name=节点序号:1|节点编号ID:1
index=1
id=1
layer=1
[NODE2]
name=节点序号:2|节点编号ID:2
index=2
id=2
layer=2
[NODE3]
name=节点序号:6|节点编号ID:6
index=3
id=6
layer=3
[NODE4]
name=节点序号:7|节点编号ID:7
index=4
id=7
layer=4
[NODE5]
name=节点序号:3|节点编号ID:3
index=5
id=3
layer=2
[NODE6]
name=节点序号:5|节点编号ID:5
index=6
id=5
layer=3
[NODE7]
name=节点序号:8|节点编号ID:8
index=7
id=8
layer=3
[NODE8]
name=节点序号:4|节点编号ID:4
index=8
id=4
layer=2
[NODE9]
name=最后一个9编号
index=9
id=9
layer=2
[INFO]
filesum=9

读取:

 char buff[255];
    CString   _layer,strNode,_str;   
    int currLayer=0;//当前层
    int _id=0;
    HTREEITEM m_htreeItem=TVI_ROOT;

//------------------
int _fileSum=GetPrivateProfileInt("INFO","filesum",NULL,appPathFile);

    for (int i=1;i<=_fileSum;i++)
    {
        _str.Format("%d",i);
        //取得节点信息
        _layer.Format("%d",i);
        GetPrivateProfileString("NODE"+_str,"layer",NULL,buff,256,appPathFile);       
        _layer=atoi(buff);
        GetPrivateProfileString("NODE"+_str,"name",NULL,buff,256,appPathFile);
        strNode=buff;
        //GetPrivateProfileString("NODE"+_str,"index",NULL,buff,256,appPathFile);
        GetPrivateProfileString("NODE"+_str,"id",NULL,buff,256,appPathFile);
        _id=atoi(buff);


        //如果在下层
        if (_layer>currLayer)
        {

            m_htreeItem = m_tree.InsertItem(strNode,0,1,m_htreeItem,TVI_LAST);
            m_tree.SetItemData(m_htreeItem,_id);//设置附加数据也就是编号

            currLayer++;
            //如果在同层
        }else if (_layer==currLayer)
        {

            m_htreeItem = m_tree.GetParentItem(m_htreeItem);       
            m_htreeItem = m_tree.InsertItem(strNode,0,1,m_htreeItem,TVI_LAST);
            m_tree.SetItemData(m_htreeItem,_id);//设置附加数据也就是编号

            //如果在上层
        }else if (_layer<currLayer)
        {
            //查找上层节点
            while(_layer!=currLayer)
            {
                m_htreeItem = m_tree.GetParentItem(m_htreeItem);
                currLayer--;
            }
            m_htreeItem =m_tree.GetParentItem(m_htreeItem);
            m_htreeItem=m_tree.InsertItem(strNode,0,1,m_htreeItem,TVI_LAST);
            m_tree.SetItemData(m_htreeItem,_id);//设置附加数据也就是编号
        }
    }
  

读取主要是依靠layer来实现的 先读取节点总数 然后进入循环 循环所有节点
1.从文件中读取第i个记录 然后判断这个记录的层是否大于当前的层(第一层总是0)所以一定大于 更新当前节点对象为新增对象
2.如果等于的话 则在当前节点对象的父节点对象上新增节点 这样 就是与当前节点为同一级了 再更新当前节点对象为新增对象
3.如果大于的话 则需要寻找到与之对应的层节点对象上 这里使用循环判断 然后再重覆2步骤.
4.完成一次判断 就返回再从1开始执行


最后还是要说下 下面这些信息 是我认为必要保存的 这才是一个能完成基本功能的结构
name--名称
index--序号(可以没有)
id--数据索引支持
layer--读取结构信息

另一个相似的例子:

//保存一个树控件m_tree_map_list结构信息到"D:/x.ini"文件中.
//首先在窗体上创建一个树控件 并创建一个成员变量为m_tree_map_list 然后在一个"保存"按钮中添加以下代码
//天枫十一郎
////////////////////////////////////////////////////////////////////////////

WritePrivateProfileString("tree",NULL,NULL,"D://x.ini");//清除原有信息文件

UINT sum=0;//项目总数
int parent=0;//父项标识
CString itemp,temp_01,temp_02;//临时变量
//--------------------------------------------------
HTREEITEM m_htreeChildItem;//下一个子项
HTREEITEM m_htreeSiblingItem;//下一个兄项
HTREEITEM m_htreeParentItem;//下一个父项
HTREEITEM m_htreeBS;//当前操作的项
m_htreeBS= m_tree_map_list.GetRootItem();//取得主项的信息

 //判断当前子项是否有效
while(m_htreeBS)
{ 
 sum++; //项目总数增加
 parent++;//父项标识改增加
 itemp.Format("%d",sum);
 temp_01.Format("%d",parent);//保存父项标识
 WritePrivateProfileString("tree","id_parent_"+itemp,temp_01,"D://x.ini");//写入当前项的父项标识(根据此信息来读取)
 
 m_tree_map_list.Expand(m_htreeBS,TVE_EXPAND);//展开此项
 m_htreeChildItem=m_tree_map_list.GetChildItem(m_htreeBS);//取得当前项的子项
 

 //判断如果没有子项了 则进行兄项查询
 while(!m_htreeChildItem)
 {
  
  m_htreeSiblingItem=m_tree_map_list.GetNextSiblingItem(m_htreeBS);//取得当前操作项的下一个兄项

  //判断是否有兄项
  if (m_htreeSiblingItem)
  {
   parent--;//父项标识减少
   m_htreeChildItem=m_htreeSiblingItem;
  }else{
   //没有兄项则进行父项查询
   m_htreeParentItem=m_tree_map_list.GetParentItem(m_htreeBS);
   if (m_htreeParentItem)
   {
    m_htreeBS=m_htreeParentItem;//改变当前操作的项
    parent--;

   }else
//没有父项则退出
    break;
  }
  //--------

 } 
 m_htreeBS=m_htreeChildItem;//改变当前操作项
 
}

//保存项目总数
 itemp.Format("%d",sum);
 WritePrivateProfileString("tree","sum",itemp,"D://x.ini");



//然后再在一个"读取"按钮里添加以下代码
////////////////////////////////////////////////////////////////////////////

 m_tree_map_list.DeleteAllItems();//清除所有项目
 
  UINT sum=0;//项目总数
 int parent=0;//父项标识
 int bs=0;
 char itemp[255];
 CString temp_01,temp_02;
 //-----------------
 HTREEITEM m_hRoot;//主项
 HTREEITEM m_htreeBS;//当前操作的项
 HTREEITEM m_htreeChildItem;//下一个子项
 HTREEITEM m_htreeSiblingItem;//下一个兄项
 HTREEITEM m_htreeParentItem;//下一个父项
 m_htreeParentItem=TVI_ROOT;//取得主项
 //--------------------------------------------------
 
 //读取总数
 GetPrivateProfileString("tre","sum","",itemp,256,"D://x.ini");
 sum=atoi(itemp);
 
 //循环所有项目 根据父项的标识来确定位置
 for (int i=1;i<=sum;i++)
 {
//取得当前项的父项标识
  temp_01.Format("%d",i);
  GetPrivateProfileString("tree","id_parent_"+temp_01,"",itemp,256,"D://x.ini");
  bs=atoi(itemp);
  
  //如果在下一节点上
  if (bs>parent)
  {
   
   m_htreeBS = m_tree_map_list.InsertItem("tree",0,1,m_htreeParentItem,TVI_LAST);
   m_htreeParentItem=m_htreeBS;
   parent++;
   
   //如果在此节点上
  }else if (bs==parent)
  {
   m_htreeSiblingItem = m_tree_map_list.GetParentItem(m_htreeParentItem);  
   m_htreeBS = m_tree_map_list.InsertItem("tree",0,1,m_htreeSiblingItem,TVI_LAST);
   m_htreeParentItem=m_htreeBS;
   
   //如果在上一节点上
  }else if(bs<parent)
  {
   //循环至当前项的父项标识为止
   while(bs!=parent)
   {
    m_htreeBS = m_tree_map_list.GetParentItem(m_htreeBS);
    parent--;
   }
   
   m_htreeBS = m_tree_map_list.GetParentItem(m_htreeBS);
   
   m_htreeBS = m_tree_map_list.InsertItem("tree",0,1,m_htreeBS,TVI_LAST);
   m_htreeParentItem=m_htreeBS;
   
  }
  
 
 }

猜你喜欢

转载自blog.csdn.net/mikasoi/article/details/81637680