TreeCtrl 树的存储和还原 2, VC++

发布时间 2023-07-31 14:01:16作者: XGZ21

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();

    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));
}