打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
[原创]CTreeCtrl 树的简单保存和打开 — Windows Live
 http://tczzya.spaces.live.com/blog/cns!914D350A94EF709E!123.entry
7月12日

[原创]CTreeCtrl 树的简单保存和打开

 

关键字:VC++ CTreeCtrl类 储存 ini文件读写 递归调用

在此之前,先引用一段别人的文章,是建立树的:(来自<VC知识库精华珍藏版在线杂志合订本>中《PWIN98/95下演练CTree》的节选)
如果你已经会建立一棵树,请跳过这一段。


通过“FILE->NEW->PROJECTS->MFC AppWizard(EXE)”建立名为VCTREE的工程,在建立过程中选择基于对话框(Dialog based)的应用;将对话

框中的默认控件删除,并将所有对话框属性中的Language域设置为Chinese(P.R.C.),以使应用程序支持中文;建立两个图标IDI_PM和IDI_CJ,

用来表示图标的选中和非选中状态,对于每个图标都应建立32X32和16X16两种大小,以保证程序的需要;在对话框窗口中添加树控制对象

(TREE CONTROL),并设置五个按钮“增加|删除|查看|排序|关闭”,其对应标识分别如下:


--------------------------------------------------------------------------------

控制名称 标题名称 标识符号


--------------------------------------------------------------------------------

树控制 IDC_TREECTRL

按钮 增 加 IDC_ADD

删 除 IDC_DEL

查 看 IDC_VIEW

排 序 IDC_SORT

关 闭 IDOK


--------------------------------------------------------------------------------

5、选中树控制控件,选择“VIEW->ClassWizard->Memory Variables。双击DC_TREECTRL 引入成员变量,其变量类型为:

变量名 种类 变量类型

m_TreeCtrl Control CTreeCtrl

同时利用“MESSAGES MAP”为各命令按钮增加控制功能函数。

6、然后在代码文件VCTREEDlg.CPP中分别加入如下控制代码:

(1)在文件开始处增加图像列表定义

CImageList Cil1,Cil2;//大小图标像列表

(2)在初始化文件开始处增加代码

BOOL CVCTREEDlg::OnInitDialog()

{ CDialog::OnInitDialog();

......//原来其它代码

// TODO: Add extra initialization here

// 此处开始增加代码

CVCTREEApp *pApp=(CVCTREEApp *)AfxGetApp();//创建图象列表

Cil1.Create(16,16,ILC_COLOR,2,2);

Cil1.Add(pApp->LoadIcon(IDI_PM));

Cil1.Add(pApp->LoadIcon(IDI_CJ));

m_TreeCtrl.SetImageList(&Cil1,TVSIL_NORMAL); //设置图象列表

DWORD dwStyles=GetWindowLong(m_TreeCtrl.m_hWnd,GWL_STYLE);//获取树控制原风格

dwStyles|=TVS_EDITLABELS|TVS_HASBUTTONS|TVS_HASLINES|TVS_LINESATROOT;

SetWindowLong(m_TreeCtrl.m_hWnd,GWL_STYLE,dwStyles);//设置风格

char * CJ[4]={"玉溪卷烟厂","云南卷烟厂","沈阳卷烟厂","成都卷烟厂"};//根数据名称

char * PM[4][5]={

{"红梅一","红梅二","红梅三","红梅四","红梅五"},//产品数据项

{"白梅一","白梅二","白梅三","白梅四","白梅五"},

{"绿梅一","绿梅二","绿梅三","绿梅四","绿梅五"},

{"青梅一","青梅二","青梅三","青梅四","青梅五"}};

int i,j;

HTREEITEM hRoot,hCur;//树控制项目句柄

TV_INSERTSTRUCT TCItem;//插入数据项数据结构

TCItem.hParent=TVI_ROOT;//增加根项

TCItem.hInsertAfter=TVI_LAST;//在最后项之后

TCItem.item.mask=TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;//设屏蔽

TCItem.item.pszText="数据选择";

TCItem.item.lParam=0;//序号

TCItem.item.iImage=0;//正常图标

TCItem.item.iSelectedImage=1;//选中时图标

hRoot=m_TreeCtrl.InsertItem(&TCItem);//返回根项句柄

for(i=0;i<4;i++){//增加各厂家

TCItem.hParent=hRoot;

TCItem.item.pszText=CJ[i];

TCItem.item.lParam=(i+1)*10;//子项序号

hCur=m_TreeCtrl.InsertItem(&TCItem);

for(j=0;j<5;j++){//增加各产品

TCItem.hParent=hCur;

TCItem.item.pszText=PM[i][j];

TCItem.item.lParam=(i+1)*10+(j+1);//子项序号

m_TreeCtrl.InsertItem(&TCItem);

}

m_TreeCtrl.Expand(hCur,TVE_EXPAND);//展开树

}

m_TreeCtrl.Expand(hRoot,TVE_EXPAND);//展开上一级树

return TRUE; // return TRUE unless you set the focus to a control

}

