「領収書・請求書・帳票を生成する」系のチュートリアルの多くは、Graphics.DrawString で枠を描き文字位置を調整するのに 200 行を費やします。そして顧客から「ロゴを 5mm 動かして」と言われた瞬間、定規を片手にまたコードエディタへ逆戻りです。

もっと短い道があります。レイアウトは Excel で一度だけ作る — ロゴ、罫線、フォント、ラベル、すべてを。それを .xlsx テンプレートとして保存し、テンプレートを読み込んで空のセルに当日の値を流し込み、印刷する小さなプログラムを書くだけ。見た目の設計がコードに一切触れないので、コードは小さいままです。

その全体 — 完成した実行可能な WinForms アプリ — が約30行です。


考え方: テンプレートが設計を担い、コードは空欄を埋める

領収書は 90% が固定レイアウト、10% が可変データです。固定部分 — 会社名、ロゴ、表の枠、「領収書」の見出し、印鑑欄 — はソースコードではなくデザイナーに属します。Excel が まさにその デザイナーであり、チームの誰もが既に使い方を知っています。

つまり役割分担はこうです。

  • receipt-template.xlsx — Excel で作る。結合したタイトルセル、罫線付きの明細表、合計行、フロート画像としてのロゴ。変化するセルは単に空欄にしておく。
  • プログラム — そのファイルを読み込み、空セル(C3B5、明細行…)に値を書き込み、印刷を呼ぶ。

ReoGrid がこの 2 つを橋渡しします。.xlsx をネイティブに読み込み(フォント・塗りつぶし・罫線・結合セル・画像をすべて維持)、Excel とまったく同じようにセルを指定でき(sheet["C3"])、内蔵エンジンで数式を再計算し、印刷する — すべてプロセス内で、Excel のインストールも不要です。


ステップ1 — Excel でテンプレートを作る

Excel を開き、印刷したいとおりに領収書を組み立て、動的なセルは空のままにします。

