首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

樹狀控件實現資料庫分層瀏覽:DAO 連接 Access 與 CTreeCtrl 完整整合指南

     編輯:關於C語言

將資料庫的階層式資料(如省市鄉鎮、產品分類、組織架構)呈現於樹狀控件(CTreeCtrl),不僅查詢結構清晰,操作更直觀。本文以單一檔案應用程式為基礎,視圖繼承 CFormView,採用 DAO 連接 Access 資料庫,詳細說明從專案建立、資料集定義、樹狀控件綁定到遞迴填充節點的完整流程。

專案建立與基礎設定

  1. 建立 單一檔案應用程式,取消「資料庫支援」,專案名 Ppp,視圖類別 CPppView 繼承 CFormView。
  2. 於對話框編輯器放置 Tree Control(ID:IDC_TREE1),屬性啟用 Has Buttons、Lines at Root、Single Expand。
  3. 用類別精靈為 Tree Control 加入控制變數 m_treectrl(類型 CTreeCtrl)。

DAO 資料庫支援加入

1. Stdafx.h 加入 DAO 標頭

#include <afxdao.h> // 必須在 afxdtctl.h 之前

2. 建立資料集類別 CShengSet

  • 繼承 CDaoRecordset
  • 類別精靈指定資料來源(Access .mdb)與資料表
  • 產生欄位成員變數(如 m_Province、m_City、m_Town)

3. 文件與視圖類別宣告資料集指標/物件

// 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。

效能優化建議

  • 大量節點(>5000)時,改用 TVM_INSERTITEM 批次發送訊息,或分頁載入(展開時才查詢子節點)。
  • 資料集開啟模式選 dbOpenSnapshot(快照),唯讀瀏覽最快。
  • 啟用 m_treectrl.SetRedraw(FALSE/TRUE) 包裹批次插入。

總結

結合 CFormView、CDaoRecordset 與 CTreeCtrl,配合 lParam 儲存主鍵 ID 的映射技巧,即可輕鬆實現資料庫分層資料的樹狀瀏覽與點擊查詢。此架構適用於組織架構、產品分類、地理區劃等所有具父子關係的資料模型。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved