將資料庫的階層式資料(如省市鄉鎮、產品分類、組織架構)呈現於樹狀控件(CTreeCtrl),不僅查詢結構清晰,操作更直觀。本文以單一檔案應用程式為基礎,視圖繼承 CFormView,採用 DAO 連接 Access 資料庫,詳細說明從專案建立、資料集定義、樹狀控件綁定到遞迴填充節點的完整流程。
Ppp,視圖類別 CPppView 繼承 CFormView。IDC_TREE1),屬性啟用 Has Buttons、Lines at Root、Single Expand。m_treectrl(類型 CTreeCtrl)。#include <afxdao.h> // 必須在 afxdtctl.h 之前
CDaoRecordsetm_Province、m_City、m_Town)// PppDoc.h
#include "ShengSet.h"
class CPppDoc : public CDocument {
public:
CShengSet m_ShengSet; // 文件持有資料集實例
};
// PppView.h
#include "ShengSet.h"
class CPppView : public CFormView {
public:
CShengSet *m_ShengSet; // 視圖持有指標,指向文件的實例
};
void CPppView::OnInitialUpdate()
{
CFormView::OnInitialUpdate();
// 1. 取得文件指標,進而取得資料集指標
CPppDoc* pDoc = (CPppDoc*)GetDocument();
m_ShengSet = &pDoc->m_ShengSet;
// 2. 開啟資料集(若文件建構時未開啟)
if (!m_ShengSet->IsOpen())
m_ShengSet->Open();
// 3. 遞迴建立樹狀節點
PopulateTree();
}
假設資料表結構:ID (PK), ParentID (FK), Name, Level,其中 Level=1 為省份、2 為城市、3 為鄉鎮。
void CPppView::PopulateTree()
{
m_treectrl.SetRedraw(FALSE); // 關閉重繪,提升大量節點效能
m_treectrl.DeleteAllItems();
// 先建立所有節點的映射:ID -> HTREEITEM
std::map nodeMap;
m_ShengSet->MoveFirst();
while (!m_ShengSet->IsEOF()) {
long id = m_ShengSet->m_ID;
long parentId = m_ShengSet->m_ParentID;
CString name = m_ShengSet->m_Name;
int level = m_ShengSet->m_Level;
TVINSERTSTRUCT tv = {0};
tv.hParent = (parentId == 0) ? TVI_ROOT : nodeMap[parentId];
tv.hInsertAfter = TVI_LAST;
tv.item.mask = TVIF_TEXT | TVIF_PARAM;
tv.item.pszText = name.GetBuffer();
tv.item.lParam = id; // 存儲 ID 供後續查詢
HTREEITEM hItem = m_treectrl.InsertItem(&tv);
nodeMap[id] = hItem;
name.ReleaseBuffer();
m_ShengSet->MoveNext();
}
// 可選:預設展開第一層
HTREEITEM hRoot = m_treectrl.GetRootItem();
while (hRoot) {
m_treectrl.Expand(hRoot, TVE_EXPAND);
hRoot = m_treectrl.GetNextSiblingItem(hRoot);
}
m_treectrl.SetRedraw(TRUE);
}
void CPppView::OnSelChangedTree(NMHDR* pNMHDR, LRESULT* pResult)
{
NM_TREEVIEW* pNM = (NM_TREEVIEW*)pNMHDR;
HTREEITEM hItem = pNM->itemNew.hItem;
if (!hItem) return;
long id = (long)m_treectrl.GetItemData(hItem); // 取得 lParam 存的 ID
// 以 ID 為條件重新查詢
CString strFilter;
strFilter.Format(_T("ID = %ld"), id);
m_ShengSet->m_strFilter = strFilter;
m_ShengSet->Requery();
if (!m_ShengSet->IsEOF()) {
// 將欄位值顯示於對話框其他控件(如 Edit、Static)
SetDlgItemText(IDC_EDIT_NAME, m_ShengSet->m_Name);
SetDlgItemText(IDC_EDIT_LEVEL, m_ShengSet->m_Level);
// ...
}
*pResult = 0;
}
在 CShengSet::GetDefaultDBName() 中動態取得執行目錄:
CString CShengSet::GetDefaultDBName()
{
TCHAR szPath[MAX_PATH];
GetModuleFileName(AfxGetInstanceHandle(), szPath, MAX_PATH);
PathRemoveFileSpec(szPath); // 移除檔名,保留目錄
PathAppend(szPath, _T("data.mdb")); // 資料庫檔放同目錄
return szPath;
}
如此專案連同 data.mdb 複製至任意電腦即可執行,無需設定 ODBC/DAO 系統 DSN。
TVM_INSERTITEM 批次發送訊息,或分頁載入(展開時才查詢子節點)。dbOpenSnapshot(快照),唯讀瀏覽最快。m_treectrl.SetRedraw(FALSE/TRUE) 包裹批次插入。結合 CFormView、CDaoRecordset 與 CTreeCtrl,配合 lParam 儲存主鍵 ID 的映射技巧,即可輕鬆實現資料庫分層資料的樹狀瀏覽與點擊查詢。此架構適用於組織架構、產品分類、地理區劃等所有具父子關係的資料模型。