LoginSignup
5
7

More than 1 year has passed since last update.

C# DataGridViewからExcelへ大量データを高速で出力する

Last updated at Posted at 2022-09-30

1万行のデータを1秒以内で送信するテクニックです。

ポイント

大切なポイント
1.EXCELとの通信回数を最小限にする。
2.データ全体を1つの2次元配列オブジェクトへ加工し、転送を1回で済ませる。

実行イメージ

EXCEL出力.gif

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 i1 = (int)Math.Floor(value / 26.0) - 1;
    int i1 = (int)(value % 26.0);

    string c1 = "";
    if (i1 >= 0 )
    {
        ((char)(i1 + 65)).ToString();
    }
    string c1 = ((char)(i1 + 65)).ToString();

    return c1 + c1;
}

サンプルソースの公開

5
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
7