C# DataGridViewにDataTableを表示・更新する方法|初心者向けサンプルコード付き
はじめに
C#のWindows Formsアプリで表形式のデータを表示したいときによく使われるのが、DataGridViewとDataTableです。
DataGridViewは画面上に表を表示するためのコントロールで、DataTableはメモリ上で表形式のデータを管理するためのクラスです。この2つを組み合わせることで、データベースの検索結果やCSVの読み込み結果、アプリ内で作成した一覧データなどを簡単に画面へ表示できます。
特に初心者の方は、次のような場面で悩みやすいです。
「DataTableを作ったけれどDataGridViewに表示されない」
「DataGridViewで編集した値はDataTableに反映されるのか分からない」
「DataTableの追加・更新・削除をどう書けばよいか分からない」
「データベースの内容をDataTable経由でDataGridViewに表示したい」
この記事では、C#のDataGridViewにDataTableを表示する基本から、編集内容の反映、行の追加・更新・削除、データベース連携、よくあるエラーの対処法まで、初心者向けにサンプルコード付きで解説します。
1. C#のDataGridViewにDataTableを表示する基本
1-1. DataGridViewとDataTableの役割
DataGridViewは、Windows Formsで表形式のデータを画面に表示するためのコントロールです。Excelのように行と列でデータを表示でき、セルの編集、行の選択、列幅の調整なども行えます。
一方、DataTableはC#で表形式のデータを扱うためのクラスです。DataTableには列を表すDataColumnと、行を表すDataRowがあります。
例えば、社員一覧を扱う場合は次のようなイメージです。
C#DataTable table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
table.Rows.Add(1, "田中", 28);
table.Rows.Add(2, "佐藤", 35);
table.Rows.Add(3, "鈴木", 41);
このDataTableをDataGridViewに設定すると、画面上に一覧表として表示できます。
C#dataGridView1.DataSource = table;
C#でDataGridViewとDataTableを使う基本は、このように「DataTableにデータを作る」「DataGridViewのDataSourceに設定する」という流れです。
1-2. DataTableを使うメリット
DataTableを使うメリットは、データを表形式で扱いやすいことです。
例えば、次のような処理が簡単にできます。
C#DataTable table = new DataTable();
table.Columns.Add("ProductName", typeof(string));
table.Columns.Add("Price", typeof(int));
table.Rows.Add("ノートPC", 120000);
table.Rows.Add("マウス", 2500);
DataTableを使うと、行や列を自由に追加でき、データベースの検索結果をそのまま格納することもできます。
また、DataGridViewのDataSourceにDataTableを設定すると、DataTableの内容が自動的に画面へ反映されます。画面表示用のデータと、内部で管理するデータを分けて考えられるため、コードの見通しも良くなります。
1-3. DataGridView.DataSourceにDataTableを設定する流れ
DataGridViewにDataTableを表示する基本的な流れは次のとおりです。
C#private void Form1_Load(object sender, EventArgs e)
{
DataTable table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Age", typeof(int));
table.Rows.Add(1, "田中", 28);
table.Rows.Add(2, "佐藤", 35);
table.Rows.Add(3, "鈴木", 41);
dataGridView1.DataSource = table;
}
ポイントは、DataTableに列を追加してから行を追加することです。列を作成していない状態で行を追加しようとすると、データの格納先がないためエラーや意図しない動作の原因になります。
また、DataGridViewに表示するときは、最後に次の1行を書きます。
C#dataGridView1.DataSource = table;
この1行によって、DataTableの内容がDataGridViewにバインドされます。
1-4. 初心者がつまずきやすいポイント
初心者がよくつまずくポイントの1つは、DataTableを作っただけではDataGridViewに表示されないという点です。
次のコードでは、DataTableは作成されていますが、DataGridViewには表示されません。
C#DataTable table = new DataTable();
table.Columns.Add("Name", typeof(string));
table.Rows.Add("田中");
表示するには、必ずDataSourceに設定する必要があります。
C#dataGridView1.DataSource = table;
また、DataSourceに設定した後で列名を変更したい場合は、DataTableの列名ではなく、DataGridView側のHeaderTextを変更することもあります。
C#dataGridView1.Columns["Name"].HeaderText = "氏名";
さらに、編集した内容がすぐにDataTableへ反映されないように見える場合は、編集中のセルがまだ確定していない可能性があります。この場合は、EndEditを使って編集を確定します。
C#dataGridView1.EndEdit();
2. DataTableを作成してDataGridViewに表示するサンプル
2-1. Windows Formsプロジェクトの準備
まず、Visual StudioでWindows Formsアプリのプロジェクトを作成します。
フォームにDataGridViewを1つ配置し、名前をdataGridView1にしておきます。初期状態では、ツールボックスからDataGridViewをドラッグ&ドロップするだけで問題ありません。
フォームの読み込み時にデータを表示したい場合は、Form1_Loadイベントにコードを書きます。
C#private void Form1_Load(object sender, EventArgs e)
{
// ここにDataTable作成とDataGridView表示のコードを書く
}
DataTableを使うには、ファイルの先頭に次の名前空間を追加します。
C#using System.Data;
2-2. DataTableに列を追加する方法
DataTableに列を追加するには、Columns.Addを使います。
C#DataTable table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Department", typeof(string));
table.Columns.Add("Salary", typeof(int));
第1引数には列名、第2引数にはデータ型を指定します。
例えば、Idは整数なのでtypeof(int)、NameやDepartmentは文字列なのでtypeof(string)を指定しています。
列名はプログラム内部で使う名前です。画面上に表示する日本語の見出しは、後からDataGridView側で設定できます。
2-3. DataTableに行を追加する方法
DataTableに行を追加するには、Rows.Addを使います。
C#table.Rows.Add(1, "田中太郎", "営業部", 300000);
table.Rows.Add(2, "佐藤花子", "総務部", 280000);
table.Rows.Add(3, "鈴木一郎", "開発部", 350000);
Rows.Addに渡す値の順番は、Columns.Addで追加した列の順番と対応しています。
つまり、次の列定義の場合、
C#table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Columns.Add("Department", typeof(string));
table.Columns.Add("Salary", typeof(int));
行を追加するときは、次の順番で値を指定します。
C#table.Rows.Add(1, "田中太郎", "営業部", 300000);
順番を間違えると、意図しない列に値が入ったり、型の不一致でエラーになったりします。
2-4. DataGridViewにDataTableをバインドするコード
作成したDataTableをDataGridViewに表示するには、DataSourceに設定します。
C#using System;
using System.Data;
using System.Windows.Forms;
namespace DataGridViewDataTableSample
{
public partial class Form1 : Form
{
private DataTable employeeTable;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
employeeTable = new DataTable();
employeeTable.Columns.Add("Id", typeof(int));
employeeTable.Columns.Add("Name", typeof(string));
employeeTable.Columns.Add("Department", typeof(string));
employeeTable.Columns.Add("Salary", typeof(int));
employeeTable.Rows.Add(1, "田中太郎", "営業部", 300000);
employeeTable.Rows.Add(2, "佐藤花子", "総務部", 280000);
employeeTable.Rows.Add(3, "鈴木一郎", "開発部", 350000);
dataGridView1.DataSource = employeeTable;
}
}
}
このコードを実行すると、employeeTableの内容がdataGridView1に表示されます。
employeeTableをフィールドとして定義している理由は、後で追加・更新・削除などの処理を行うときにも同じDataTableを参照できるようにするためです。
2-5. 表示結果の確認
実行すると、DataGridViewには次のような内容が表示されます。
| Id | Name | Department | Salary |
|---|---|---|---|
| 1 | 田中太郎 | 営業部 | 300000 |
| 2 | 佐藤花子 | 総務部 | 280000 |
| 3 | 鈴木一郎 | 開発部 | 350000 |
この時点で、DataGridViewはDataTableの内容を表示しています。
DataGridView上でセルを編集できる設定になっていれば、セルの値を変更することもできます。編集した内容は、基本的にはバインド元であるDataTableにも反映されます。
3. DataGridViewの表示を見やすく設定する方法
3-1. 列名を変更する
DataTableの列名はプログラム内部で使いやすい英語名にしておき、画面上の見出しだけ日本語に変更することができます。
C#dataGridView1.Columns["Id"].HeaderText = "社員ID";
dataGridView1.Columns["Name"].HeaderText = "氏名";
dataGridView1.Columns["Department"].HeaderText = "部署";
dataGridView1.Columns["Salary"].HeaderText = "給与";
このようにすると、DataTableの列名はIdやNameのままですが、DataGridView上では「社員ID」「氏名」のように表示できます。
列名を直接日本語にすることもできます。
C#table.Columns.Add("社員ID", typeof(int));
table.Columns.Add("氏名", typeof(string));
ただし、プログラム内で列名を指定して処理する場合は、英語名の方が扱いやすいことが多いです。
3-2. 列幅を自動調整する
DataGridViewの列幅を自動調整するには、AutoSizeColumnsModeを設定します。
C#dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
Fillを指定すると、DataGridView全体の幅に合わせて列幅が自動調整されます。
内容に合わせて列幅を調整したい場合は、次のようにします。
C#dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells;
よく使われる設定は次のとおりです。
| 設定値 | 内容 |
|---|---|
| Fill | 表全体の幅に合わせて列幅を調整 |
| AllCells | ヘッダーとセルの内容に合わせて調整 |
| DisplayedCells | 表示中のセル内容に合わせて調整 |
| None | 自動調整しない |
画面いっぱいに表を表示したい場合は、Fillが使いやすいです。
3-3. 読み取り専用にする
DataGridViewで編集させたくない場合は、ReadOnlyをtrueにします。
C#dataGridView1.ReadOnly = true;
これで、ユーザーはセルの値を変更できなくなります。
特定の列だけ読み取り専用にすることもできます。
C#dataGridView1.Columns["Id"].ReadOnly = true;
例えば、社員IDのように変更されたくない列だけ読み取り専用にし、氏名や部署は編集可能にする、といった使い方ができます。
C#dataGridView1.Columns["Id"].ReadOnly = true;
dataGridView1.Columns["Name"].ReadOnly = false;
dataGridView1.Columns["Department"].ReadOnly = false;
dataGridView1.Columns["Salary"].ReadOnly = false;
3-4. 行追加・削除の可否を設定する
DataGridViewでは、ユーザーによる行追加や行削除を制御できます。
行追加を禁止するには、次のようにします。
C#dataGridView1.AllowUserToAddRows = false;
行削除を禁止するには、次のようにします。
C#dataGridView1.AllowUserToDeleteRows = false;
両方を禁止する場合は、次のように設定します。
C#dataGridView1.AllowUserToAddRows = false;
dataGridView1.AllowUserToDeleteRows = false;
一覧表示だけを目的とする画面では、行追加や行削除を禁止しておくと誤操作を防ぎやすくなります。
3-5. 選択モードを設定する
DataGridViewでは、セル単位で選択するか、行全体を選択するかを設定できます。
行全体を選択したい場合は、SelectionModeを設定します。
C#dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
複数行選択を禁止したい場合は、MultiSelectをfalseにします。
C#dataGridView1.MultiSelect = false;
よく使う設定をまとめると、次のようになります。
C#dataGridView1.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
dataGridView1.MultiSelect = false;
dataGridView1.AllowUserToAddRows = false;
dataGridView1.AllowUserToDeleteRows = false;
この設定にすると、ユーザーは1行ずつ選択でき、不要な行追加や削除を防げます。
4. DataGridView上で編集した内容をDataTableに反映する
4-1. DataGridViewとDataTableのデータ連携の仕組み
DataGridViewのDataSourceにDataTableを設定すると、DataGridViewはDataTableの内容を表示します。
C#dataGridView1.DataSource = employeeTable;
この状態では、DataGridViewとDataTableがバインドされています。
そのため、DataGridView上でセルの値を編集すると、バインド元であるDataTableにも変更が反映されます。
例えば、DataGridView上で「田中太郎」を「田中次郎」に変更した場合、employeeTable内の該当行のName列も変更されます。
ただし、セルを編集中の状態では、まだ値が確定していないことがあります。その場合、DataTableを確認しても古い値のままに見えることがあります。
4-2. セル編集後にDataTableへ反映されるタイミング
DataGridViewでセルを編集した値は、通常、セルの編集が終了したタイミングで反映されます。
例えば、次のような操作をしたときです。
Enterキーを押す
別のセルへ移動する
別の行へ移動する
フォーカスが移動する
逆に、セル内にカーソルが残っていて編集中の状態では、DataTable側にまだ反映されていない場合があります。
ボタンを押して変更内容を保存するような画面では、ボタンクリック時に編集を確定してからDataTableを確認するのが安全です。
C#private void buttonSave_Click(object sender, EventArgs e)
{
dataGridView1.EndEdit();
// この後にDataTableの内容を確認・保存する
}
4-3. 編集中の値を確定する方法
編集中の値を確定するには、DataGridView.EndEditを使います。
C#dataGridView1.EndEdit();
BindingSourceを使っている場合は、BindingSource.EndEditも呼び出すとより確実です。
C#bindingSource1.EndEdit();
例えば、BindingSourceを使ってDataTableをバインドする場合は次のようになります。
C#private DataTable employeeTable;
private BindingSource bindingSource;
private void Form1_Load(object sender, EventArgs e)
{
employeeTable = new DataTable();
employeeTable.Columns.Add("Id", typeof(int));
employeeTable.Columns.Add("Name", typeof(string));
employeeTable.Columns.Add("Department", typeof(string));
employeeTable.Rows.Add(1, "田中太郎", "営業部");
employeeTable.Rows.Add(2, "佐藤花子", "総務部");
bindingSource = new BindingSource();
bindingSource.DataSource = employeeTable;
dataGridView1.DataSource = bindingSource;
}
private void buttonSave_Click(object sender, EventArgs e)
{
dataGridView1.EndEdit();
bindingSource.EndEdit();
MessageBox.Show("編集内容を確定しました。");
}
DataGridViewとDataTableの間にBindingSourceを挟むと、並び替えやフィルター、編集確定の管理がしやすくなります。
4-4. DataTableの変更内容を確認するサンプルコード
DataGridViewで編集した内容がDataTableに反映されているか確認するには、DataTableの行をループして値を取得します。
C#private void buttonCheck_Click(object sender, EventArgs e)
{
dataGridView1.EndEdit();
foreach (DataRow row in employeeTable.Rows)
{
string message =
$"Id: {row["Id"]}, Name: {row["Name"]}, Department: {row["Department"]}";
MessageBox.Show(message);
}
}
DataGridViewの表示値ではなく、DataTableの中身を確認している点が重要です。
また、削除された行が含まれている場合、削除済み行に直接アクセスするとエラーになることがあります。その場合は、RowStateを確認します。
C#foreach (DataRow row in employeeTable.Rows)
{
if (row.RowState == DataRowState.Deleted)
{
continue;
}
Console.WriteLine(row["Name"]);
}
4-5. GetChangesで変更行を取得する方法
DataTableには、変更された行だけを取得するGetChangesメソッドがあります。
C#DataTable changedTable = employeeTable.GetChanges();
変更がない場合、GetChangesはnullを返します。
C#private void buttonGetChanges_Click(object sender, EventArgs e)
{
dataGridView1.EndEdit();
DataTable changedTable = employeeTable.GetChanges();
if (changedTable == null)
{
MessageBox.Show("変更された行はありません。");
return;
}
foreach (DataRow row in changedTable.Rows)
{
MessageBox.Show($"状態: {row.RowState}");
}
}
RowStateを見ることで、その行が追加されたのか、更新されたのか、削除されたのかを判断できます。
| RowState | 意味 |
|---|---|
| Added | 追加された行 |
| Modified | 更新された行 |
| Deleted | 削除された行 |
| Unchanged | 変更されていない行 |
データベースへ変更内容を保存するときは、このRowStateをもとにInsert、Update、Deleteを判断することが多いです。
5. DataTableのデータを追加・更新・削除する方法
5-1. DataTableに新しい行を追加する
DataTableに新しい行を追加するには、Rows.Addを使います。
C#private void buttonAdd_Click(object sender, EventArgs e)
{
employeeTable.Rows.Add(4, "高橋美咲", "経理部", 310000);
}
DataGridViewがDataTableにバインドされていれば、DataTableに行を追加したタイミングでDataGridViewにも反映されます。
NewRowを使って行を作成する方法もあります。
C#private void buttonAdd_Click(object sender, EventArgs e)
{
DataRow newRow = employeeTable.NewRow();
newRow["Id"] = 4;
newRow["Name"] = "高橋美咲";
newRow["Department"] = "経理部";
newRow["Salary"] = 310000;
employeeTable.Rows.Add(newRow);
}
列数が多い場合や、列名を指定して値を入れたい場合は、NewRowを使う方法が分かりやすいです。
5-2. 指定した行の値を更新する
指定した行の値を更新するには、対象のDataRowを取得して値を変更します。
例えば、先頭行の氏名を変更する場合は次のように書きます。
C#private void buttonUpdate_Click(object sender, EventArgs e)
{
employeeTable.Rows[0]["Name"] = "田中次郎";
}
条件に一致する行を探して更新する場合は、Selectを使えます。
C#private void buttonUpdate_Click(object sender, EventArgs e)
{
DataRow[] rows = employeeTable.Select("Id = 2");
if (rows.Length > 0)
{
rows[0]["Department"] = "人事部";
rows[0]["Salary"] = 300000;
}
}
Selectの条件式では、列名と値を使って対象行を検索できます。
文字列で検索する場合は、値をシングルクォートで囲みます。
C#DataRow[] rows = employeeTable.Select("Name = '佐藤花子'");
5-3. 指定した行を削除する
DataTableから行を削除する方法には、主にDeleteとRemoveがあります。
Deleteは行を削除済み状態にします。
C#employeeTable.Rows[0].Delete();
Removeは行をコレクションから取り除きます。
C#employeeTable.Rows.Remove(employeeTable.Rows[0]);
データベース更新と連携する場合は、Deleteを使うことが多いです。理由は、Deleteを使うとRowStateがDeletedになり、後から削除された行として判断できるためです。
C#private void buttonDelete_Click(object sender, EventArgs e)
{
if (dataGridView1.CurrentRow == null)
{
return;
}
int rowIndex = dataGridView1.CurrentRow.Index;
if (rowIndex >= 0 && rowIndex < employeeTable.Rows.Count)
{
employeeTable.Rows[rowIndex].Delete();
}
}
ただし、DataGridViewの行インデックスとDataTableの行インデックスは、並び替えをしている場合に一致しないことがあります。より安全に削除するには、DataBoundItemからDataRowViewを取得します。
C#private void buttonDelete_Click(object sender, EventArgs e)
{
if (dataGridView1.CurrentRow == null)
{
return;
}
DataRowView rowView = dataGridView1.CurrentRow.DataBoundItem as DataRowView;
if (rowView != null)
{
rowView.Row.Delete();
}
}
5-4. DataGridViewの表示を更新する
通常、DataTableに行を追加・更新・削除すると、バインドされているDataGridViewも自動的に更新されます。
しかし、表示が変わらない場合は、DataSourceを再設定する方法があります。
C#dataGridView1.DataSource = null;
dataGridView1.DataSource = employeeTable;
BindingSourceを使っている場合は、ResetBindingsを呼び出します。
C#bindingSource.ResetBindings(false);
例えば、次のように使います。
C#private void buttonRefresh_Click(object sender, EventArgs e)
{
bindingSource.ResetBindings(false);
}
ただし、毎回DataSourceを設定し直すと、列幅や選択状態がリセットされることがあります。基本的には、DataTableを直接変更し、必要な場合だけResetBindingsを使うのがおすすめです。
5-5. AcceptChangesとRejectChangesの違い
DataTableには、変更状態を確定するAcceptChangesと、変更を取り消すRejectChangesがあります。
AcceptChangesを呼び出すと、追加・更新・削除などの変更状態が確定され、RowStateがUnchangedになります。
C#employeeTable.AcceptChanges();
一方、RejectChangesを呼び出すと、変更内容が取り消されます。
C#employeeTable.RejectChanges();
例えば、次のような使い方ができます。
C#private void buttonAccept_Click(object sender, EventArgs e)
{
dataGridView1.EndEdit();
employeeTable.AcceptChanges();
MessageBox.Show("変更を確定しました。");
}
private void buttonReject_Click(object sender, EventArgs e)
{
employeeTable.RejectChanges();
MessageBox.Show("変更を取り消しました。");
}
注意点として、データベースに保存する前にAcceptChangesを呼び出すと、どの行が追加・更新・削除されたのか分からなくなる場合があります。
データベース更新を行う場合は、基本的に保存処理が成功してからAcceptChangesを呼び出します。
6. データベースのデータをDataTable経由でDataGridViewに表示する
6-1. SQL Serverからデータを取得する基本
C#でSQL Serverのデータを取得してDataGridViewに表示する場合も、DataTableを使うと分かりやすくなります。
基本的な流れは次のとおりです。
SQL Serverへ接続する
SQLを実行してデータを取得する
取得結果を
DataTableに読み込むDataGridView.DataSourceにDataTableを設定する
SQL Serverへ接続するには、接続文字列が必要です。
C#string connectionString =
@"Data Source=.\SQLEXPRESS;Initial Catalog=SampleDb;Integrated Security=True;";
環境によって、サーバー名やデータベース名は変わります。
SQL Server認証を使う場合は、次のような形式になります。
C#string connectionString =
@"Data Source=localhost;Initial Catalog=SampleDb;User ID=sa;Password=your_password;";
実際のアプリでは、パスワードをソースコードに直接書かず、設定ファイルなどで管理する方が安全です。
6-2. SqlDataAdapterでDataTableに読み込む
SQL Serverの検索結果をDataTableに読み込むには、SqlDataAdapterを使う方法が簡単です。
C#using System.Data;
using System.Data.SqlClient;
サンプルコードは次のとおりです。
C#private DataTable LoadEmployees()
{
string connectionString =
@"Data Source=.\SQLEXPRESS;Initial Catalog=SampleDb;Integrated Security=True;";
string sql = "SELECT Id, Name, Department, Salary FROM Employees";
DataTable table = new DataTable();
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlDataAdapter adapter = new SqlDataAdapter(sql, connection))
{
adapter.Fill(table);
}
return table;
}
adapter.Fill(table)を実行すると、SQLの実行結果がDataTableに読み込まれます。
この方法では、SqlConnectionのOpenを明示的に書かなくても、SqlDataAdapterが必要に応じて接続を開いて閉じてくれます。
6-3. DataGridViewに検索結果を表示する
取得したDataTableをDataGridViewに表示するには、これまでと同じようにDataSourceへ設定します。
C#private DataTable employeeTable;
private void Form1_Load(object sender, EventArgs e)
{
employeeTable = LoadEmployees();
dataGridView1.DataSource = employeeTable;
}
検索ボタンを押したときに表示する場合は、次のように書けます。
C#private void buttonSearch_Click(object sender, EventArgs e)
{
employeeTable = LoadEmployees();
dataGridView1.DataSource = employeeTable;
}
部署名で検索したい場合は、SQLに条件を追加します。
C#private DataTable SearchEmployees(string department)
{
string connectionString =
@"Data Source=.\SQLEXPRESS;Initial Catalog=SampleDb;Integrated Security=True;";
string sql = "SELECT Id, Name, Department, Salary FROM Employees WHERE Department = @Department";
DataTable table = new DataTable();
using (SqlConnection connection = new SqlConnection(connectionString))
using (SqlDataAdapter adapter = new SqlDataAdapter(sql, connection))
{
adapter.SelectCommand.Parameters.AddWithValue("@Department", department);
adapter.Fill(table);
}
return table;
}
画面側では次のように呼び出します。
C#private void buttonSearch_Click(object sender, EventArgs e)
{
string department = textBoxDepartment.Text;
employeeTable = SearchEmployees(department);
dataGridView1.DataSource = employeeTable;
}
SQLに値を埋め込むとSQLインジェクションの原因になるため、検索条件にはパラメーターを使うようにします。
6-4. DataTableの変更内容をデータベースに更新する
DataTableの変更内容をSQL Serverへ反映するには、SqlDataAdapterのUpdateを使う方法があります。
簡単なサンプルとして、SqlCommandBuilderを使う方法を紹介します。
C#private SqlDataAdapter adapter;
private DataTable employeeTable;
private void Form1_Load(object sender, EventArgs e)
{
string connectionString =
@"Data Source=.\SQLEXPRESS;Initial Catalog=SampleDb;Integrated Security=True;";
string sql = "SELECT Id, Name, Department, Salary FROM Employees";
adapter = new SqlDataAdapter(sql, connectionString);
SqlCommandBuilder builder = new SqlCommandBuilder(adapter);
employeeTable = new DataTable();
adapter.Fill(employeeTable);
dataGridView1.DataSource = employeeTable;
}
保存ボタンでは、次のようにUpdateを呼び出します。
C#private void buttonSave_Click(object sender, EventArgs e)
{
dataGridView1.EndEdit();
adapter.Update(employeeTable);
MessageBox.Show("データベースを更新しました。");
}
SqlCommandBuilderを使うと、単純なSELECT文からInsert、Update、Delete用のSQLを自動生成できます。
ただし、実務では複雑なテーブル構造や業務ルールがあるため、Insert、Update、Delete用のSQLを自分で明示的に書くことも多いです。
6-5. Insert・Update・Deleteの考え方
DataTableでは、各行にRowStateという状態があります。
新しく追加された行はAdded、編集された行はModified、削除された行はDeletedになります。
C#foreach (DataRow row in employeeTable.Rows)
{
Console.WriteLine(row.RowState);
}
データベースに反映するときは、この状態を見て次のように処理を分けます。
| RowState | データベース処理 |
|---|---|
| Added | INSERT |
| Modified | UPDATE |
| Deleted | DELETE |
手動で処理を書く場合のイメージは次のとおりです。
C#foreach (DataRow row in employeeTable.Rows)
{
if (row.RowState == DataRowState.Added)
{
// INSERT処理
}
else if (row.RowState == DataRowState.Modified)
{
// UPDATE処理
}
else if (row.RowState == DataRowState.Deleted)
{
// DELETE処理
}
}
削除された行の値を取得する場合は、DataRowVersion.Originalを使います。
C#if (row.RowState == DataRowState.Deleted)
{
int id = (int)row["Id", DataRowVersion.Original];
// idを使ってDELETEする
}
更新前の値と更新後の値を比較したい場合も、DataRowVersionを使います。
C#if (row.RowState == DataRowState.Modified)
{
string oldName = row["Name", DataRowVersion.Original].ToString();
string newName = row["Name", DataRowVersion.Current].ToString();
}
7. DataGridViewとDataTableでよくあるエラーと対処法
7-1. DataSourceに設定しても表示されない
DataGridView.DataSourceにDataTableを設定しても表示されない場合は、まずDataTableに列と行が入っているか確認します。
C#MessageBox.Show($"列数: {employeeTable.Columns.Count}, 行数: {employeeTable.Rows.Count}");
列がない場合、DataGridViewには表示する項目がありません。
また、AutoGenerateColumnsがfalseになっている場合、自動で列が生成されません。
C#dataGridView1.AutoGenerateColumns = true;
dataGridView1.DataSource = employeeTable;
フォーム上に配置したDataGridViewとは別のインスタンスにDataSourceを設定していないかも確認しましょう。
例えば、次のように新しくDataGridViewを作ってしまうと、画面上のdataGridView1には表示されません。
C#DataGridView grid = new DataGridView();
grid.DataSource = employeeTable;
画面に配置したdataGridView1へ設定する必要があります。
C#dataGridView1.DataSource = employeeTable;
7-2. 編集した値が反映されない
DataGridViewで編集した値がDataTableに反映されない場合は、編集が確定していない可能性があります。
保存ボタンなどの処理では、最初にEndEditを呼び出します。
C#private void buttonSave_Click(object sender, EventArgs e)
{
dataGridView1.EndEdit();
// DataTableの内容を確認・保存する
}
BindingSourceを使っている場合は、次のようにします。
C#dataGridView1.EndEdit();
bindingSource.EndEdit();
また、DataGridViewが読み取り専用になっている場合、そもそも編集できません。
C#dataGridView1.ReadOnly = false;
列単位で読み取り専用になっていないかも確認します。
C#dataGridView1.Columns["Name"].ReadOnly = false;
7-3. 列名が意図した表示にならない
DataTableの列名を英語で作成している場合、DataGridView上にも英語名が表示されます。
C#table.Columns.Add("Name", typeof(string));
画面上の見出しを日本語にしたい場合は、HeaderTextを変更します。
C#dataGridView1.Columns["Name"].HeaderText = "氏名";
ただし、DataSourceを設定する前にColumns["Name"]へアクセスすると、列がまだ作成されていないためエラーになることがあります。
正しい順番は次のとおりです。
C#dataGridView1.DataSource = employeeTable;
dataGridView1.Columns["Id"].HeaderText = "社員ID";
dataGridView1.Columns["Name"].HeaderText = "氏名";
また、AutoGenerateColumnsをfalseにしている場合は、手動で列を作成し、DataPropertyNameを設定する必要があります。
C#dataGridView1.AutoGenerateColumns = false;
DataGridViewTextBoxColumn nameColumn = new DataGridViewTextBoxColumn();
nameColumn.HeaderText = "氏名";
nameColumn.DataPropertyName = "Name";
dataGridView1.Columns.Add(nameColumn);
dataGridView1.DataSource = employeeTable;
DataPropertyNameには、DataTableの列名を指定します。ここを間違えると、列は表示されても値が表示されません。
7-4. Null値でエラーになる
DataTableの値がnullやDBNull.Valueの場合、文字列や数値に変換するときにエラーになることがあります。
データベースから読み込んだ値がNULLの場合、C#では多くの場合DBNull.Valueとして扱われます。
例えば、次のようなコードはエラーになる可能性があります。
C#int salary = (int)row["Salary"];
NULLの可能性がある場合は、DBNull.Valueかどうか確認します。
C#int salary = 0;
if (row["Salary"] != DBNull.Value)
{
salary = Convert.ToInt32(row["Salary"]);
}
文字列の場合は、次のように書くと安全です。
C#string name = row["Name"] == DBNull.Value
? string.Empty
: row["Name"].ToString();
DataTableの列にNULLを許可したくない場合は、AllowDBNullをfalseにできます。
C#DataColumn nameColumn = new DataColumn("Name", typeof(string));
nameColumn.AllowDBNull = false;
employeeTable.Columns.Add(nameColumn);
ただし、NULLを禁止した列に値を入れずに行を追加するとエラーになります。
7-5. データ更新後に画面が変わらない
DataTableの値を変更したのにDataGridViewの表示が変わらない場合は、バインドの状態を確認します。
BindingSourceを使っている場合は、ResetBindingsを呼び出します。
C#bindingSource.ResetBindings(false);
直接DataTableを設定している場合は、DataSourceを再設定する方法もあります。
C#dataGridView1.DataSource = null;
dataGridView1.DataSource = employeeTable;
ただし、通常はDataTableの行や列を正しく変更していれば、自動的に反映されます。
次のように、DataTableではなく別の変数を変更しているだけでは、DataGridViewには反映されません。
C#string name = employeeTable.Rows[0]["Name"].ToString();
name = "変更後の名前";
この場合、変数nameを変更しただけで、DataTableの値は変わっていません。
正しくは、DataRowの値を変更します。
C#employeeTable.Rows[0]["Name"] = "変更後の名前";
また、データベースを更新しただけでは、画面上のDataTableが自動的に最新になるとは限りません。データベース更新後に最新データを再取得する場合は、再度検索処理を実行します。
C#private void buttonReload_Click(object sender, EventArgs e)
{
employeeTable = LoadEmployees();
dataGridView1.DataSource = employeeTable;
}
まとめ
C#でDataGridViewにDataTableを表示する基本は、DataTableに列と行を作成し、DataGridView.DataSourceに設定することです。
C#dataGridView1.DataSource = employeeTable;
DataTableを使うことで、メモリ上で表形式のデータを管理しながら、DataGridViewに簡単に表示できます。
基本的な流れは次のとおりです。
C#DataTable table = new DataTable();
table.Columns.Add("Id", typeof(int));
table.Columns.Add("Name", typeof(string));
table.Rows.Add(1, "田中");
table.Rows.Add(2, "佐藤");
dataGridView1.DataSource = table;
DataGridView上で編集した内容は、基本的にバインド元のDataTableへ反映されます。ただし、編集中の値を確実に反映したい場合は、保存処理の前にEndEditを呼び出します。
C#dataGridView1.EndEdit();
また、DataTableの行を追加・更新・削除すると、バインドされているDataGridViewにも表示が反映されます。
C#employeeTable.Rows.Add(4, "高橋美咲", "経理部", 310000);
employeeTable.Rows[0]["Name"] = "田中次郎";
employeeTable.Rows[1].Delete();
データベースのデータを表示する場合は、SqlDataAdapterで検索結果をDataTableに読み込み、DataGridViewに設定します。
C#SqlDataAdapter adapter = new SqlDataAdapter(sql, connectionString);
DataTable table = new DataTable();
adapter.Fill(table);
dataGridView1.DataSource = table;
C#のWindows Formsで一覧画面を作る場合、DataGridViewとDataTableの組み合わせは非常に便利です。まずは小さなサンプルで表示、編集、追加、更新、削除の流れを理解し、その後でデータベース連携に進むとスムーズに学習できます。

