DataGridView:データを Excel に出力する(サンプルあり)

📋 目次(クリックで展開)

Windows Forms の DataGridView に表示されている一覧データを、Excel ファイル(.xlsx)として出力・保存する実装手順について解説します。

本記事では、Microsoft 公式の COM 参照(Microsoft.Office.Interop.Excel)を利用し、リソースの解放処理やデータ不整合のエラー対策を考慮した標準的な実装コードを提示します。Excel の起動を伴うため、処理開始までに数秒のオーバーヘッドが発生しますが、データ転送の往復回数を抑える工夫を含めて解説します。

事前準備:COM参照の追加と型埋め込みの設定

Excel の操作機能を使用するために、プロジェクトに COM 参照を追加します。また、近年の .NET(Core系列 / .NET 10 など)環境で発生しやすいファイル行方不明エラー(System.IO.FileNotFoundException)を防ぐため、以下の手順で参照を設定します。

1.Visual Studio のソリューションエクスプローラーで、プロジェクトの「依存関係」を右クリックして [COM 参照の追加] を選択します。

2.検索ボックスに「Excel」と入力し、Microsoft 製の 「Microsoft Excel 16.0 Object Library」(バージョン番号は環境により異なる場合があります)にチェックを入れて [OK] をクリックします。

3.追加された依存関係(COM)の中にある Microsoft.Office.Interop.Excel を選択し、プロパティウィンドウで 「相互運用型の埋め込み」(Embed Interop Types)が True になっていることを確認します。

※ネット上で有志が公開している非公式の NuGet パッケージ(作成者が Microsoft 以外になっているもの)は、.NET の仕様変更により実行時エラーの原因になることがあるため、上記のように Visual Studio の標準機能から本物の COM 参照を追加する方法が確実です。

実装コード

以下は、DataGridView の内容を取得し、Excel ブックを生成してデータを転送後、名前を付けて保存するコード例です。ファイル冒頭の名前空間の宣言(using)も含めて記述しています。

Form1.cs

