Stack Overflow には数か月おきに同じ質問が投稿されます。 「WinForms アプリで Excel ファイルを表示するにはどうすればよいですか?」 回答はだいたい 3 種類に分かれます — Office を自動化する(やめておきましょう)、WebBrowser でファイルを開く(やめておきましょう)、スプレッドシートコンポーネントを使う(本記事の内容です)。

.xlsx ファイルをデスクトップアプリの中で実際に 表示 し、できれば 編集 して、保存し直す 必要があるなら、本記事が WinForms / WPF でゼロから動作するビューアまでの最短経路です。


「表示する」とは具体的に何を意味するのか

アプローチを選ぶ前に、ユーザーが本当に求めているものを明確にしておく価値があります。要望には次の 4 段階があります。

  1. 読み取り専用プレビュー — 既知のシートの値を表示する。書式や数式は不要。
  2. 忠実なビューア — Excel と同じようにワークブックを描画する: フォント、塗りつぶし、罫線、結合セル、複数シート、ウィンドウ枠の固定。
  3. ライブエディタ — セルをクリックして入力させ、数式の再計算を見せ、.xlsx に保存し直す。
  4. スプレッドシートホスト — アプリ自体がスプレッドシートで、独自コマンドや付随データを持つ。

レベル 1〜2 は DataGridView が向かないタスクです。レベル 3〜4 はあらゆる データグリッド が向かないタスクです。必要なのはスプレッドシートコンポーネント — Excel と同じ方法でセルを描画し、.xlsx 形式をネイティブに理解するコントロールです。


やってはいけないこと

ウェブでよく見かけるものの避けた方がよいアプローチが 3 つあります。

  • Microsoft.Office.Interop.Excel — 実際の Excel プロセスを COM 経由で自動操作します。ユーザーのマシンすべてに Excel がインストールされていることが必要で、サービス上ではサポートされず、そして UI を自分のフォームに 埋め込む こともできません。
  • WebBrowser でファイルを開く — Excel をプロセス外で開くか、オンラインなら OneDrive のウェブビューアが表示されます。10 分間は動くように見えますが、Office が入っていない最初のマシンで破綻します。
  • 列ヘッダー付き DataGridView — フラットなレコードリストには使えます。ですが、ファイルに数式・結合セル・複数シートが現れた瞬間に破綻します。DataGridView が力不足になるタイミングについては この記事 を参照してください。

実際に必要なアプローチは、ワークブック自体を描画するコントロールです。本記事では ReoGrid を例にします — 同じ形のコードはどのスプレッドシートコンポーネントでも通用しますが、ReoGrid は弊社製品で、無料利用枠が最も寛容です。


WinForms: 読み込み・表示・保存

全体でおよそ 15 行です。

1. NuGet パッケージのインストール:

PM> Install-Package unvell.ReoGrid.dll

2. フォームに ReoGridControl を配置 します(Visual Studio のツールボックスから、もしくはコードから)。

3. 読み込みと保存:

using unvell.ReoGrid;

public partial class MainForm : Form
{
    public MainForm()
    {
        InitializeComponent();
    }

    private void openButton_Click(object sender, EventArgs e)
    {
        using var dlg = new OpenFileDialog { Filter = "Excel Workbook|*.xlsx" };
        if (dlg.ShowDialog() != DialogResult.OK) return;

        reoGridControl1.Load(dlg.FileName);   // .xlsx を読み込み、完全に描画される。
    }

    private void saveButton_Click(object sender, EventArgs e)
    {
        using var dlg = new SaveFileDialog { Filter = "Excel Workbook|*.xlsx" };
        if (dlg.ShowDialog() != DialogResult.OK) return;

        reoGridControl1.Save(dlg.FileName);   // ユーザーの編集内容がそのまま往復する。
    }
}

これだけです。ユーザーはファイルを開き、フォント・塗りつぶし・罫線がそのまま反映された状態でワークブックを見て、任意のセルをクリックして入力し、依存する数式が再計算されるのを目視で確認し、下部のタブでシートを切り替え、保存できます。外部プロセスも不要、Office のインストールも不要、WebBrowser のハックも不要です。


WPF: 考え方は同じ、パッケージが違うだけ

1. WPF 用パッケージのインストール:

PM> Install-Package unvell.ReoGridWPF.dll

2. XAML で名前空間を参照し、コントロールを配置:

<Window x:Class="ExcelViewerWpf.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:rg="clr-namespace:unvell.ReoGrid;assembly=unvell.ReoGrid"
        Title="Excel Viewer" Height="600" Width="900">

    <DockPanel>
        <ToolBar DockPanel.Dock="Top">
            <Button Content="Open"  Click="OpenButton_Click"/>
            <Button Content="Save"  Click="SaveButton_Click"/>
        </ToolBar>

        <rg:ReoGridControl x:Name="Grid"/>
    </DockPanel>
