帳票出力の深淵:PrintDocumentを極限まで制御し、レガシーを「芸術」へ昇華させる
多くの開発者がWindows Formsの`PrintDocument`に触れ、そして挫折する。
「なぜかプレビューと実印字でレイアウトがズレる」「メモリリークで巨大な帳票が落ちる」「標準のプレビュー画面がダサすぎて業務に耐えない」。これらは全て、フレームワークの甘い誘惑に身を委ねた結果だ。
真のエンジニアは、プリンタードライバの気まぐれに依存しない。描画の全てを座標計算で支配し、オブジェクトの生殺与奪を握る。今日は、帳票出力の「最終形態」について語ろう。
—
1. ページ計算の自律化:レンダリングエンジンを自作する
`PrintPage`イベントにロジックを詰め込むのは初心者のやることだ。複雑な帳票は、まず「仮想的なレイアウト計算機」を作る必要がある。
プリンターの物理的解像度(DPI)に振り回されないために、全ての座標は `Graphics.PageUnit = GraphicsUnit.Millimeter` で固定せよ。これが「機種依存からの脱却」の第一歩だ。
.net
‘ 帳票のページングロジックをカプセル化するクラス
Public Class ReportEngine
Private _data As List(Of InvoiceItem)
Private _currentPageIndex As Integer = 0
Private Const PageHeight As Single = 297.0F ‘ A4縦
Private Const MarginTop As Single = 20.0F
‘ ページ分割のロジック(PrintPageで呼び出す)
Public Sub DrawPage(g As Graphics, e As PrintPageEventArgs)
Dim currentY As Single = MarginTop
‘ 現在のページで描画可能な行数を算出し、ループを回す
While _currentPageIndex < _data.Count
Dim item = _data(_currentPageIndex)
' 描画処理...
currentY += 10.0F ' 行の高さ
_currentPageIndex += 1
' 次のページへ溢れるか判定
If currentY > (PageHeight – 30.0F) Then
e.HasMorePages = True
Return
End If
End While
e.HasMorePages = False
End Sub
End Class
2. PrintPreviewDialogの「支配」
標準の`PrintPreviewDialog`は拡張性に欠ける。しかし、`PrintPreviewControl`を独自フォームに埋め込めば、話は別だ。ツールバーを自作し、クライアントの要望に応じた操作性(全画面表示、拡大率の固定、ページ遷移アニメーション)を実装できる。
.net
‘ 自作プレビューフォームの設計指針
Public Class CustomPrintPreviewForm
Private WithEvents _previewControl As New PrintPreviewControl()
Public Sub New(doc As PrintDocument)
_previewControl.Document = doc
_previewControl.Zoom = 1.0F
_previewControl.Dock = DockStyle.Fill
Me.Controls.Add(_previewControl)
‘ ここで自作のツールバー等を配置し、Document.Print()を制御する
End Sub
End Class
3. メモリ管理の極意:マネージドコードの限界を超える
大量の画像(ロゴや印影)を含む帳票を出力する際、`PrintDocument`は往々にしてメモリを食いつぶす。`GC.Collect()`に祈るな。明示的な`Dispose()`こそが唯一の正義だ。
特に`Graphics`オブジェクトや`Font`オブジェクトは、インスタンスを使い回すのではなく、描画サイクルごとに生成と破棄を繰り返すのが最も安定する。
.net
Private Sub OnPrintPage(sender As Object, e As PrintPageEventArgs)
‘ 描画が終わったら、必ずリソースを解放する
Using g As Graphics = e.Graphics
Using myFont As New Font(“MS Gothic”, 10)
‘ 描画ロジック
End Using
End Using
‘ Using句を抜ければ即座に解放される。これが最も確実なメモリ戦略だ
End Sub
4. シニアエンジニアへの遺言:Win32 APIの活用
どうしても標準の印刷ダイアログの挙動が納得いかない場合、`winspool.drv` を呼び出し、プリンターの設定をバイナリレベルで強制変更する術も存在する。だが、それは劇薬だ。
まずは「なぜその帳票が必要なのか」という本質を問い直せ。複雑すぎる帳票は、PDF生成に切り替えるのが現代の最適解かもしれない。しかし、どうしても「紙」でなければならない現場があるならば、上記のアーキテクチャを堅牢に実装せよ。
結び
VB.NETは古臭い言語ではない。イベント駆動という設計思想が、現代のWebフロントエンドや非同期処理にも通ずる「リアクティブなUI」の先駆けであったことを忘れてはならない。
コードは、ただ動けばいいというものではない。
数年後、自分が保守する時に「美しい」と感じられるか。それがプロフェッショナルの仕事だ。
さあ、プリンターの挙動を完全に掌握し、完璧な帳票を出力する準備は整ったか?
