1万行のデータを1秒以内で送信するテクニックです。
ポイント
大切なポイント
1.EXCELとの通信回数を最小限にする。
2.データ全体を1つの2次元配列オブジェクトへ加工し、転送を1回で済ませる。
実行イメージ
1万行 ✕ 8列 のデータをEXCEL出力するための時間
計測時間 2.737ミリ秒
※ 上記のGIFは等速ですが、EXCELオブジェクトの作成や書式設定など
転送以外の処理も含んでいるので全体で2秒程度掛かっています。
【実行環境】
・CPU i7-7000 3.6GHz
・メモリ 16GB
DataGridViewのDataSourceを2次元配列へ加工し、一回でEXCELへ転送する
private void SetExcelList(Excel.Worksheet CurrentSheet)
{
// dataGridViewからデータソースを取得する。
BindingSource bs = (BindingSource)dataGridView.DataSource;
var fullDataSource = (List<Object>)bs.DataSource;
// 行数
int rows = fullDataSource.Count;
// 0行ならなにもしない
if (rows == 0) return;
// 列数
IList<PropertyInfo> props = new List<PropertyInfo>(
fullDataSource[0].GetType().GetProperties());
int cols = props.Count;
// 一回で転送するための2次元オブジェクト
object[,] ListToExcel = new object[rows, cols];
int row = 0;
foreach (object rowItem in fullDataSource)
{
int col = 0;
foreach (PropertyInfo prop in props)
{
ListToExcel[row, col] = prop.GetValue(rowItem, null);
col++;
}
row++;
}
// 出力先を指定して転送
Excel.Range range = CurrentSheet.Cells[2, 1]; //1行目はヘッダーを表示するので2行目に先頭行を表示
range = range.get_Resize(rows, cols);
range.Value = ListToExcel;
}
参考:EXCELシートの準備
public void SendToExcel()
{
if (dataGridView.Rows.Count == 0) return;
//Excelオブジェクトの初期化
Excel.Application ExcelApp = null;
Excel.Workbooks exCurrentBooks = null;
Excel.Workbook exCurrentBook = null;
Excel.Worksheet exCurrentSheet = null;
try
{
//Excelシートのインスタンスを作る
ExcelApp = new Excel.Application();
exCurrentBooks = ExcelApp.Workbooks;
exCurrentBook = ExcelApp.Workbooks.Add();
exCurrentSheet = exCurrentBook.Worksheets[1];
exCurrentSheet.Select(Type.Missing);
// ヘッダー
SetExcelHeader(exCurrentSheet);
// リスト
SetExcelList(exCurrentSheet);
// 書式
SetExcelFormat(exCurrentSheet);
}
finally
{
exCurrentSheet.Cells.Columns.AutoFit();
//excel表示
ExcelApp.Visible = true;
// Excelオブジェクトの開放
Marshal.ReleaseComObject(exCurrentSheet);
exCurrentSheet = null;
Marshal.ReleaseComObject(exCurrentBook);
exCurrentBook = null;
Marshal.ReleaseComObject(exCurrentBooks);
exCurrentBooks = null;
// EXCELオブジェクトのガベージ コレクトを強制。
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
// Application オブジェクトを破棄。
Marshal.ReleaseComObject(ExcelApp);
ExcelApp = null;
// Application オブジェクトのガベージ コレクトを強制。
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
}
}
参考:EXCELのヘッダー
private void SetExcelHeader(Excel.Worksheet CurrentSheet)
{
int cols = dataGridView.Columns.Count;
string[] Header = new string[cols];
int col = 0;
foreach (DataGridViewColumn column in dataGridView.Columns)
{
Header[col] = column.HeaderText;
col++;
}
Excel.Range rangeHeader = (Excel.Range)CurrentSheet.Cells[1, 1];
rangeHeader = rangeHeader.get_Resize(1, cols);
rangeHeader.Value = Header;
rangeHeader.Interior.Color = Color.DarkBlue;
rangeHeader.Font.Color = Color.White;
rangeHeader.Font.Bold = true;
}
参考:EXCELの書式
private void SetExcelFormat(Excel.Worksheet CurrentSheet)
{
int col = 0;
foreach (DataGridViewColumn column in dataGridView.Columns)
{
string currentLetter = IntToChars(col);
Excel.Range range = CurrentSheet.get_Range(currentLetter + ":" + currentLetter);
if (column.DefaultCellStyle.Format == "MM/dd")
{
// グリッド上での日付を年月日表示
range.NumberFormatLocal = "yyyy/MM/dd";
}
else if (column.DefaultCellStyle.Format == "C")
{
// グリッド上での通貨を円で表示
range.NumberFormatLocal = "\\ #,##0";
}
else
{
// バーコードの様な長い数字を省略せずに表示する
range.NumberFormatLocal = "0";
}
col++;
}
}
// 列番号の数値をEXCELの列番号(A列とか)へ変換(A列~ZZ列までの範囲)
private string IntToChars(int value)
{
int i上1桁 = (int)Math.Floor(value / 26.0) - 1;
int i下1桁 = (int)(value % 26.0);
string c上1桁 = "";
if (i上1桁 >= 0 )
{
((char)(i上1桁 + 65)).ToString();
}
string c下1桁 = ((char)(i下1桁 + 65)).ToString();
return c上1桁 + c下1桁;
}
サンプルソースの公開