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

VC++ 優秀網格控件 CGridCtrl 進階應用:儲存格合併與自訂擴充

     編輯:VC 教學

前言

在需要類 Excel 試算表介面的專案中,Chris Maunder 開發的 CGridCtrl 是 MFC 開發者的首選開源網格控件。其功能涵蓋儲存格編輯、排序、拖放、提示工具、記憶體 DC 雙緩衝繪圖等。本文記錄如何在專案中整合 CGridCtrl,並擴充儲存格合併(Merge/Unmerge)、固定儲存格合併、取得儲存格指標等實用功能,附完整原始碼整合步驟。

一、原始碼檔案清單與專案整合

從官方來源(CodeProject / CodeTools)下載最新版,將下列檔案複製至專案目錄並加入專案:

  • 核心:GridCtrl.cpp/h, CellRange.h
  • 編輯器:InPlaceEdit.cpp/h, InPlaceList.cpp/h
  • 拖放:GridDropTarget.cpp/h
  • 輔助:MemDC.h, TitleTip.cpp/h

在 `stdafx.h` 或對話方塊標頭檔加入:

#include "GridCtrl.h"

二、擴充合併儲存格功能

在 `GridCtrl.h` 的 `CGridCtrl` 類別中新增公有成員:

// 儲存格合併相關
BOOL SetCellCombine(int nRow, int nCol, int nRowSpan, int nColSpan); // 設定合併範圍
BOOL UndoCellCombine(int nRow, int nCol);                            // 取消合併
BOOL SetFixedCellCombine(int nRow, int nCol, int nRowSpan, int nColSpan); // 固定列/欄合併
CGridCellBase* GetCellp(int nRow, int nCol);                         // 取得儲存格指標(非 const)

核心實作邏輯(GridCtrl.cpp 節錄)

BOOL CGridCtrl::SetCellCombine(int nRow, int nCol, int nRowSpan, int nColSpan)
{
    if (nRowSpan <= 1 && nColSpan <= 1) return FALSE;
    CGridCellBase* pCell = GetCellp(nRow, nCol);
    if (!pCell) return FALSE;

    // 標記主儲存格
    pCell->SetState(pCell->GetState() | GVIS_MERGED);
    pCell->SetRowSpan(nRowSpan);
    pCell->SetColSpan(nColSpan);

    // 隱藏被合併的儲存格
    for (int r = nRow; r < nRow + nRowSpan; ++r)
        for (int c = nCol; c < nCol + nColSpan; ++c)
            if (r != nRow || c != nCol)
            {
                CGridCellBase* pSub = GetCellp(r, c);
                if (pSub) pSub->SetState(pSub->GetState() | GVIS_HIDDEN);
            }
    Refresh();
    return TRUE;
}

BOOL CGridCtrl::UndoCellCombine(int nRow, int nCol)
{
    CGridCellBase* pCell = GetCellp(nRow, nCol);
    if (!pCell || !(pCell->GetState() & GVIS_MERGED)) return FALSE;

    int rs = pCell->GetRowSpan(), cs = pCell->GetColSpan();
    pCell->SetRowSpan(1);
    pCell->SetColSpan(1);
    pCell->SetState(pCell->GetState() & ~GVIS_MERGED);

    for (int r = nRow; r < nRow + rs; ++r)
        for (int c = nCol; c < nCol + cs; ++c)
            if (r != nRow || c != nCol)
            {
                CGridCellBase* pSub = GetCellp(r, c);
                if (pSub) pSub->SetState(pSub->GetState() & ~GVIS_HIDDEN);
            }
    Refresh();
    return TRUE;
}

// 取得可修改的儲存格指標(原版 GetCell 回傳 const)
CGridCellBase* CGridCtrl::GetCellp(int nRow, int nCol)
{
    if (nRow < 0 || nRow >= GetRowCount() || nCol < 0 || nCol >= GetColumnCount()) return NULL;
    return m_GridCells[nRow][nCol];
}

在繪圖函數 `OnDraw` 中,依據 `GVIS_MERGED`、`GVIS_HIDDEN`、`GetRowSpan/ColSpan` 計算合併區域矩形,僅繪製主儲存格內容與邊框,略過隱藏儲存格。標註註解 `//added for combine` 方便日後維護。

三、使用範例

void CMyDialog::OnInitGrid()
{
    m_Grid.SetRowCount(10);
    m_Grid.SetColumnCount(6);
    m_Grid.SetFixedRowCount(1);
    m_Grid.SetFixedColumnCount(1);

    // 標題列合併:第 0 行,第 0-2 欄合併為「基本資料」
    m_Grid.SetFixedCellCombine(0, 0, 1, 3);
    m_Grid.SetItemText(0, 0, _T("基本資料"));

    // 資料區合併:第 2 行,第 1-2 欄合併
    m_Grid.SetItemText(2, 1, _T("跨欄內容"));
    m_Grid.SetCellCombine(2, 1, 1, 2);

    // 取消合併範例
    // m_Grid.UndoCellCombine(2, 1);
}

四、執行效果

CGridCtrl 合併儲存格示範

畫面呈現:固定標題列跨欄合併、資料區儲存格跨行跨欄合併、邊框正確繪製、編輯時自動展開合併區域。

五、常見問題與技巧

  • 編輯合併儲存格:進入編輯狀態時,建議暫時 `UndoCellCombine`,編輯完成後再 `SetCellCombine` 還原,避免 InPlaceEdit 定位錯誤。
  • 排序與合併衝突:排序前應展開所有合併,或僅對非合併區域排序。
  • 記憶體洩漏:動態建立儲存格時確保 `DeleteAllItems()` 正確釋放。
  • 高 DPI 支援:搭配 `MemDC` 雙緩衝,需在 `OnDraw` 使用 `GetDeviceCaps(LOGPIXELSX)` 縮放字型與行高。

六、結語

CGridCtrl 功能強大且原始碼開放,適合深度客製化。本文示範的合併儲存格擴充僅需修改核心繪圖與狀態管理,即可滿足類 Excel 報表的複雜排版需求。建議開發者下載最新版本(支援 Unicode、VS2019+),並參考官方範例進一步擴充資料綁定、條件格式、圖表嵌入等進階功能。

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