みなさんこんにちは!ヒロポンです!
つい先日、後輩に相談されまして。「DataGridView に行を足したいんすけど、grid.Rows.Add が例外吐くんですよ」と。ん? DataSource バインドしてない?? ……案の定でした。
業務の入力画面やマスタ編集で、やりたいのは「グリッドに1行足す」だけ。なのに思った通りに入らない。地味にハマるんですよね、これ。俺も昔やらかしました。
今回は C# DataGridView 行追加 のやり方を、業務SEが現場で踏む3パターンに絞って書いていきます。Rows.Add で直接足す、DataSource にバインドしてる時、BindingList<T> 経由。この3つ。全部コピペで動くコードで出します。
💡 DataGridView の クリックの出し分け(左/右/ダブル)は C# WinForms DataGridView のクリック3アクション、行選択イベントの使い分けは C# DataGridView 行選択イベント3種 にまとめてます。今回はその実務クラスタの 「行を足す」編 です。
結論: 行追加は「バインドしてるか」で3つに分かれる
先に結論。DataGridView の行追加でやることは、「DataSource をバインドしてるかどうか」で書き方が変わる。これだけです。迷ったらこの表を見て選べばOK。

ひとことで言うと、DataGridView は データソースをバインドした瞬間、grid 側の直接追加を禁止する。だから「足す相手」を間違えると例外で落ちる。ここさえ押さえれば9割解決です。
なぜ行追加でつまずくのか
こんな単純な操作で、なんでつまずくのか。理由は2つ。
1つは、DataSource を設定すると grid.Rows.Add が使えなくなること。バインド中の grid は「表示はソースに任せる」モードになります。grid に直接足そうとすると InvalidOperationException で蹴られる。
もう1つは、AllowUserToAddRows の新規入力行。末尾に出る空っぽの行のことなんですけど、これが有効だと Rows.Count に幻の1行が混ざる。件数チェックで「あれ、1件多い??」ってなるやつです。
この2つは後半のハマりポイントで潰します。まずは動くコードから。
最短対処: コピペで動く3つの行追加
動作確認メモ: コードはすべて C# 7.3(.NET Framework 4.7.2)でビルド確認済みです。
DataTableへの追加とBindingList<T>の変更通知は実機ログで裏取りしてます(このあと実行結果を載せます)。一方 DataGridView 自体の画面表示が絡む部分は、Windows + Visual Studio の実環境で確認してください(Linux の検証環境は画面サーバーが無く、grid の描画までは再現できないため)。
パターン①: 非バインド時 — Rows.Add / Rows.Insert
DataSource を使わず、grid に手で列と行を組むパターン。一番シンプルです。
// 先に列を用意(非バインド)
grid.Columns.Add("Id", "ID");
grid.Columns.Add("Name", "名前");
// 末尾に1行追加(値をそのまま渡す)
grid.Rows.Add("1", "佐藤");
// 空行を足してからセルに代入してもいい
int idx = grid.Rows.Add();
grid.Rows[idx].Cells["Name"].Value = "鈴木";
// 指定位置に差し込みたい時は Insert
grid.Rows.Insert(0, "0", "先頭に入る行");
Rows.Add が末尾、Rows.Insert(index, ...) が指定位置。手組みの一覧なら、これでいい感じに足せます。
パターン②: DataTable バインド時 — ソース側に足す
grid.DataSource = dt でバインドしてる場合は、grid じゃなくて DataTable 側に足す。「grid に足してるのに増えない??」ってなるやつ、だいたいこれです。一番ハマる人が多い。
// DataSource にバインド済みの DataTable を取り出して足す
var dt = (DataTable)grid.DataSource;
dt.Rows.Add(3, "高橋"); // grid に自動で反映される
// DataRow を作って詰めてから Add でもOK
var row = dt.NewRow();
row["Id"] = 4;
row["Name"] = "渡辺";
dt.Rows.Add(row);
ソース(dt)に足せば、grid は勝手に追従します。こんな感じで、SQL から引いた表をそのまま出してる画面なら、この形が一番素直。
実行結果(DataTable 側に足した中身を確認):

