TreeCtrl 树的存储和还原 2, VC++
TreeCrtl树的节点可以随意的添加,删除,移动。树保存到数据库中,并能从数据库读出还原。
树节点的LPARAM存放ID,这个ID是数据表自增长主键。
1.数据库中读出,按PID,SID顺序,这样读出的第一个为root
2.读出的数据存入list
3.创建root节点,把root放入map,从list中删除
4.遍历list,pid,sid 在map中找到,说明符合创建条件,创建后,把节点从list中移进map
5.继续遍历list,直到list中没有元素。
树已经建好,清空map,list。
好处:
1.树结存储的结构数据是 id,pid,sid,和树结构的逻辑关系完全一致
2.树结构改动,只需更新数据库中相邻节点的结构数据,更新量少
3.树中不需要保存额外的结构数据。
缺点:
重建时,从数据库中读出的节点数据顺序是随机的。无法按记录顺序创建树。
需要循环遍历list找到可以创建的节点,顺序创建。
.
总结:
比之前的利用层数据和遍历序号存储还原要好,树结构越大性价比越高,增加内存操作减少磁盘IO。
树的存储方式:
树的还原:
树节点对应的内容是foreign ID 关联的另一张表的Blob字段。
主要的树的还原程序段:
1。
HTREEITEM CPNotesTree::AddNode(LPARAM lParam, int image, int imageselected, wchar_t* szText, HTREEITEM hParent, HTREEITEM hAfter) { TVINSERTSTRUCT tvins; TVITEM tvi; tvins.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; // tvins.item.pszText = szText; tvins.item.iImage = image; //初始图标都是文件 tvins.item.iSelectedImage = imageselected; tvins.item.lParam = lParam; tvins.hParent = hParent; tvins.hInsertAfter = hAfter; return (HTREEITEM)SendMessage(m_hWndTree, TVM_INSERTITEM, 0, (LPARAM)(LPTVINSERTSTRUCT)&tvins); } void CPNotesTree::ShowNotesTree() { int nrow, ncolum; int i, j; char* p = NULL; int p_len =0; int len = 0; DeleteTreetView(); //非动态修改的用const不占用堆栈, char* sql = "select id,pid,sid,name from notestree order by pid, sid"; //order 保证第一个是root m_pSQLite = &m_SQLite; m_pSQLite->Query(sql, nrow, ncolum); HTREEITEM hParent = NULL; HTREEITEM hAfter = NULL; //Sibling HTREEITEM hCurrent = NULL; //HTREEITEM hRoot = m_wndFileView.GetSelectedItem(); //插入节点 //HTREEITEM hRoot = m_hNotes; HTREEITEM hRoot = NULL; struct ST_TN { UINT32 type; UINT32 id; UINT32 pid; UINT32 sid; wchar_t wname[256]; //数据库中的UTF8转化层UTF16 }tn; list<ST_TN> ltn; //list 中是没有插入的节点 list<ST_TN>::iterator it_list; list<ST_TN>::iterator it_list2; //副本用来删除时保证右移 map<int, HTREEITEM> DataMap; //map 中是已经插入的节点 map<int, HTREEITEM>::iterator it_map_pid; //迭代器,用于查找 map<int, HTREEITEM>::iterator it_map_sid; ltn.clear(); for (i = 1; i < nrow + 1; i++) //第一行是字段名称 { tn.id = atoi(m_pSQLite->m_sresult[i * ncolum + 0]); tn.pid = atoi(m_pSQLite->m_sresult[i * ncolum + 1]); tn.sid = atoi(m_pSQLite->m_sresult[i * ncolum + 2]); p = m_pSQLite->m_sresult[i * ncolum + 3]; p_len = strlen(p); len = MultiByteToWideChar(CP_UTF8, 0,p,p_len,NULL, 0); //用UTF8 保存在数据库中 if (len > 255) len = 255; //别溢出了 MultiByteToWideChar(CP_UTF8, 0, p, p_len, tn.wname, len); tn.wname[len] = '\0'; ltn.push_back(tn); //第一个是root } //root hParent = hRoot; hAfter = hRoot;
//it_list = ltn.begin();
for (it_list = ltn.begin(); it_list != ltn.end(); it_list++) //找根节点可以避免顺序不对的问题
{
if (it_list->pid == 0) break;
}
hCurrent = AddNode((LPARAM)it_list->id, 0, 0, it_list->wname, hParent, hAfter); DataMap.insert(pair<int, HTREEITEM>(it_list->id, hCurrent)); //插入后到的句柄和id成对保存到map ltn.erase(it_list); while (ltn.size()) //直到List中的节点全部移走 { it_list = ltn.begin(); while (it_list!= ltn.end()) //遍历List { it_map_pid = DataMap.find(it_list->pid); hParent = (it_map_pid == DataMap.end())?NULL: it_map_pid->second; if (it_list->sid == 0) { hAfter = TVI_FIRST; } else { it_map_sid = DataMap.find(it_list->sid); hAfter = (it_map_sid == DataMap.end()) ? NULL : it_map_sid->second; } if ((hParent != NULL) && (hAfter != NULL)) { hCurrent = AddNode((LPARAM)it_list->id, 0, 0, it_list->wname, hParent, hAfter); //type4 notes 数据结构 //处理节点数据:可以用list扩展,id换成pos作为索引 DataMap.insert(pair<int, HTREEITEM>(it_list->id, hCurrent)); //插入后到的句柄和id成对保存到map it_list2 = it_list; //list 删除用迭代器位置的元素后,当前迭代器不能继续遍历,要用副本进行删除。 it_list++; ltn.erase(it_list2); } else { it_list++; } } } ltn.clear(); DataMap.clear(); //树构造完后清除map,若保留则树改变时也要维护map ExpandAll(TreeView_GetRoot(m_hWndTree)); }