(3)增加树项功能的实现

在增加树项功能时,除了需要定义和设置插入树项的数据结构之外,还需要注意的是新增树项的名称初始时均为“新增数据”,增加后允许用

户给数据项设置自定义名称。在编程时应特别注意m_TreeCtrl.EditLabel(hInsert);后面不能跟任何其它程序命令,否则这条编辑指令无效。

void CVCTREEDlg::OnAdd()

{ //增加子项功能函数

HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选择项句柄

if(hSel==NULL) return;//无任何选项则返回

static int nAddNo=100;//编号大于100为新增数据

TV_INSERTSTRUCT TCItem;//定义插入项数据结构

TCItem.hParent=hSel; //设置父项句柄

TCItem.hInsertAfter=TVI_LAST;//在最后增加

TCItem.item.mask=TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;//设屏蔽

TCItem.item.pszText="新增数据";

TCItem.item.lParam=nAddNo++;//索引号增加

TCItem.item.iImage=0;//正常图标

TCItem.item.iSelectedImage=1;//选中时图标

HTREEITEM hInsert=m_TreeCtrl.InsertItem(&TCItem);//增加

m_TreeCtrl.Expand(hSel,TVE_EXPAND);

m_TreeCtrl.EditLabel(hInsert);//修改增加的数据

}

(4)删除树项功能的实现

在实现删除功能时,应对存在子项的树项进行提示,以警告用户是否连同其子项一起删除。

void CVCTREEDlg::OnDel()

{ //删除子项功能函数

HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选项句柄;

if(hSel==NULL) return;//无任何选项则返回

if(m_TreeCtrl.ItemHasChildren(hSel))//判断是否有子项

if(MessageBox("厂家下存在品名,一同删除?","警告",MB_YESNO)==IDNO) return;

m_TreeCtrl.DeleteItem(hSel);

}

(5)排序功能的实现

排序功能是对所选中的树项的所有子项按字符中顺序进行排序,如果想要按照其它规则进行排序,应利用SortChildrenItemBC()函数进行自行

开发排序程序,这个自行开发的函数与列表控制中实现的函数基本相同,可兴趣的读可以试验。

void CVCTREEDlg::OnSort()

{ //排序子项功能函数

HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选项句柄;

if(hSel==NULL) return;//无任何选项则返回

m_TreeCtrl.SortChildren(hSel);

}

(6)查看功能的实现

查看功能用来查看选中树项的有关信息,函数中中显示了树项的文本名称和标识号,可以将这两个信息作为查找关键字,来查看其它更详细的

信息。

void CVCTREEDlg::OnView()

{ //查看选中项功能函数

HTREEITEM hSel=m_TreeCtrl.GetSelectedItem();//取得选项句柄;

if(hSel==NULL) return;//无任何选项则返回

CString cText=m_TreeCtrl.GetItemText(hSel);//取得数据项名

LONG IDs=m_TreeCtrl.GetItemData(hSel);//取得数据项序号

char temp[100];

wsprintf(temp,"厂家:%s 编号:%05d",cText,IDs);

MessageBox(temp,"选择信息");

}

(7)修改功能的实现

如果不进行其它处理,当修改树项的文本名称后,就会发现其未被修改,这是因为程序中没有对修改结果进行保存处理,这就要利用

TV_DISPINFO结构和SetItemText函数对TVN_ENDLABELEDIT进行处理,这样就可以正确地实现修改功能。

void CVCTREEDlg::OnEndlabeleditTree(NMHDR* pNMHDR, LRESULT* pResult)

{ TV_DISPINFO* pTVDispInfo = (TV_DISPINFO*)pNMHDR;

// TODO: Add your control notification handler code here

if(pTVDispInfo->item.pszText==0) return;//用户取消修改操作

m_TreeCtrl.SetItemText(pTVDispInfo->item.hItem,

pTVDispInfo->item.pszText);//设置新数据

*pResult = 0;

}