Form1.cs
using System.Data;
using System.Runtime.InteropServices;
// 長い名前空間を省略するため、ファイルの最上部で別名を定義します
using Excel = Microsoft.Office.Interop.Excel;
namespace DataGridView_Export_Excel
{
public partial class Form1 : Form
{
private DataTable _dataTable = new DataTable();
public Form1()
{
InitializeComponent();
InitializeDataGridView();
btnExportExcel.Click += btnExportExcel_Click;
}
private void InitializeDataGridView()
{
// テスト用データの準備(5行×3列)
_dataTable.Columns.Add("Code", typeof(string));
_dataTable.Columns.Add("Name", typeof(string));
_dataTable.Columns.Add("Price", typeof(int));
_dataTable.Rows.Add("P001", "ノートパソコン", 120000);
_dataTable.Rows.Add("P002", "USBメモリ", 2500);
_dataTable.Rows.Add("P003", "モニター", 35000);
_dataTable.Rows.Add("P004", "マウス", 4500);
_dataTable.Rows.Add("P005", "キーボード", 7800);
dataGridView1.DataSource = _dataTable;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
}
/// <summary>
/// 「Excel出力」ボタンのクリックイベント
/// </summary>
private void btnExportExcel_Click(object? sender, EventArgs e)
{
// 出力先のファイルパスをユーザーに指定させる(SaveFileDialog)
using var sfd = new SaveFileDialog();
sfd.Filter = "Excel ブック (*.xlsx)|*.xlsx";
sfd.FileName = $"商品リスト_{DateTime.Now:yyyyMMddHRmmss}.xlsx";
if (sfd.ShowDialog() != DialogResult.OK) return;
// Excel関係のオブジェクトは try-finally で確実に解放するために外側で宣言します
Excel.Application? xlApp = null;
Excel.Workbooks? xlBooks = null;
Excel.Workbook? xlBook = null;
Excel.Sheets? xlSheets = null;
Excel.Worksheet? xlSheet = null;
Excel.Range? xlRange = null;
try
{
// 1. メモリ上に DataGridView のデータを2次元配列として抽出する(ヘッダー行+データ行)
int rowCount = dataGridView1.RowCount;
int colCount = dataGridView1.ColumnCount;
// Excelのインデックスに合わせて 1始まりの配列を作成
object[,] dataArray = new object[rowCount + 1, colCount];
// ヘッダー(列名)の格納
for (int c = 0; c < colCount; c++)
{
dataArray[0, c] = dataGridView1.Columns[c].HeaderText;
}
// セルデータの格納
for (int r = 0; r < rowCount; r++)
{
for (int c = 0; c < colCount; c++)
{
dataArray[r + 1, c] = dataGridView1.Rows[r].Cells[c].Value ?? "";
}
}
// 2. Excelの起動とブックの作成
xlApp = new Excel.Application();
xlApp.Visible = false; // 処理中にExcel画面をピコピコ表示させない(高速化)
xlApp.DisplayAlerts = false; // 上書き警告などを非表示にする
xlBooks = xlApp.Workbooks;
xlBook = xlBooks.Add();
xlSheets = xlBook.Sheets;
xlSheet = (Excel.Worksheet)xlSheets[1];
// 3. 💡【高速化の肝】開始セルと終了セルからRangeを特定し、2次元配列を一括転送
Excel.Range startCell = (Excel.Range)xlSheet.Cells[1, 1];
Excel.Range endCell = (Excel.Range)xlSheet.Cells[rowCount + 1, colCount];
xlRange = xlSheet.Range[startCell, endCell];
// 配列を代入(これだけで全セルに一瞬で値が入ります)
xlRange.Value = dataArray;
// 見栄えの調整:列幅を文字量に合わせて自動フィット
xlRange.Columns.AutoFit();
// 4. ファイルの保存
xlBook.SaveAs(sfd.FileName);
MessageBox.Show("Excelファイルの出力が完了しました!", "成功", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"Excel出力中にエラーが発生しました:\n{ex.Message}", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
// 5. ⭕【最重要】生成されたオブジェクトを逆順で完全に解放する(finallyブロック)
if (xlBook != null)
{
xlBook.Close(false); // 保存は終わっているので変更破棄で閉じる
}
if (xlApp != null)
{
xlApp.Quit(); // Excelアプリケーションを終了
}
// COMリソースの明示的解放(解放を怠るとEXCEL.EXEがプロセスに残ります)
if (xlRange != null) Marshal.ReleaseComObject(xlRange);
if (xlSheet != null) Marshal.ReleaseComObject(xlSheet);
if (xlSheets != null) Marshal.ReleaseComObject(xlSheets);
if (xlBook != null) Marshal.ReleaseComObject(xlBook);
if (xlBooks != null) Marshal.ReleaseComObject(xlBooks);
if (xlApp != null) Marshal.ReleaseComObject(xlApp);
// ガベージコレクションを強制して完全にメモリから抹消する
GC.Collect();
GC.WaitForPendingFinalizers();
}
}
}
}

サンプルアプリケーション

DataGridView_Export_Excel.zip
※ サンプルアプリケーションは、.NET 10 をターゲットに作成しています。

留意すべき注意点と実装の背景

Excel 連携機能を実務の環境で安定して稼働させるため、考慮すべきポイントが2つあります。

ドットの連続呼出による参照リークの回避

COM オブジェクトを操作する際、以下のようにプロパティを連続して呼び出す記述(点つなぎ)は避けてください。

// ❌ プロセスが残る原因となる記述例
xlApp.Workbooks.Add().Sheets[1].Cells[1, 1] = "値";

このように1行で記述すると、途中の WorkbooksSheets といった中間オブジェクトへの参照を C# 側が名前付きの変数として保持できないため、後から個別に ReleaseComObject で解放することが不可能になります。

コードの記述量は増えますが、サンプルコードのように各階層のオブジェクトを必ず個別の変数(xlBooksxlBook など)に一度受けてから操作し、finally ブロックでそれぞれを確実に解放する手順をとる必要があります。

実行環境への依存(Excelの必要性)

この実装方法は、プログラムを実行するクライアントPCに、本物の Microsoft Excel(デスクトップ版)がインストールされていることが前提となります。内部でインストール済みの Excel エンジンを直接呼び出して操作しているためです。

もし、Webサーバー上での自動処理アプリであったり、Excel がインストールされていない軽量な環境でも動作させたい場合は、この COM 操作方式ではなく、オープンソースのライブラリ(ClosedXMLMiniExcel など)を導入し、OpenXML 形式のファイルを直接バイナリ生成して出力するアプローチを検討する必要があります。

まとめ

今回は、DataGridView のデータを Excel ファイルへ出力するための基本的な実装手順について解説しました。
Excel 自体の起動やプロセス通信に伴う数秒の処理時間は発生しますが、2次元配列を用いた一括データ転送を行うことで、セルごとの細かな書き込み負荷を抑えることができます。

  • using Excel = Microsoft.Office.Interop.Excel; で名前空間を整理する
  • object[,] の2次元配列にデータをまとめ、Range.Value へ一括代入する
  • プロセスの残存を防ぐため、中間オブジェクトも含めて finally 内で確実に ReleaseComObject を実行する

業務システムにおいて簡易的なデータエクスポート機能を実装する際の参考になりましたら幸いです。


💬 コメント