| 元件 | 命名空間 | 用途 |
PrintDocument | System.Drawing.Printing | 核心列印邏輯、頁面內容繪製、事件驅動 |
PrintDialog | System.Windows.Forms | 選擇印表機、份數、頁面範圍 |
PageSetupDialog | System.Windows.Forms | 紙張大小、方向、邊界 |
PrintPreviewDialog | System.Windows.Forms | 列印預覽視窗 |
所有對話框皆透過 Document 屬性綁定同一個 PrintDocument 實例,共享設定。
PrintDocument 核心成員
PrintDocument pd = new PrintDocument();
pd.DocumentName = "我的文件"; // 顯示在印表機佇列
pd.PrintPage += Pd_PrintPage; // 核心繪製事件
pd.BeginPrint += Pd_BeginPrint; // 列印開始前初始化
pd.EndPrint += Pd_EndPrint; // 列印結束清理
pd.PrinterSettings = ...; // 印表機設定
pd.DefaultPageSettings = ...; // 預設頁面設定
PrintPage 事件:逐頁繪製邏輯
private StringReader _lineReader;
private Font _printFont;
private void Pd_BeginPrint(object sender, PrintEventArgs e)
{
_printFont = textBox.Font;
_lineReader = new StringReader(textBox.Text);
}
private void Pd_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
float linesPerPage = e.MarginBounds.Height / _printFont.GetHeight(g);
float yPos = e.MarginBounds.Top;
int count = 0;
string line;
while (count < linesPerPage && (line = _lineReader.ReadLine()) != null)
{
yPos = e.MarginBounds.Top + count * _printFont.GetHeight(g);
g.DrawString(line, _printFont, Brushes.Black, e.MarginBounds.Left, yPos);
count++;
}
e.HasMorePages = (line != null); // 還有資料 → 觸發下一次 PrintPage
}
private void Pd_EndPrint(object sender, PrintEventArgs e)
{
_lineReader?.Dispose();
_lineReader = null;
}
關鍵點:
e.MarginBounds:可列印區域(扣除邊界)
e.Graphics:頁面繪圖介面,同 Paint 事件
e.HasMorePages:控制多頁列印流程
- 使用
StringReader 記憶讀取位置,跨頁面連續
整合四大對話框
// 列印設定
private void MenuPrintSetup_Click(object sender, EventArgs e)
{
using (var dlg = new PrintDialog { Document = pd })
dlg.ShowDialog();
}
// 頁面設定
private void MenuPageSetup_Click(object sender, EventArgs e)
{
using (var dlg = new PageSetupDialog { Document = pd })
dlg.ShowDialog();
}
// 列印預覽
private void MenuPrintPreview_Click(object sender, EventArgs e)
{
using (var dlg = new PrintPreviewDialog { Document = pd })
{
_lineReader = new StringReader(textBox.Text); // 重置讀取器
try { dlg.ShowDialog(); }
catch (Exception ex) { MessageBox.Show(ex.Message); }
}
}
// 正式列印
private void MenuPrint_Click(object sender, EventArgs e)
{
using (var dlg = new PrintDialog { Document = pd })
{
if (dlg.ShowDialog() == DialogResult.OK)
{
_lineReader = new StringReader(textBox.Text);
try { pd.Print(); }
catch (Exception ex) { MessageBox.Show(ex.Message); }
}
}
}
進階繪製技巧
| 需求 | 實作方式 |
| 表格/格線 | g.DrawLine/Pen 計算座標繪製 |
| 圖片 | g.DrawImage(Image, Rect) 注意解析度 |
| 頁碼 | 在 PrintPage 末尾 g.DrawString($"第 {pageNum} 頁", ...) |
| 不同字型/顏色 | 建立多個 Font/Brush 物件,using 釋放 |
| 橫向列印 | pd.DefaultPageSettings.Landscape = true |
資源管理與效能
Font、Brush、Pen 實作 IDisposable,用 using 或統一在 EndPrint 釋放
- 大量資料列印考慮背景執行緒 +
PrintController 避免 UI 凍結
- 預覽對話框會多次觸發
PrintPage,確保邏輯可重入
總結流程
- 表單初始化建立
PrintDocument,掛接 PrintPage/BeginPrint/EndPrint
- 使用者透過選單開啟「頁面設定」/「列印預覽」/「列印設定」
- 點擊「列印」→ 顯示
PrintDialog → 確認後呼叫 pd.Print()
PrintDocument 依序觸發 BeginPrint → 多次 PrintPage → EndPrint
- 列印完成,資源釋放
- 上一篇文章: [你必須知道的 .NET] 第四回:Class 與 Struct 的記憶體模型、語法差異與選型指南
- 下一篇文章: C# 開發 Windows 服務發送中文簡訊:Oxygen SMS 控制項與定時輪詢架構
Copyright ©
程式碼之家 All Rights Reserved