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 を引くことになります。

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

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

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

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

巨大な XLSX を開く

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

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

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

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

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

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

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

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

CSV も同様です。

描画は件数に依存しない

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

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

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

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

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

次に読む

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