セル用途埋める担当
C2領収書番号コード
C3発行日コード
B5宛名(御中 / コード
B7但し書きコード
B10:D12明細(品名・数量・単価)コード
E10:E12行ごとの金額数式
E13合計数式
ロゴ・タイトル・罫線・ラベルテンプレート(固定)

これを実行ファイルの隣に receipt-template.xlsx として保存します。このファイルがスタイルをすべて持つので、コードはフォントも罫線も一切設定しません。


ステップ2 — アプリの全体

パッケージをインストール。

PM> Install-Package unvell.ReoGrid.dll

そしてこれがプログラムの全体です。

using System;
using System.Windows.Forms;
using unvell.ReoGrid;

class ReceiptApp : Form
{
    readonly ReoGridControl grid = new() { Dock = DockStyle.Fill };

    ReceiptApp()
    {
        var button = new Button { Text = "発行して印刷", Dock = DockStyle.Top, Height = 40 };
        button.Click += (s, e) => IssueReceipt();
        Controls.Add(grid);
        Controls.Add(button);
        grid.Load("receipt-template.xlsx");          // Excel で作ったレイアウト
        Text = "領収書発行";
        Size = new System.Drawing.Size(720, 900);
    }

    void IssueReceipt()
    {
        var sheet = grid.CurrentWorksheet;

        sheet["C2"] = $"No. {DateTime.Now:yyyyMMdd}-001";
        sheet["C3"] = DateTime.Now.ToString("yyyy年MM月dd日");
        sheet["B5"] = "株式会社サンプル 御中";
        sheet["B7"] = "Web サイト制作費として";

        var items = new[]
        {
            ("デザイン費",      1, 120000),
            ("コーディング費",  1,  80000),
            ("保守費(月額)",  6,   5000),
        };

        int first = 10, r = first;                   // 明細は 10 行目から
        foreach (var (name, qty, price) in items)
        {
            sheet[$"B{r}"] = name;
            sheet[$"C{r}"] = qty;
            sheet[$"D{r}"] = price;
            sheet[$"E{r}"] = $"=C{r}*D{r}";          // 金額 = 数量 × 単価
            r++;
        }
        sheet[$"E{r}"] = $"=SUM(E{first}:E{r - 1})"; // 合計

        grid.Save($"receipt-{DateTime.Now:yyyyMMdd-HHmmss}.xlsx");  // 控えを保存
        sheet.CreatePrintSession().Print();          // 既定プリンタへ印刷
    }

    [STAThread]
    static void Main() => Application.Run(new ReceiptApp());
}

これだけです。実行して 発行して印刷 をクリックすると、完全に書式の整った領収書がプリンタから出てきます — 合計は数式エンジンが計算し、控えの .xlsx がディスクに保存されます。

実際に領収書を 発行する 部分 — IssueReceipt() — は約20行。残りはボタン付きのウィンドウだけです。


なぜこれほど短いのか

3 つの要素が重労働を肩代わりしてくれるので、コードがやることが減ります。

  • Excel テンプレート がレイアウトの 1 ピクセルまで担います。DrawString も、手動の列幅も、罫線コードも不要。ロゴを動かしたい? .xlsx を開いてドラッグして保存。コード変更ゼロです。
  • 数式エンジン が行金額と合計を計算します。Excel と同じように =C10*D10=SUM(...) をセルに書くだけで、ReoGrid が印刷前に再計算します。税・小計の計算を C# に書く必要はありません。
  • CreatePrintSession().Print() が描画済みワークシートを 1 呼び出しで印刷出力に変えます。ページ分割・スケーリング・印刷範囲は ReoGrid が処理します — 全オプションは C# でスプレッドシートを印刷する を参照してください。

セルの指定: 等価な 2 つの書き方

例では、Excel テンプレートと同じように読める文字列アドレスを使っています。

sheet["C3"] = DateTime.Now.ToString("yyyy年MM月dd日");
sheet[$"E{r}"] = $"=C{r}*D{r}";

行を反復処理するときは、0 始まりの [row, col] インデクサのほうがすっきりすることが多いです。

sheet[9, 4] = "=C10*D10";   // 行 9(0 始まり)= Excel の 10 行目、列 4 = E 列

どちらも同じセルに書き込みます。行の計算が分かりやすいほうを使ってください。手作業で作ったテンプレートに合わせるときは、文字列アドレスのほうが有利です。


よくある追加要望: 印刷前にプレビュー

Print() は既定のプリンタへ直行します。先にユーザーへプレビューを見せるには、セッションの PrintDocument を標準ダイアログに渡します。

using var session = sheet.CreatePrintSession();
using var preview = new PrintPreviewDialog { Document = session.PrintDocument };
preview.ShowDialog(this);

あるいは System.Windows.Forms.PrintDialog でプリンタ選択を出すこともできます。どちらも 印刷ガイド で扱っています。


よくある追加要望: 紙ではなく PDF で保存

領収書アプリは印刷ではなく PDF を メール送信 したいことがほとんどです。印刷セッションを内蔵の PDF プリンタに向けます。

sheet.PrintSettings.PrinterName = "Microsoft Print to PDF";
sheet.CreatePrintSession().Print();   // 紙ではなく .pdf を書き出す

PDF ライブラリも追加の依存も不要 — Windows 自身の仮想プリンタが描画してくれます。


よくある追加要望: 控え用にもう 1 部、別シートを

実際の領収書では「控え」と「お客様控え」、あるいは納品書を別に、ということがよくあります。テンプレートでそれぞれを別のワークシートに置き、必要なものだけ印刷します。

var session = grid.CreatePrintSession();
session.Worksheets.Clear();
session.Worksheets.Add(grid.Worksheets["Receipt"]);
session.Worksheets.Add(grid.Worksheets["OfficeCopy"]);
session.Print();

テンプレートが、各帳票の見た目に関する唯一の真実であり続けます。


もっと重い仕組みが要るのはどんなとき

このパターンは、領収書・請求書・見積書・納品書、そしてレイアウトが安定していてデータが表形式の単票帳票のほとんどに最適です。帳票ツール側でグルーピングや小計を計算しながら何ページにもわたって伸びるバンド形式のレポートが必要なら、専用のレポートエンジンが価値を発揮します。ですが「このフォームを埋めて印刷する」用途なら、スプレッドシートテンプレート+30行に勝つのは難しく、しかも非エンジニアが開発者の手を借りずにレイアウトを編集できます。


次に読むもの