</Window>

3. コードビハインド:

using Microsoft.Win32;
using unvell.ReoGrid;

public partial class MainWindow : Window
{
    public MainWindow() => InitializeComponent();

    private void OpenButton_Click(object sender, RoutedEventArgs e)
    {
        var dlg = new OpenFileDialog { Filter = "Excel Workbook|*.xlsx" };
        if (dlg.ShowDialog() != true) return;
        Grid.Load(dlg.FileName);
    }

    private void SaveButton_Click(object sender, RoutedEventArgs e)
    {
        var dlg = new SaveFileDialog { Filter = "Excel Workbook|*.xlsx" };
        if (dlg.ShowDialog() != true) return;
        Grid.Save(dlg.FileName);
    }
}

WinForms 版と WPF 版のコントロールは API が同一で、違うのは参照するアセンブリだけです。両方の UI をメンテナンスする場合でも、ロジックを共有する余地が残されています。


「完全に描画される」とは何を意味するのか

ファイルを読み込めば、ユーザーは DataGridView では失われていたものを得られます。

  • フォント・塗りつぶし・罫線 — Excel と同じ描画
  • 結合セル — ヘッダー内の結合も含む
  • 複数ワークシート — 下部にタブバー付き
  • 数式 — セルに =SUM(A1:A10) と入力すれば、内蔵の数式エンジンで即座に再計算
  • 数値・日付・通貨・パーセント書式.xlsx 元ファイルからそのまま維持
  • 行/列の固定 — ファイルに含まれていれば再現
  • 条件付き書式こちらの記事 を参照

これらを有効化するためのコードは一切書く必要がありません。これこそが、スプレッドシートコントロールが単なるグリッドコントロールと違うところです。


よくある追加要望: 読み取り専用モード

ファイルを 表示するだけ で十分な場合(例: 請求書のプレビュー画面)、すべてのシートで編集をロックします。

foreach (var sheet in reoGridControl1.Worksheets)
{
    sheet.SetSettings(WorksheetSettings.Edit_Readonly, true);
}

コントロールはフォント・数式・書式設定をそのまま描画しつつ、ユーザーはどのセルも変更できなくなります。「Excel ビューアが欲しいが、ユーザーには編集させたくない」という要望への最もすっきりした答えです。


よくある追加要望: ストリームやバイト配列から読み込む

多くのアプリでは、.xlsx がディスク上にあるわけではなく、データベースの BLOB、HTTP レスポンス、MemoryStream から来ます。Load メソッドはストリームを引数に取れます。

using var stream = await httpClient.GetStreamAsync(invoiceUrl);
reoGridControl1.Load(stream, FileFormat.Excel2007);

そして逆方向も同様に。

using var stream = new MemoryStream();
reoGridControl1.Save(stream, FileFormat.Excel2007);
byte[] xlsxBytes = stream.ToArray();    // 保存、アップロード、メール添付など

一時ファイルも追加の依存も不要です。


よくある追加要望: ユーザーの変更を検知する

「何か変わったか?」というワークフローのほとんどは、2 つのイベントでカバーできます。

// 特定セルの編集(入力もしくはペースト)
reoGridControl1.CurrentWorksheet.CellDataChanged += (s, e) =>
{
    Debug.WriteLine($"セル {e.Cell.Address} が {e.Cell.Data} になりました");
};

// 「ワークブックが dirty」の広範なシグナル — 構造や値が変わるたびに発火
reoGridControl1.WorkbookSaved += (s, e) => isDirty = false;

これだけで、「未保存の変更があります — 終了してよいですか?」ダイアログを、元ファイルとの差分計算なしで実装できます。


Interop と比較したメリット

具体的な比較表は次の通りです。

Office Interopスプレッドシートコンポーネント
Excel のインストール必須はいいいえ
サーバー / Linux で動作いいえはい(ヘッドレス用途で)
UI をアプリに埋め込みいいえ(別ウィンドウの Excel)はい(フォーム上のコントロール)
Excel アップデートで壊れない場合によるはい
ライセンスユーザーごとに Office ライセンスコンポーネントライセンス 1 回
セル読み取り速度遅い(COM マーシャリング)速い(プロセス内)

「全ユーザーが Office を持っている」と前提できる社内ツールなら、まだ Interop も魅力的に見えます。しかし、その前提が崩れたとたん — 1 台でも Office が入っていない、1 社の顧客が Mac、サービスとして配布する — 動かなくなります。スプレッドシートコントロールはこの問題を丸ごと回避します。


次に読むもの