【実務・中級編】Windows Formsの印刷プレビューにおける独自ヘッダー・フッターの動的描画:PrintPageEventArgsの完全制御 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

Windows Forms印刷の深淵:PrintPageEventArgsを支配し、帳票エンジンを自作せよ

業務系アプリケーション開発において、帳票出力は「最後の砦」だ。標準の `PrintPreviewDialog` や `PrintDocument` をそのまま使って、顧客から「ヘッダーに社外秘の透かしを入れたい」「ページ番号をX/Y形式で出せ」「明細の改ページ位置がずれている」といった要求を受けたとき、多くのエンジニアはパッチワーク的な実装で自滅していく。

いいか、標準コンポーネントの挙動に振り回されるな。`PrintPageEventArgs` を完全に制御し、GDI+ の描画コンテキストを直接叩くアーキテクチャこそが、保守性の高い帳票エンジンの正解だ。

今日は、その極意を伝授する。

—

1. なぜ「標準コントロール」では限界があるのか

標準の `PrintDocument` は、OSのプリンタドライバーと密接に結合している。安易に `PrintPage` イベント内で「データベースアクセス」や「複雑な計算」を行うと、以下の地獄を見る。

  • プレビューと印刷結果の乖離: プレビュー時の解像度(DPI)とプリンタの解像度は異なる。座標計算をハードコードすると必ず崩れる。
  • メモリリーク: `PrintPageEventArgs.Graphics` は使い捨てだ。描画オブジェクト(Pen, Brush, Font)を `Using` ブロックで適切に破棄しないと、数百ページの印刷でメモリが枯渇する。
  • 状態管理の崩壊: ページをまたぐ計算でグローバル変数を使うな。印刷状態を保持する「ステートマシン」を設計せよ。

—

2. 堅牢な帳票エンジンの設計:状態管理の分離

印刷プロセスを「データ取得」と「描画ロジック」に分離する。これがバグを生まない唯一の設計だ。

‘ 印刷状態を管理するクラス
Public Class ReportState
Public Property CurrentPage As Integer = 1
Public Property TotalPages As Integer = 0
Public Property DataIndex As Integer = 0
‘ 印刷対象のリストやデータセットを保持
End Class

3. 実践:PrintPageEventArgsを制する描画ロジック

以下は、ヘッダー・フッター・透かし・改ページ制御を実装したプロダクションコードの雛形だ。

Private Sub PrintDocument1_PrintPage(sender As Object, e As PrintPageEventArgs) Handles PrintDocument1.PrintPage
Dim g As Graphics = e.Graphics
Dim pageBounds As RectangleF = e.MarginBounds

‘ 1. 「社外秘」の透かしを背景に描画(透過処理)
Using brush As New SolidBrush(Color.FromArgb(50, Color.Red))
Dim font As New Font(“Arial”, 72, FontStyle.Bold)
g.RotateTransform(-45) ‘ 斜めに傾ける
g.DrawString(“社外秘”, font, brush, -100, 500)
g.ResetTransform() ‘ 変換を戻すことを忘れない
End Using

‘ 2. ヘッダー・フッター描画
DrawHeaderFooter(g, pageBounds, state.CurrentPage)

‘ 3. 明細描画(改ページ制御)
Dim yOffset As Single = 100 ‘ 初期位置
While state.DataIndex < myDataList.Count ' ここで描画ロジック実行 ' もし yOffset がページ下限を超えたら HasMorePages = True にして抜ける If yOffset > pageBounds.Bottom – 50 Then
e.HasMorePages = True
state.CurrentPage += 1
Return
End If

‘ 行描画処理…
yOffset += 30
state.DataIndex += 1
End While

e.HasMorePages = False
End Sub

Private Sub DrawHeaderFooter(g As Graphics, bounds As RectangleF, pageNo As Integer)
‘ ページ番号の描画
g.DrawString($”ページ: {pageNo}”, New Font(“MS UI Gothic”, 9), Brushes.Black, bounds.Right – 50, bounds.Bottom + 10)
End Sub

—

4. 現場で生き残るための「鉄則」

① DPIスケーリングを意識せよ

`g.DpiX` や `g.DpiY` を使って座標を補正しろ。特にプレビュー画面と実際の紙では解像度が異なるため、`GraphicsUnit.Inch` や `GraphicsUnit.Millimeter` を基準に計算する癖をつけろ。

② 描画リソースの即時解放

GDI+ のオブジェクトはマネージドリソースではない。`Using` ステートメントの使用は必須だ。怠れば、WindowsのGDIハンドル制限に抵触し、OSごとフリーズさせることになる。

③ データベース接続は印刷の「前」に完了させる

`PrintPage` イベントの中でクエリを投げるな。ユーザーが印刷ボタンを押した瞬間にデータをメモリ(または一時ファイル)にロードし、描画時はそのキャッシュを参照する。これがパフォーマンスと堅牢性の両立だ。

—

最後に:エンジニアの美学

「コードは動けばいい」という段階は卒業したはずだ。
帳票出力とは、システムから出力される「顔」である。ヘッダーのズレ一つ、透かしの不自然な重なり一つで、ユーザーはそのシステム全体の信頼性を判断する。

`PrintPageEventArgs` は、単なるイベント引数ではない。君のロジックを紙の上に具現化するための、最も強力なキャンバスだ。このキャンバスをどう使いこなすか、それは君の設計思想そのものになる。

さあ、退屈な標準機能の制約を脱ぎ捨て、真の帳票エンジンを組み上げろ。君のコードなら、必ずできるはずだ。

タイトルとURLをコピーしました