DataGridView:Excel データをインポート(読込)する(サンプルあり)

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

Excel で作成された商品リストやマスターデータをシステムに読み込んで DataGridView に一括表示させたいという要件は、業務アプリケーションの開発において多く発生します。

以前紹介した「DataGridView:データを Excel に出力する」の関連記事として、今回はその逆の処理である Excel ファイルをインポート(読込)して DataGridView に表示する方法 を解説します。

取り扱うデータ構造はエクスポート時と全く同じ「商品コード(Code)」、「商品名(Name)」、「価格(Price)」の3列構成をベースにし、COM オブジェクトの解放手順までをまとめて紹介します。

事前準備:COM 参照の追加と設定

エクスポート時と同様に、C# から Excel を直接コントロールするために、プロジェクトへの参照追加が必要です。

  1. 変更対象のプロジェクトを右クリックして「追加」➡️「COM 参照」を選択します。
  2. 検索ボックスに「Excel」と入力し、「Microsoft Excel 16.0 Object Library」(環境によってバージョンは異なります)にチェックを入れて追加します。
  3. 追加された参照(Microsoft.Office.Interop.Excel)を選択し、プロパティウィンドウで 「相互運用型の埋め込み」を「True」 に設定します。

実装コード

画面に DataGridView(dataGridView1)と、読込を開始するためのボタン(btnImportExcel)が配置されているものとします。

Form1.cs

