V4 V5

10 万行・100 万行を扱う場合の具体的な手順です。前提となる性能特性は 性能とメモリ にまとめてあります。

まず押さえておくこと — シートを大きく作ること自体はコストではありません。 104 万行のシートを作っても、値を書くまでメモリはほぼゼロです。遅くなる原因は ほぼ常に「書き方」のほうにあります。

全件をメモリに載せる場合

数十万行までなら、素直に全部書いてしまうのが最も簡単で速い方法です。

using unvell.ReoGrid.Core;
using unvell.ReoGrid.Core.Data;
using unvell.ReoGrid.Core.Style;

// 1. 書式は行・列単位で先に決める
ws.SetColumnStyle(1, new StyleRecord { TextAlign = HAlign.Right });

// 2. 値を書く。型が分かっているなら SetText / SetNumber が最も軽い
for (int i = 0; i < rows.Count; i++)
{
	ws.SetText(i, 0, rows[i].Name);
	ws.SetNumber(i, 1, rows[i].Amount);
}

// 3. 数式は最後に。まとめて書いてから 1 回だけ再計算する
ws.SetFormula(rows.Count, 1, $"SUM(B1:B{rows.Count})");
ws.Recalculate();

順序が重要です。

  1. 書式を先に、行・列単位で。 セルのループで書式を設定すると、セル数ぶんの エントリが実体化してメモリが跳ね上がります
  2. 値は型付きの Set* で。 SetObjectValue は型判定を通るぶん遅くなります
  3. 数式は最後に、Recalculate() は 1 回だけ。 SetFormula はそのセルと依存先を その場で再計算します

この 3 点を守れば、10 万行の書き込みは通常 1 秒未満で終わります。

表示された行だけを読み込む

DB に数百万件あり、全部をメモリに載せたくない場合は IRowDataSource を実装します。 画面に映る行だけが読み込まれます。

/// <summary>表示された行だけを DB から取り出してシートに載せる。</summary>
public sealed class QueryRowSource : IRowDataSource
{
	private readonly Worksheet _sheet;
	private readonly HashSet<int> _loaded = new();

	public QueryRowSource(Worksheet sheet) => _sheet = sheet;

	public void EnsureRows(int firstRow, int lastRow)
	{
		for (int r = firstRow; r <= lastRow; r++)
		{
			if (!_loaded.Add(r)) continue;      // 既に用意済みの行は読み飛ばす
			foreach (var (col, value) in FetchRow(r))
				_sheet.SetObjectValue(r, col, value);
		}
	}

	private static IEnumerable<(int Col, object? Value)> FetchRow(int row)
		=> [(0, $"row {row}")];                 // 実際はここで DB を引く
}

シートに割り当てます。

ws.RowDataSource = new QueryRowSource(ws);

// コントロールは表示直前に可視範囲ぶんを自動で呼ぶ。
// ヘッドレスや先読みしたいときだけ明示的に呼ぶ
ws.EnsureRowsLoaded(0, 99);

コントロールは描画の直前に、可視範囲ぶんの EnsureRowsLoaded を毎回呼びます。 既に用意した行は読み飛ばす実装にしておいてください(上の例の _loaded がそれです)。 そうしないとスクロールのたびに DB を引くことになります。

行数(AddWorksheetrows)は、実際の件数に合わせて先に決めておきます。 スクロールバーの長さがこれで決まります。

向いている場面・向いていない場面

向き
一覧を眺める・スクロールする
特定の行を検索して飛ぶ○(飛び先を EnsureRowsLoaded してから MoveTo
全体の集計を数式で出す✗ 読み込まれていない行は空セル扱いになる
ソート・フィルタ✗ シート上のデータしか見ない

集計・ソート・フィルタが要るなら、全件を載せるか、DB 側で処理してから 結果だけをシートに出してください。

巨大な XLSX を開く

ファイル側も部分読み込みにできます。シートは即座に使える状態になり、 セルは描画されたぶんだけファイルから読み込まれます。

using var book = XlsxReader.OpenVirtual("large.xlsx", SheetLoadMode.OnDemand);
control.LoadWorkbook(book.Workbook);      // そのままコントロールへ渡せる

VirtualWorkbookIDisposable です。ファイルとシートごとの一時ファイルを 開いたまま保持するため、ワークブックを差し替えるときや終了時に破棄してください。 破棄した後にアクセスすると、セルは空として読まれます。

保存・書き出しの前には MaterializeAll() を呼んでください。 呼ばずに書き出すと、 その時点で読み込まれていた範囲しか出力されません。

book.MaterializeAll();                    // 全シート・全行を実体化する
XlsxWriter.Write(book.Workbook, "out.xlsx");

MaterializeAll() は全件をメモリに載せるため、通常読み込みと同じメモリを使います (XLSX 入出力)。

書き出しはストリーミング

XLSX の書き出しは、ワークブック全体を XML として組み立ててから書くのではなく、 逐次書き出します。行数に比例したメモリしか使いません。 100 万行を書き出しても、メモリ使用量はシートが載っているぶんだけです。

CSV も同様です。

描画は件数に依存しない

シートに何行あっても、描画されるのは画面に映っているセルだけです。 100 万行のシートでもスクロールの滑らかさは変わりません。

同じ理由で、外観のカスタマイズウィンドウ枠の固定 を使っても件数によるコストはかかりません。

詰まったときに見るところ

症状よくある原因
書き込みが遅いセルのループで書式を設定している
メモリが想定より多い同上、または少しずつ違う書式を大量に作っている
初回表示が遅い0 .. RowCount を素朴に走査している
スクロールが引っかかるEnsureRows が毎回データを取りに行っている

ws.DistinctStyleCount が想定より大きい場合は書式の作りすぎです。

次に読む

ページの内容は役に立ちましたか?