7、树视的演练技巧

树视的应用技巧在使用树视时,其方法与树控制基本相同,只不过树视是在窗口中来实现的而树控制是在对话框中实现,树视的各种功能是通

过菜单来实现的而树控制是通过按钮等方式来实现的,树控制需要在对话框中创建树控制控件而树视直接占据整个窗口,在设计过程中只要将

按钮和树控制设计过程变为菜单设计,并注意在功能函数是在类向导中是通过菜单命令来操作,同时在每个功能函数前面增加取得列表视引用

的命令(CTreeCtrl& TreeCtrl = GetTreeCtrl()),而其余数据结构和代码均不需要修改,实现起来比较容易。


我是通过这篇文章了解到了树的基本操作的,拜一个先~
之后,我发现我要将建立的树保存到一个文件,之后再在另一个程序里打开,生成这棵数。怎么保存好呢……?这个时候,我找到了一篇把数

据保存到ini文件里的教程,知道了三个函数:(会的跳过)
写入:
BOOL WritePrivteProfileString(
LPCTSTR lpAppName,   //INI文件中的一个字段名
LPCTSTR lpKeyName,   //lpAppName下的一个键名
LPCTSTR lpString,   //键值,LPCTSTR或CString型的
LPCTSTR lpFileName   //完整的INI文件名
);
读取,前者为读字符串,后者为读整型:
DWORD GetPrivateProfileString(
LPCTSTR lpAppName,   //INI文件中的一个字段名
LPCTSTR lpKeyName,   //lpAppName下的一个键名
LPCTSTR lpDefault,   //如果没有前两个参数指定的字段名或者键名,则用此值赋给变量
LPCTSTR lpReturnedString,  //接受INI文件重点的值的CString对象,即目的缓冲器
DWORD nSize,    //目的缓冲器大小
LPCTSTR lpFileName   //完整的INI文件名
);

UINT GetPrivateProfileInt(
LPCTSTR lpAppName,   //INI文件中的一个字段名
LPCTSTR lpKeyName,   //lpAppName下的一个键名
INT nDefault,    //如果没有前两个参数指定的字段名或者键名,则用此值赋给变量
LPCTSTR lpFileName   //完整的INI文件名
);

这三个函数在这里要比fstream方便的多了(因为本真人还不会vc++里那个读写文件的函数……)
下面让我们想想,树,应该怎么保存?一棵树是由一个根节点,一堆支干节点和一堆叶子节点组成的。而构造一个树的节点,就要给他:
一个lParam的序号,一个pszText的名字,一个父节点的指针。这些是我们构造一个节点的充分必要条件,而只要我们把对应的所有节点构造出

来,那么我们就相当于构造出了一棵一样的树。下面,我来说说我的想法:
递归遍历树的每一个节点,顺序保存他的pszText和他所在的层数(根算0层,第一层枝算一层,类推)。
打开时,动态产生lParam的序号给每个节点,通过每个节点所在层数来确定其父节点是谁。
好象很简单的样子,一句话就完了,事实上——确实很简单:下面是代码。

写:
void CRaWtreeDlg::OnWrite()      //按下保存按钮
{

 m_tn=1;       //节点总数,包括根节点——我是从1开始记的
 CString ct;
 ::WritePrivateProfileString("Count","Ceng","0",".\\root.ini");//当前层数,0
 HTREEITEM hRoot;
 hRoot=m_tw.GetChildItem(TVI_ROOT);   //获得根节点
 LB(hRoot,0);      //递归调用,最开始把根节点放入,并指定当前层数为0

      
 ct.Format("%d",m_tn-1);     //记录完毕,记录总节点数
 ::WritePrivateProfileString("Count","Count",ct,".\\root.ini");

}