Form1.cs
using System.Data;
using System.Runtime.InteropServices;
// 長い名前空間を省略するため、ファイルの最上部で別名を定義します
using Excel = Microsoft.Office.Interop.Excel;
namespace DataGridView_Import_Excel;
public partial class Form1 : Form
{
private DataTable _dataTable = new DataTable();
public Form1()
{
InitializeComponent();
InitializeDataGridView();
// ボタンのクリックイベントを購読
btnImportExcel.Click += btnImportExcel_Click;
}
private void InitializeDataGridView()
{
// エクスポート時と同じデータ構造(3列)で DataTable を定義
_dataTable.Columns.Add("Code", typeof(string));
_dataTable.Columns.Add("Name", typeof(string));
_dataTable.Columns.Add("Price", typeof(int));
dataGridView1.DataSource = _dataTable;
dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
}
/// <summary>
/// 「Excel読込」ボタンのクリックイベント
/// </summary>
private void btnImportExcel_Click(object? sender, EventArgs e)
{
// 読込元のファイルをユーザーに選択させる(OpenFileDialog)
using var ofd = new OpenFileDialog();
ofd.Filter = "Excel ブック (*.xlsx)|*.xlsx|従来の Excel ブック (*.xls)|*.xls";
if (ofd.ShowDialog() != DialogResult.OK) return;
// 既存のグリッドデータを一度クリア
_dataTable.Rows.Clear();
// Excel関係のオブジェクトは try-finally で確実に解放するために外側で宣言します
Excel.Application? excelApp = null;
Excel.Workbooks? workbooks = null;
Excel.Workbook? workbook = null;
Excel.Sheets? sheets = null;
Excel.Worksheet? worksheet = null;
Excel.Range? usedRange = null;
try
{
excelApp = new Excel.Application();
workbooks = excelApp.Workbooks;
workbook = workbooks.Open(ofd.FileName);
sheets = workbook.Sheets;
worksheet = (Excel.Worksheet)sheets[1]; // 1番目のシートを取得
usedRange = worksheet.UsedRange;
// セルを1つずつループで読むと遅いため、2次元配列として一括で読み込みます
object[,] dataArray = (object[,])usedRange.Value2;
int rowCount = dataArray.GetLength(0);
int colCount = dataArray.GetLength(1);
// 1行目はヘッダー(列名)のため、データ行である2行目からループを開始
for (int r = 2; r <= rowCount; r++)
{
// 配列が Null の場合や列数が足りない場合を考慮した安全な値の取得
string code = dataArray[r, 1]?.ToString() ?? "";
string name = dataArray[r, 2]?.ToString() ?? "";
// 数値型(Price)への安全なパース
int price = 0;
if (dataArray[r, 3] != null)
{
int.TryParse(dataArray[r, 3].ToString(), out price);
}
// データの追加(空行をスキップしたい場合は、ここで適宜条件を挟みます)
if (!string.IsNullOrEmpty(code) || !string.IsNullOrEmpty(name))
{
_dataTable.Rows.Add(code, name, price);
}
}
MessageBox.Show("Excel データを正常に読み込みました。", "インポート完了", MessageBoxButtons.OK, MessageBoxIcon.Information);
}
catch (Exception ex)
{
MessageBox.Show($"エラーが発生しました:\n{ex.Message}", "エラー", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
finally
{
// 参照リーク(バックグラウンドに Excel が残る現象)を防止する解放処理
if (workbook != null)
{
workbook.Close(false); // 上書き保存せずに閉じる
}
if (excelApp != null)
{
excelApp.Quit();
}
// COM オブジェクトを割り当てた逆順で確実に解放
if (usedRange != null) Marshal.ReleaseComObject(usedRange);
if (worksheet != null) Marshal.ReleaseComObject(worksheet);
if (sheets != null) Marshal.ReleaseComObject(sheets);
if (workbook != null) Marshal.ReleaseComObject(workbook);
if (workbooks != null) Marshal.ReleaseComObject(workbooks);
if (excelApp != null) Marshal.ReleaseComObject(excelApp);
}
}
}

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

↓ アプリケーションを実行します。

↓ サンプルアプリケーションに同梱の、「商品リスト.xlsx」を選択します。

↓ エクセルデータの読み込みが完了し、DataGridView にデータが表示されます。

DataGridView_Import_Excel.zip

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

実装のポイント

1. 二次元配列(object[,])への一括代入による高速化

Excel データを読み込む際、worksheet.Cells[r, c].Value のように、セルの値を直接 for ループで何度も見に行く実装は実務では避けるべきです。セルの件数が増えるほど C# と Excel(COM)間の通信オーバーヘッドが膨らみ、画面がフリーズする原因になります。

今回のコードでは、worksheet.UsedRange.Value2 を使ってシート上の全データをメモリ内の object[,](2次元配列)に一括取得しています。配列からデータを回す形にすることで、大量データであっても短時間で DataGridView に読み込めるようになります。

⚠️ 注意点:Excel の2次元配列は「1」から始まる
C# の通常の配列(0 スタート)と異なり、Excel の Range から取得した object[,] 配列のインデックスは 1 からスタートします。 そのため、今回の列指定も dataArray[r, 1](商品コード)、dataArray[r, 2](商品名)のように、1 を起点に指定してループを回します。

2. データ型(特に数値型)の安全な型変換

Excel ファイルを読み込む際、価格(Price)の列にカンマが入っていたり、予期せぬ文字列や空欄が混じっていたりすると、単純な型変換コードではシステムがクラッシュします。 バインド先である DataTable の Price 列は int 型として定義されているため、int.TryParse を挟んで安全に数値へ変換してから行を追加していくのが、実務における実装の定石です。

3. プロセスの残存を防ぐ “finally” での COM 解放

エクスポートの処理と同様に、読込処理が終わった後にタスクマネージャーの裏で EXCEL.EXE プロセスが残り続けるのを防ぐため、finally ブロックで例外が発生した場合でも確実に Marshal.ReleaseComObject が走る構造にしています。途中で経由した WorkbooksSheets といった中間オブジェクトも漏れなく変数に受けて個別に解放を行います。

まとめ

今回は、エクスポート記事と同じデータ構造を対象に、Excel ファイルから「商品コード」「商品名」「価格」を安全かつ高速にインポートして DataGridView にバインドする手順について解説しました。

双方向のエクスポート・インポート機能をセットでマスターしておくことで、現場のユーザーが使い慣れた Excel 帳票とシステムをシームレスに繋ぐ、実用的な業務アプリケーションを構築できるようになります。

  • UsedRange.Value2 を使い、全セルデータを object[,] の2次元配列に一括格納して高速化する
  • Excel 由来の2次元配列は 0 ではなく 1 からインデックスが始まる点に配慮する
  • int.TryParse を用いて、Excel 側の予期せぬ文字入力からデータ型の整合性を守る
  • アプリ終了後も Excel プロセスが残るのを防ぐため、finally で割当オブジェクトを逆順に完全解放する

Excel 連携画面を設計する際の参考になりましたら幸いです。


💬 コメント