印刷は、「とりあえず DataGridView でいこう」というプロジェクトの多くが破綻する工程です。表データを 画面に出す のは簡単ですが、紙に出す — ページ分割し、収まるよう縮小し、適切な余白ときれいな改ページで — となると、結局 PrintDocument を手で組み、Graphics.MeasureString で文字列を測る羽目になります。
データが既にスプレッドシートコントロール上にあるなら、その一切を省けます。本記事では、.NET アプリからワークシートを印刷する方法を示します。1 行の印刷、プレビューダイアログ、システム印刷ダイアログ、PDF への直接出力、そして実際に手を伸ばす用紙・余白・スケーリングの設定です。例には ReoGrid を使いますが、形はどの本格的なスプレッドシートコンポーネントにも当てはまります。
核: 印刷セッション
ReoGrid は 印刷セッション を通じて印刷します。ワークシートから作れば(そのシートを印刷)、コントロールから作れば(ワークブック全体を印刷)、あとは Print() を呼ぶだけです。
// 現在のワークシートを既定設定で印刷
var session = sheet.CreatePrintSession();
session.Print();
この 1 呼び出しが、ページ分割・印刷範囲・スケーリングを処理し、ジョブを既定プリンタへ送ります。本記事の残りは、このセッションが 何をするか を制御する話です。
印刷前に ReoGrid は使用範囲を自動検出し、用紙に収まるようシートをページ分割します。設定ゼロでも CreatePrintSession().Print() は妥当な結果を出します。
印刷プレビュー
たいていのアプリは、先にユーザーへプレビューを見せたいものです。印刷セッションは標準の System.Drawing.Printing.PrintDocument を公開するので、それを組み込みの PrintPreviewDialog に渡します。
var sheet = reoGridControl1.CurrentWorksheet;
using (var session = sheet.CreatePrintSession())
using (var ppd = new PrintPreviewDialog())
{
ppd.Document = session.PrintDocument;
ppd.SetBounds(200, 200, 1024, 768);
ppd.PrintPreviewControl.Zoom = 1d;
ppd.ShowDialog(this);
}
プレビューは以下のすべての設定 — 用紙サイズ、余白、向き、スケーリング、グリッド線 — を反映するので、ユーザーが見たものがそのまま印刷されます。
システム印刷ダイアログ(プリンタをユーザーに選ばせる)
プリンタ選択・部数・ページ範囲を備えた、おなじみの Windows 印刷ダイアログを出すには、同じ PrintDocument で PrintDialog を動かします。
var doc = sheet.CreatePrintSession().PrintDocument;
using (var pd = new System.Windows.Forms.PrintDialog())
{
pd.Document = doc;
pd.UseEXDialog = true; // 64 ビット Windows で必要
if (pd.ShowDialog() == DialogResult.OK)
{
doc.PrinterSettings = pd.PrinterSettings;
doc.Print();
}
}
これがユーザーの期待する「ファイル ▸ 印刷…」の体験で、こちら側にカスタム UI は不要です。
PDF へ直接出力(PDF ライブラリ不要)
「印刷」が実は「メール添付用の PDF が欲しい」を意味することは驚くほど多いものです。PDF ライブラリは要りません — Windows には仮想プリンタが付属しています。セッションを名前で指定して向けるだけです。
sheet.PrintSettings.PrinterName = "Microsoft Print to PDF";
sheet.CreatePrintSession().Print();
ジョブは紙ではなく .pdf に描画されます。PrinterName を null に戻せば、また既定プリンタを使います。同じ手は、インストール済みのどの仮想プリンタ(サードパーティの PDF ドライバなど)でも通用します — 単なるプリンタ名です。
何を印刷するかを選ぶ
印刷セッションは既定で現在のワークシートを対象にしますが、ワークブック全体や、特定のシート群を印刷できます。
単一ワークシート:
var session = worksheet.CreatePrintSession();
session.Print();
ワークブック全体(全シート):
var session = reoGridControl.CreatePrintSession();
session.Print();
特定のシートを、指定した順序で:
var session = reoGridControl.CreatePrintSession();
session.Worksheets.Clear();
session.Worksheets.Add(grid.Worksheets["Summary"]);
session.Worksheets.Add(grid.Worksheets["Details"]);
session.Print();
これが、1 つのワークブックから複数文書のジョブ — たとえば領収書と控え — を作る方法です。そのパターンの最初から最後までは 領収書発行アプリの記事 を参照してください。
印刷範囲を設定する
既定では ReoGrid は使用範囲を印刷します。シートの一部だけを印刷するには PrintableRange を設定します。
sheet.PrintableRange = new RangePosition(1, 1, 9, 9); // 行, 列, 行数, 列数
印刷範囲の外は出力されません。範囲が用紙より広い/高い場合、ReoGrid は自動的に複数ページへ分割します。
用紙サイズ・向き・余白
ページ設定は sheet.PrintSettings にあります。
// 用紙サイズ — PaperManager.PaperSizesInch のキーと一致する必要あり
sheet.PrintSettings.PaperName = "A4"; // "A3", "A5", "Letter", "Legal", "JIS_B4"…
// 向き
sheet.PrintSettings.Landscape = true;
// 余白(インチ)
sheet.PrintSettings.Margins = new PageMargins(1f); // 全辺 = 1"
sheet.PrintSettings.Margins = new PageMargins(0.5f, 0.5f, 1f, 1f); // 上, 下, 左, 右
カスタム用紙サイズの場合は PaperName = "Custom" にして寸法を指定します。
sheet.PrintSettings.PaperName = "Custom";
sheet.PrintSettings.PaperWidth = 6.0f; // インチ
sheet.PrintSettings.PaperHeight = 9.0f; // インチ
ReoGrid は ISO の A/B/C シリーズ、JIS B シリーズ、北米サイズを知っているので、"Custom" が要ることはめったにありません。
収まるように縮小する
シートが少しだけ幅広なときは、数列を 2 ページ目にこぼすのではなく縮小します。
sheet.PrintSettings.PageScaling = 0.7f; // 70% で印刷
スケールを変えると、ワークシートは自動的に再ページングされます。有効範囲はおおよそ 0.1〜4.0(10%〜400%)です。
改ページ: 紙が分かれる位置
ReoGrid は用紙に収めるため システム改ページ(破線)を自動挿入します。自分で ユーザー改ページ(実線)を追加して、分割を強制することもできます。
sheet.ColumnPageBreaks.Add(5); // 列 5 で改ページ
sheet.RowPageBreaks.Add(40); // 行 40 でも
任意のタイミングで自動ページングを再実行するには — たとえばデータや用紙サイズを変えた後に。
sheet.AutoSplitPage();
すべてのユーザー改ページを消して最初からやり直すには。
sheet.ResetAllPageBreaks();
改ページ位置を画面に 表示 して、ユーザーがページの分かれ目を見えるようにするには。
sheet.EnableSettings(WorksheetSettings.View_ShowPageBreaks);
グリッド線を印刷する
既定ではセルのグリッドは 印刷されません — 適用した罫線だけが出ます。グリッド線も含めるには(生データの出力に便利)。
sheet.PrintSettings.ShowGridLines = true;
印刷前にページ割りを知る
シートがどうページ分割されるか知りたいことがあります — ページ数を表示したり、「Page 1 of N」を入れたり。印刷ページを反復処理します。
sheet.IteratePrintPages(range =>
{
Console.WriteLine("ページ範囲: " + range.ToAddress());
return true; // false を返すと途中で停止
});
各コールバックが、1 ページに載るセル範囲を渡します。
PrintSettings 早見表
| プロパティ | 型 | 既定 | 用途 |
|---|---|---|---|
PrinterName | string | null | 出力先プリンタ(null = システム既定。PDF は "Microsoft Print to PDF") |
PaperName | string | "Letter" | 用紙サイズキー("A4", "Legal", "Custom"…) |
Landscape | bool | false | 横向き |
Margins | PageMargins | 全辺 1f | ページ余白(インチ) |
PageScaling | float | 1f | スケール倍率(0.1〜4.0) |
PageOrder | PrintPageOrder | DownThenOver | 複数ページ時のページ割り方向 |
ShowGridLines | bool | false | セルのグリッドを印刷 |
PrintDocument を手で組むより優れている理由
DataGridView を自分で印刷すると、すべてを自分で持つことになります。行の測定、改ページ位置の決定、各ページへのヘッダー描画、余白に合わせた縮小。数百行になり、しかも壊れやすい。スプレッドシートコントロールは既に それ自体が ページ分割済みの書式付き文書なので、印刷は実装ではなく設定です。用紙と余白を指定すれば、レイアウトは向こうが処理します。
次に読むもの
- 約30行のC#で領収書発行アプリを作る — 印刷を実際の完成アプリに適用
- ページングと印刷 — 完全リファレンス — すべての改ページ・印刷セッション API
- C# で WinForms / WPF アプリに Excel ファイルを表示・編集する — これから印刷するワークブックの読み込み
- ReoGrid インストールガイド — NuGet パッケージと参照
