印刷は、「とりあえず 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 印刷ダイアログを出すには、同じ PrintDocumentPrintDialog を動かします。

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 に描画されます。PrinterNamenull に戻せば、また既定プリンタを使います。同じ手は、インストール済みのどの仮想プリンタ(サードパーティの 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 早見表

プロパティ既定用途
PrinterNamestringnull出力先プリンタ(null = システム既定。PDF は "Microsoft Print to PDF"
PaperNamestring"Letter"用紙サイズキー("A4", "Legal", "Custom"…)
Landscapeboolfalse横向き
MarginsPageMargins全辺 1fページ余白(インチ)
PageScalingfloat1fスケール倍率(0.1〜4.0)
PageOrderPrintPageOrderDownThenOver複数ページ時のページ割り方向
ShowGridLinesboolfalseセルのグリッドを印刷

PrintDocument を手で組むより優れている理由

DataGridView を自分で印刷すると、すべてを自分で持つことになります。行の測定、改ページ位置の決定、各ページへのヘッダー描画、余白に合わせた縮小。数百行になり、しかも壊れやすい。スプレッドシートコントロールは既に それ自体が ページ分割済みの書式付き文書なので、印刷は実装ではなく設定です。用紙と余白を指定すれば、レイアウトは向こうが処理します。


次に読むもの