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

VC++ 使用 Windows API 直接打印 ListCtrl 報表完整方案

     編輯:關於VC++

應用場景與痛點

MFC 文檔/視圖架構提供完善列印支援,但對話框應用或 CFormView 中的 CListCtrl(報表模式)列印卻相對繁瑣。本文提供一個即拿即用的 PrintListCtrl() 函數,支援自動欄寬計算、多頁分頁、列印對話框選項,適合嵌入各類專案。

核心功能特性

  • 自動讀取 CListCtrl 欄標題與資料
  • 依螢幕字型度量計算列印欄寬,均勻分佈
  • 依頁高自動分頁,支援任意筆數
  • 使用標準 PrintDlg 讓用戶選擇印表機、紙張、方向
  • 純 Win32 API 實現,無 MFC 列印架構依賴

關鍵資料結構

typedef struct tagColAtt {
    int nColIndex;       // 原始欄索引
    CString strColText;  // 欄標題文字
    int nPrintX;         // 列印 X 座標
    int nSubItemIndex;   // 子項索引
} COLATT;

核心實現流程

  1. 初始化列印環境:PRINTDLG 取得 hDC,GetDeviceCaps 取得可列印區域解析度
  2. 收集欄資訊:透過 GetHeaderCtrl() 與 GetColumnOrderArray() 取得顯示順序與標題文字,計算初步欄寬
  3. 檢查溢位:若總寬度超過頁寬,提示用戶改用橫向或較大紙張
  4. 啟動列印作業:StartDoc/StartPage 開啟列印任務
  5. 調整欄位分佈:將剩餘空間平均分配給各欄間距
  6. 輸出標題列:TextOut 繪製欄標題
  7. 逐行輸出資料:雙層迴圈遍歷列/行,達到每頁最大行數時 EndPage/StartPage 換頁
  8. 結束列印:EndDoc、DeleteDC 釋放資源

完整函數代碼

BOOL PrintListCtrl(CListCtrl &list) {
    PRINTDLG pd = { sizeof(PRINTDLG) };
    pd.Flags = PD_RETURNDC;
    if (!PrintDlg(&pd)) return FALSE;
    ASSERT(pd.hDC != NULL);

    int nHorRes = GetDeviceCaps(pd.hDC, HORZRES);
    int nVerRes = GetDeviceCaps(pd.hDC, VERTRES);
    TEXTMETRIC tm; GetTextMetrics(pd.hDC, &tm);
    int nCharHeight = tm.tmHeight, nCharWidth = tm.tmAveCharWidth;

    CHeaderCtrl* pHeader = list.GetHeaderCtrl();
    int nColCount = pHeader->GetItemCount();
    int nLineCount = list.GetItemCount();
    int ColOrderArray[100];
    COLATT ca[100];
    list.GetColumnOrderArray(ColOrderArray, nColCount);

    // 1. 讀取欄標題與初步定位
    int nColX = 2 * nCharWidth; // 左邊距
    for (int i = 0; i < nColCount; i++) {
        ca[i].nColIndex = ColOrderArray[i];
        LVCOLUMN lvc = { LVCF_TEXT | LVCF_SUBITEM };
        char text[100]; lvc.pszText = text; lvc.cchTextMax = 100;
        list.GetColumn(ca[i].nColIndex, &lvc);
        ca[i].strColText = lvc.pszText;
        ca[i].nSubItemIndex = lvc.iSubItem;
        ca[i].nPrintX = nColX;
        nColX += nCharWidth * strlen(ca[i].strColText);
        if (nColX > nHorRes) {
            DeleteDC(pd.hDC);
            AfxMessageBox(_T("欄位過多無法單行列印,請嘗試橫向或較大紙張。"));
            return FALSE;
        }
    }

    // 2. 開始列印文檔
    DOCINFO di = { sizeof(DOCINFO), _T("ListCtrl Data Printing") };
    StartDoc(pd.hDC, &di);
    StartPage(pd.hDC);

    // 3. 均勻分佈剩餘空間
    int space = (nHorRes - 2 * nCharWidth - nColX) / (nColCount - 1);
    for (int i = 1; i < nColCount; i++) ca[i].nPrintX += i * space;

    // 4. 列印標題
    for (int i = 0; i < nColCount; i++)
        TextOut(pd.hDC, ca[i].nPrintX, 2, ca[i].strColText, ca[i].strColText.GetLength());

    // 5. 列印資料分頁
    int nMaxLinePerPage = nVerRes / nCharHeight - 3;
    int nCurPage = 1;
    for (int i = 0; i < nLineCount; i++) {
        for (int j = 0; j < nColCount; j++) {
            int lineOnPage = i + 1 - (nCurPage - 1) * nMaxLinePerPage;
            if (lineOnPage > nMaxLinePerPage) {
                EndPage(pd.hDC);
                StartPage(pd.hDC);
                nCurPage++;
                lineOnPage = 1;
            }
            CString subitem = list.GetItemText(i, ca[j].nSubItemIndex);
            TextOut(pd.hDC, ca[j].nPrintX, 2 + lineOnPage * nCharHeight, 
                    subitem, subitem.GetLength());
        }
    }

    EndPage(pd.hDC);
    EndDoc(pd.hDC);
    DeleteDC(pd.hDC);
    return TRUE;
}

使用說明與擴展建議

  • 將上述代碼放入頭文件或通用工具類中
  • 呼叫方式:PrintListCtrl(m_myListCtrl);
  • 可擴展支援:自定義頁首頁尾、列印預覽、複雜儲存格樣式(需改用 ExtTextOut 或 GDI+)
  • 適用環境:VC6.0 / VS2005+,Windows 98/2000/XP/7/10/11

小結

此方案以精簡的 API 呼叫解決對話框類應用的列印難題,代碼結構清晰、易於維護擴展,是 MFC 開發者工具箱中的實用利器。

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