当前位置:首页 > 技术 > 正文内容

MFC CTreeCtrl 树结构在SQLite中的存储与重建

访客 技术 2026年5月22日 3

在MFC开发中,CTreeCtrl控件用于展示树形结构数据。本文探讨如何将CTreeCtrl的树形结构存储到SQLite数据库,并在需要时重新构建树形结构。

树节点的位置通过父节点(hParent)和插入顺序(hInsertAfter)确定。这种设计使得节点的增删改操作仅需更新少量数据即可实现。为了便于存储和恢复,我们使用节点的层级(lev)和遍历序号(idno),配合唯一标识符(id)来记录树结构。

节点的id是自动增长的整数,在数据库中确保唯一性。保存树结构时,计算每个节点的lev和idno值并存入数据库;恢复树结构时,利用这些值重新构造树形结构。

关键代码实现

头文件定义

#pragma once

#include "ViewTree.h"
#include "CSQLite.h"

class CFileViewToolBar : public CMFCToolBar
{
    virtual void OnUpdateCmdUI(CFrameWnd* /*pTarget*/, BOOL bDisableIfNoHndler)
    {
        CMFCToolBar::OnUpdateCmdUI((CFrameWnd*) GetOwner(), bDisableIfNoHndler);
    }

    virtual BOOL AllowShowOnList() const { return FALSE; }
};

class CFileView : public CDockablePane
{
public:
    CFileView();
    ~CFileView();

    void AdjustLayout();
    void OnChangeVisualStyle();

    CSQLite m_SQLite;
    HTREEITEM m_hNodeSrc;
    HTREEITEM m_hNodeFile;

    struct FileTable
    {
        int id;
        int lev;
        int idno;
        char name[256];
        HTREEITEM hItem;
    };

    FileTable* m_pFileTable;

    int m_NodeIndex;
    int m_NodeLevel;

    void TraverseAndSave(HTREEITEM hItem, CString& sql);
    void TraverseAndUpdate(HTREEITEM hItem, CString& sql);
    void RestoreTreeFromDB();
    void InsertTreeNode(HTREEITEM hParent, FileTable& data);

protected:
    CViewTree m_wndFileView;
    CImageList m_FileViewImages;
    CFileViewToolBar m_wndToolBar;

    void FillFileView();

BEGIN_MESSAGE_MAP(CFileView, CDockablePane)
    ON_WM_CREATE()
    ON_WM_SIZE()
    ON_WM_CONTEXTMENU()
    ON_COMMAND(ID_POP_EXP_SAVE, &CFileView::OnPopExpSave)
    ON_COMMAND(ID_POP_EXP_UPDATE, &CFileView::OnPopExpUpdate)
END_MESSAGE_MAP()

public:
    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
    afx_msg void OnSize(UINT nType, int cx, int cy);
    afx_msg void OnContextMenu(CWnd* pWnd, CPoint point);
};

CPP文件实现

#include "stdafx.h"
#include "mainfrm.h"
#include "FileView.h"

CFileView::CFileView()
{
    m_pFileTable = nullptr;
}

CFileView::~CFileView()
{
    if (m_pFileTable != nullptr)
    {
        delete[] m_pFileTable;
        m_pFileTable = nullptr;
    }
}

void CFileView::TraverseAndSave(HTREEITEM hItem, CString& sql)
{
    if (!hItem) return;

    FileTable* pData = (FileTable*)m_wndFileView.GetItemData(hItem);
    CString itemText = m_wndFileView.GetItemText(hItem);

    CString insertSql;
    insertSql.Format(_T("INSERT INTO tree_data(id, level, index, name) VALUES(%d, %d, %d, '%s');"),
                     pData->id, m_NodeLevel, m_NodeIndex++, itemText);

    sql += insertSql;

    HTREEITEM hChild = m_wndFileView.GetChildItem(hItem);
    if (hChild)
    {
        m_NodeLevel++;
        TraverseAndSave(hChild, sql);
        m_NodeLevel--;
    }

    HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem);
    if (hSibling)
    {
        TraverseAndSave(hSibling, sql);
    }
}

void CFileView::TraverseAndUpdate(HTREEITEM hItem, CString& sql)
{
    if (!hItem) return;

    FileTable* pData = (FileTable*)m_wndFileView.GetItemData(hItem);
    CString itemText = m_wndFileView.GetItemText(hItem);

    CString updateSql;
    updateSql.Format(_T("UPDATE tree_data SET level=%d, index=%d, name='%s' WHERE id=%d;"),
                     m_NodeLevel, m_NodeIndex++, itemText, pData->id);

    sql += updateSql;

    HTREEITEM hChild = m_wndFileView.GetChildItem(hItem);
    if (hChild)
    {
        m_NodeLevel++;
        TraverseAndUpdate(hChild, sql);
        m_NodeLevel--;
    }

    HTREEITEM hSibling = m_wndFileView.GetNextSiblingItem(hItem);
    if (hSibling)
    {
        TraverseAndUpdate(hSibling, sql);
    }
}