パターン③: BindingList / BindingSource 経由
DataTable じゃなくて「クラスで持ちたい」時は BindingList<T> を使います。型がある分、後の保守が楽になる。
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
}
// BindingSource を噛ませてバインド
var list = new BindingList<Employee>();
var source = new BindingSource { DataSource = list };
grid.DataSource = source;
// list に Add すると grid に即反映される
list.Add(new Employee { Id = 5, Name = "中村" });
List<T> だと追加が grid に反映されない。でも BindingList<T> は変更通知を持ってるんで、Add した瞬間に grid が更新されます。こんな感じで、クラスベースの画面はこれが定番。
実行結果(Add で変更通知が発火しているのを確認):

ハマりポイント: 知らないと一晩飛ぶやつ
コピペで動くのは確認した。ここからは、知らずに本番へ流すと夜中に呼び出されるやつを2つ。
ハマり①: DataSource 設定済みで grid.Rows.Add → InvalidOperationException
冒頭の後輩がハマったのがこれ。俺も2年目で同じ例外を出して、30分ほど原因が分からず固まりました。
var dt = new DataTable();
dt.Columns.Add("Id", typeof(int));
dt.Columns.Add("Name", typeof(string));
var grid = new DataGridView();
grid.DataSource = dt; // バインドした
grid.Rows.Add(1, "佐藤"); // ← ここで InvalidOperationException
例外メッセージはこう出ます。
System.InvalidOperationException:
データ連結されている場合、DataGridView コントロールの行コレクションをプログラムで変更することはできません。
これは .NET の仕様で、バインド中の grid.Rows.Add は必ず弾かれます。直し方はパターン②と同じ。grid じゃなく dt.Rows.Add(1, "佐藤") 側に足す。この「ソース側に足す」だけは実環境でも確認済みなんで、安心して使ってOK。バインド中は「grid は表示係、追加はソース係」と役割が分かれてる。こう覚えておくと迷いません。
ハマり②: AllowUserToAddRows の新規入力行で件数が1ズレる
AllowUserToAddRows(既定 true)が有効だと、末尾に空の入力行が出ます。これ自体は便利なんですけど、Rows.Count にこの1行が含まれる。
// AllowUserToAddRows = true(既定)のまま件数を数えると…
int count = grid.Rows.Count; // データ3行でも「4」が返る(+新規入力行)
// データ行だけ数えたい時は IsNewRow を除外する
int real = grid.Rows.Cast<DataGridViewRow>().Count(r => !r.IsNewRow);
// そもそも入力行が要らない画面なら、最初にオフにしておく
grid.AllowUserToAddRows = false;
「件数が1多い」「最後の行が空でループがコケる」系のバグ。だいたいこの新規入力行が犯人です。表示専用のグリッドなら、最初に false にしておく。件数もいい感じに素直になります。
現場メモ: マスタ編集画面で時間を溶かさないために
流通系の基幹システムを保守してた頃、商品マスタの編集画面で「行を1件足す」ボタンが例外を吐く、という問い合わせが来たことがあって。数千行のマスタを DataTable でバインドしてる画面でした。
原因はまさにハマり①。前任者が grid.Rows.Add で書いてて、テスト時はバインド前に呼んでたから動いてた。本番でデータを先に読むようにしたら、バインド後に Rows.Add が走って落ちる。時限式のバグだったんですよね。
直したのは1行です。grid.Rows.Add(...) を ((DataTable)grid.DataSource).Rows.Add(...) に変えただけ。ボタン1個の修正でしたが、原因にたどり着くまでが長かった。
教訓は、「この grid はバインドされてるか?」を最初に確認する。これだけで行追加まわりの事故は8割減ります。バインド済みなら grid に直接触らない。それだけ。
まとめ
DataGridView の行追加、整理するとこう。
- ① Rows.Add / Rows.Insert — 非バインド(手組み)の時。
grid.Rows.Add(...) - ② DataTable 側に足す — DataTable をバインドしてる時。
((DataTable)grid.DataSource).Rows.Add(...) - ③ BindingList
— クラスで持つ時。 list.Add(new Employee{...})
ハマりは「バインド済みで grid.Rows.Add すると例外」と「新規入力行で件数が1ズレる」の2つ。バインドしてるかを先に確認して、してるならソース側に足す。これで詰まることはほぼ無くなります!!
よくある質問
Q1. DataSource をバインドした DataGridView に grid.Rows.Add で行を足せますか?
A. 足せません。InvalidOperationException(「データ連結されている場合…」)が出ます。バインド中はソース側(DataTable や BindingList)に Add すると、grid に自動反映されます。
Q2. DataGridView の AllowUserToAddRows とは何ですか?
A. 末尾に表示される新規入力用の空行(プレースホルダ)を出すかどうかの設定です。既定は true で、有効だと Rows.Count にその1行が含まれて件数が1件ズレます。表示専用なら false にしておくのが無難。
Q3. BindingList と DataTable、行追加ではどちらを使うべきですか?
A. クラス(型)で扱いたいなら BindingList<T>、SQL から引いた表をそのまま出すなら DataTable が素直です。どちらもソース側に Add すれば grid に反映されます。逆に List<T> だと反映されないので注意。
Q4. Rows.Add と Rows.Insert の違いは何ですか?
A. Rows.Add は末尾に追加、Rows.Insert(index, ...) は指定位置に差し込みます。どちらも非バインド(DataSource 未設定)の時に使うメソッドです。
Q5. なぜ BindingList を使うのか、List じゃダメなんですか?
A. List<T> は「中身が変わった」という通知を grid に送れないからです。Add しても画面が更新されない。BindingList<T> は変更通知(IBindingList)を持ってるので、追加・削除が即グリッドに反映されます。クラスで持つならこっち一択です。
ここまでで DataGridView の行追加は押さえられたはず。
ただね、こういう「バインドしてるか1回確認すれば3秒、しないと30分」の勘どころって、現場で一個ずつ拾っていくしかないんですよね。で、こういう地味な事故対応を積み重ねてる業務SEほど、なぜか自分の市場価値を低く見積もりがち。「俺なんてただの保守要員だから」って。その自己評価の話を、下の関連記事の最後に置いときます。技術の合間の箸休めにどうぞ。
関連記事
- C# WinForms DataGridView のクリック3アクション — 行を足したあと「クリックで何をするか」。左/右/ダブルクリックの出し分けを実装ベースで
- C# DataGridView 行選択イベント3種 — SelectionChanged / CellEnter / CurrentCellChanged の使い分け。行追加と並ぶ実務クラスタの選択編
- WinForms DataGridView の編集モード完全ガイド — 足した行を「どう編集させるか」。ReadOnly / EditMode / RowValidating の使い分け
- C# DataGridView の DataSource を後から変更する全パターン — バインド先を途中で差し替えたくなった時の応用編。行追加の前提知識として
- 業務SE が市場価値を語る時に避けるべき3つの自己否定 — 口癖が「俺なんてただの業務SEだから」になってる人へ。技術記事の箸休めにどうぞ
以上!
同じところで詰まってる人いたら、どんどんシェア待ってるぜ!!
執筆者
バイブス父さん — 業務 SE 7 年 (正社員 2 / フリーランス 5)。 現職は SEO 直轄部の AI アドバイザー兼 PL、 副業で中小 SIer の CTO。 SES 複数社・フリーランスエージェント複数経由の経験ベースで「業務 SE 視点」 の技術 + キャリア記事を書いています。
🐦 X: @hiro_progra0524 (日々の現場メモ更新中)
📝 About Me で経歴詳細を見る


コメント