| 屬性 | 說明 |
Graphics | 頁面繪圖介面,呼叫 DrawString、DrawImage 等 |
MarginBounds | 可列印區域矩形(含邊界) |
PageBounds | 整頁紙張矩形 |
HasMorePages | 關鍵:true=繼續下一頁,false=結束列印 |
Cancel | 設 true 取消列印 |
三、完整實作程式碼
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Windows.Forms;
public partial class NotepadForm : Form
{
private PrintDocument _printDoc;
private StringReader _lineReader; // 追蹤讀取位置
public NotepadForm()
{
InitializeComponent();
InitializePrinting();
}
private void InitializePrinting()
{
_printDoc = new PrintDocument();
_printDoc.DocumentName = "記事本文件";
_printDoc.PrintPage += PrintDoc_PrintPage;
}
// ========== 核心分頁邏輯 ==========
private void PrintDoc_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
Font printFont = textBox.Font;
Brush brush = Brushes.Black;
float linesPerPage = e.MarginBounds.Height / printFont.GetHeight(g);
float yPos = e.MarginBounds.Top;
float xPos = e.MarginBounds.Left;
int count = 0;
string line;
// 逐行列印,直到填滿一頁或內容結束
while (count < linesPerPage && ((line = _lineReader.ReadLine()) != null))
{
yPos = e.MarginBounds.Top + (count * printFont.GetHeight(g));
g.DrawString(line, printFont, brush, xPos, yPos);
count++;
}
// 還有內容未列印完 → 觸發下一頁
e.HasMorePages = (line != null);
}
// ========== 列印設定 ==========
private void Menu_PrintSetup_Click(object sender, EventArgs e)
{
using (var dlg = new PrintDialog())
{
dlg.Document = _printDoc; // 同步設定到 PrintDocument
dlg.ShowDialog();
}
}
// ========== 頁面設定 ==========
private void Menu_PageSetup_Click(object sender, EventArgs e)
{
using (var dlg = new PageSetupDialog())
{
dlg.Document = _printDoc;
dlg.ShowDialog();
}
}
// ========== 列印預覽 ==========
private void Menu_PrintPreview_Click(object sender, EventArgs e)
{
_lineReader = new StringReader(textBox.Text); // 重置讀取位置
using (var dlg = new PrintPreviewDialog())
{
dlg.Document = _printDoc;
dlg.WindowState = FormWindowState.Maximized;
try
{
dlg.ShowDialog();
}
catch (Exception ex)
{
MessageBox.Show($"預覽錯誤: {ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
// ========== 正式列印 ==========
private void Menu_Print_Click(object sender, EventArgs e)
{
_lineReader = new StringReader(textBox.Text); // 重置讀取位置
using (var dlg = new PrintDialog())
{
dlg.Document = _printDoc;
if (dlg.ShowDialog() == DialogResult.OK)
{
try
{
_printDoc.Print();
}
catch (Exception ex)
{
MessageBox.Show($"列印錯誤: {ex.Message}", "錯誤", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
}
}
}
四、分頁邏輯深度解析
關鍵變數
_lineReader:類別層級,跨 PrintPage 事件保持讀取位置
linesPerPage:MarginBounds.Height / Font.Height,每頁可容納行數
yPos:每行垂直座標,Top + 行號 * 行高
流程圖解
Print() 呼叫
↓
PrintPage 事件 (第 1 頁)
↓
while (行數 < linesPerPage && 讀取下一行)
繪製該行
行數++
↓
若 讀取到 null (內容結束)
HasMorePages = false → 列印結束
若 讀取非 null (還有內容)
HasMorePages = true → 觸發下一次 PrintPage
↓
PrintPage 事件 (第 2 頁) ... 循環直到 HasMorePages = false
五、常見問題與解決
| 問題 | 原因 | 解決 |
| 只列印第一頁 | HasMorePages 未設 true | 檢查 line != null 邏輯 |
| 內容重複/遺漏 | _lineReader 未重置或共用錯誤 | 每次列印/預覽前 new StringReader(text) |
| 字型/大小不符 | 使用預設字型而非 TextBox 字型 | Font printFont = textBox.Font |
| 中文亂碼 | 字型不支援中文 | 使用 Microsoft JhengHei / SimSun 等 |
| 預覽與列印不同 | 預覽未重置 _lineReader | 兩者前均重置 |
六、進階擴展
- 頁首/頁尾:在
PrintPage 繪製頁碼、標題、日期
- 表格/圖片列印:計算欄寬、
DrawImage、分頁斷行處理
- 異步列印:
PrintDocument.PrintController 自訂
- 報表元件:複雜報表建議用
ReportViewer、FastReport、Stimulsoft
七、總結
WinForm 列印核心三步驟:
- 建立
PrintDocument,掛接 PrintPage 事件
- 實作
PrintPage:Graphics.DrawString + HasMorePages 分頁控制
- 搭配
PrintDialog/PageSetupDialog/PrintPreviewDialog 提供使用者設定
掌握 StringReader 追蹤位置 + HasMorePages 遞迴觸發,即掌握多頁列印精髓。
- 上一篇文章: C# 存取 Hotmail 完整實作(一):HTTPMail 協議、XMLHTTP 連線、資料夾 URL 取得與 XML 解析
- 下一篇文章: [你必須知道的.NET] 第四回:class 與 struct 深度比較(二):經典範例、.NET Framework 實作、結論
Copyright ©
程式碼之家 All Rights Reserved