void CRaWtreeDlg::LB(HTREEITEM hroot,int Ceng)    //遍历
{
 HTREEITEM hRoot,hCur;
 CString ct;
 CString st;
 adddat *addp;
 hRoot=hroot;
 if (m_tw.ItemHasChildren(hRoot))    //如果有子节点,即不是叶子
 { 
  ct.Format("%d",Ceng);
  st.Format("Ceng[%d]",m_tn);
  ::WritePrivateProfileString("Root",st,ct,".\\root.ini");  //保存当前层数
  ct=m_tw.GetItemText(hRoot);
  st.Format("Name[%d]",m_tn);
  ::WritePrivateProfileString("Root",st,ct,".\\root.ini");  //保存当前名字


  m_tn++;

  //记录最大层数
  int nc=::GetPrivateProfileInt("Count","Ceng",0,".\\root.ini");

  if (nc<Ceng)
  {
   st.Format("%d",Ceng);
   ::WritePrivateProfileString("Count","Ceng",st,".\\root.ini");
  }

 

  
  hCur=m_tw.GetChildItem(hRoot);     //获得子节点
  while (hCur!=NULL)      //如果子节点不空
  {
   LB(hCur,Ceng+1);     //递归调用,层数+1
   hCur=m_tw.GetNextItem(hCur,TVGN_NEXT);   //获得下一个子节点
  }
  
 }
 else         //下面代码含义同上    
 { 
  ct.Format("%d",Ceng);
  st.Format("Ceng[%d]",m_tn);
  ::WritePrivateProfileString("Root",st,ct,".\\root.ini");
  ct=m_tw.GetItemText(hRoot);
  st.Format("Name[%d]",m_tn);
  ::WritePrivateProfileString("Root",st,ct,".\\root.ini");
  
  addp=m_cul.GetItem(m_tw.GetItemData(hRoot));
  ct=addp->m_urladd;
  st.Format("Address[%d]",m_tn);
  ::WritePrivateProfileString("Root",st,ct,".\\root.ini");

 

  m_tn++;

  int nc=::GetPrivateProfileInt("Count","Ceng",0,".\\root.ini");
  
  if (nc<Ceng)
  {
   st.Format("%d",Ceng);
   ::WritePrivateProfileString("Count","Ceng",st,".\\root.ini");
  }
 }
}

本问来自tczzya.spaces.msn.com

读:

void CRaWtreeDlg::OnRead()
{
 int iCount;
 int nid=10;
 char schar[50];
 iCount=::GetPrivateProfileInt("Count","Count",0,".\\root.ini");  //如果打开错误
 if (iCount==0)
 {
  MessageBox("未找到配置文件");
  return;
 }

 HTREEITEM hRoot;
 
 //读入根节点并储存
 TV_INSERTSTRUCT TCItem;

 TCItem.hParent=TVI_ROOT;
 TCItem.hInsertAfter=TVI_LAST;
 TCItem.item.mask=TVIF_TEXT|TVIF_PARAM|TVIF_IMAGE|TVIF_SELECTEDIMAGE;
 ::GetPrivateProfileString("Root","Name[1]",NULL,schar,50,".\\root.ini");
 TCItem.item.pszText=schar;
 TCItem.item.lParam=nid;       //序号
 TCItem.item.iImage=1;
 TCItem.item.iSelectedImage=0;
 hRoot=m_tr.InsertItem(&TCItem);

 nid++;

 //获得最大层数
 int nc=::GetPrivateProfileInt("Count","Ceng",0,".\\root.ini");
 HTREEITEM *hR;
 hR=new HTREEITEM[nc];  //创建nc个节点的指针,用来指向每层当前的父节点
 hR[0]=hRoot;   //第0层为根节点
 
 int ceng;   //读入层数
 CString st;

 for(int i = 2; i <= iCount; i++)//读入剩下的iCount-1个节点
 {
  st.Format("Ceng[%d]",i);
  ceng=::GetPrivateProfileInt("Root",st,0,".\\root.ini");
  st.Format("Name[%d]",i);
  ::GetPrivateProfileString("Root",st,NULL,schar,50,".\\root.ini");
  TCItem.item.pszText=schar;
  TCItem.item.lParam=nid++;

  TCItem.hParent=hR[ceng-1]; //当前读入节点的父节点为父节点层的当前节点
  hR[ceng]=m_tr.InsertItem(&TCItem);//读入节点层的当前节点为读入节点
 }
}


不是很详细,有不明白的地方就说吧~


欢迎转载,请标明出处,谢谢

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
注册表知识大全(基础篇)
MFC树控件应用实例
wx.TreeCtrl
华为GuassDB 200轻量化单节点模式部署指南
云计算管理平台之OpenStack网络服务neutron
: 在Tree控件中添加了CheckBox,怎么在初始化时将所有的项都SetCheck(true)
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服