MFC CTreeCtrl 树结构在SQLite中的存储与重建
在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;
}