void CFileView::RestoreTreeFromDB()
{
    CString query = _T("SELECT id, level, index, name FROM tree_data ORDER BY index;");
    int rows, cols;
    if (!m_SQLite.Query(query, rows, cols)) return;

    m_pFileTable = new FileTable[rows];

    for (int i = 0; i < rows; ++i)
    {
        m_pFileTable[i].id = atoi(m_SQLite.m_sresult[i * cols + 0]);
        m_pFileTable[i].lev = atoi(m_SQLite.m_sresult[i * cols + 1]);
        m_pFileTable[i].idno = atoi(m_SQLite.m_sresult[i * cols + 2]);
        strcpy_s(m_pFileTable[i].name, m_SQLite.m_sresult[i * cols + 3]);
    }

    HTREEITEM hRoot = nullptr;
    for (int i = 0; i < rows; ++i)
    {
        FileTable& data = m_pFileTable[i];
        HTREEITEM hParent = (data.lev == 0) ? TVI_ROOT : m_pFileTable[data.lev - 1].hItem;
        HTREEITEM newItem = m_wndFileView.InsertItem(data.name, hParent, TVI_LAST);
        m_wndFileView.SetItemData(newItem, (DWORD_PTR)&data);
        data.hItem = newItem;

        if (data.lev == 0) hRoot = newItem;
    }

    m_wndFileView.Expand(hRoot, TVE_EXPAND);
}

void CFileView::InsertTreeNode(HTREEITEM hParent, FileTable& data)
{
    HTREEITEM newItem = m_wndFileView.InsertItem(data.name, hParent, TVI_LAST);
    m_wndFileView.SetItemData(newItem, (DWORD_PTR)&data);
    data.hItem = newItem;
}
标签: MFCSQLite

相关文章

Linux crontab 详解

1) crontab 是什么cron 是 Linux 的定时任务守护进程;crontab 是用来编辑/查看“按时间周期执行命令”的表(cron table)。常见两类:用户 crontab:每个用户一份(crontab -e 编辑)系统级 crontab / cron.d:可指定执行用户(/etc/crontab、/etc/cron.d/*)2) crontab 时间...

富文本里可以允许的 HTML 属性

一、所有标签默认允许的安全属性(极少)class        (可选)id           (通常建议禁用)title️ 注意:id 容易被滥用做锚点注入,很多系统直接禁用class 允许的话最好只允许固定前缀(如 editor-*)二、a 标签允许属性<a href="" t...

Mac 安装 Node.js 指南

方法一:通过官网安装包(最简单,适合初学者)如果你只是想快速安装并开始使用,这是最直接的方法。访问 Node.js 官网。页面会显示两个版本:LTS (Recommended For Most Users):长期支持版,最稳定。建议选这个。Current:最新特性版,包含最新功能但可能不够稳定。下载 .pkg 安装包并运行。按照安装向导点击“下一步”即可完成。方法二:使用 Homebrew 安装(...

Dom\HTML_NO_DEFAULT_NS 的副作用:自动加闭合标签

在使用Dom\HTMLDocument时,Dom\HTML_NO_DEFAULT_NS 将禁止在解析过程中设置元素的命名空间, 此设置是为了与DOMDocument向后兼容而存在的。当使用它时,已知的一个副作用就是:自动加闭合标签例如 </img> 为什么会这样?当你使用:Dom\HTML_NO_DEFAULT_NS文档会变成 无命名空间模式,此时内部更接近 XML...

Laravel 事件和监听器创建

在 Laravel 中,使用 Artisan 命令创建 Events(事件) 和 Listeners(监听器) 是非常高效的。你可以通过以下几种方式来实现:1. 手动创建单个 Event如果你只想创建一个事件类,可以使用 make:event 命令:Bashphp artisan make:event UserRegistered执行后,文件将生成在 app/Even...

自定义域名解析神器 dnsmasq

什么是 dnsmasq?dnsmasq 是一个轻量级、功能强大的网络服务工具,专为小型和中等规模网络设计。它是一个综合的网络基础设施解决方案[1]。dnsmasq 能做什么?功能说明应用场景DNS 转发与缓存将 DNS 查询转发到上游服务器(ISP、Google DNS 等),并在本地缓存结果加快 DNS 查询速度,减少外部 DNS 流量本地 DNS解析本地网络设备的主机名,无需编辑&n...

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。