DataGridView でデータ変更が画面に反映されない原因と解決方法(サンプルあり)

📋 目次(クリックで展開)

DataGridView を使って開発していると、コード側でデータクラスの値を変更したのに、画面上の DataGridView のセルが古いまま更新されないという問題に遭遇することがあります。

一見、正しく値を代入しているように見えても画面が変わらないため、デバッグに時間がかかってしまうケースは非常に多いです。

今回は、データ変更が画面に反映されない原因と、.NET 8 / .NET 10 などの最新環境にも適した解決方法について解説します。

データ変更が画面に反映されない原因について

プログラム側でオブジェクトのプロパティ(例:person.Name = "新名称";)を書き換えても画面が変わらないのは、DataGridView コントロールに対して「データが変わりました」という通知(イベント)が届いていないためです。

通常の List<T> コレクションや、標準的なクラスのプロパティは、内部の値が変わってもそれを外部に知らせる仕組みを持っていません。DataGridView 側はデータが変わったことを検知できないため、古い情報のまま表示を続けてしまいます。

これを解決するには、データソース側に「変更を通知する仕組み」を組み込む必要があります。

解決方法:BindingList と INotifyPropertyChanged の利用

「画面が変わらないから」といって、変更のたびに DataSource = null; を挟んで再設定したり、Refresh() を呼び出す実装を見かけることがあります。しかし、これらは画面のちらつきやスクロール位置のリセットを引き起こすため、実務の業務アプリでは避けたいアプローチです。

モダンな Windows Forms 開発では、データと UI をスマートに同期させるために、以下の2つの機能を組み合わせて解決します。

  1. BindingList<T>:リスト自体の追加・削除を UI に通知するコレクション
  2. INotifyPropertyChanged:オブジェクト内部のプロパティ変更を UI に通知するインターフェース

これらを組み合わせることで、コード側でデータを書き換えるだけで、DataGridView のセルが自動的に再描画されるようになります。

実装コード

以下は、データモデルの変更が DataGridView に即座に自動反映される実装例です。C# のモダンな書き方に合わせ、プロパティ名の指定を安全に行える [CallerMemberName] を活用しています。

Form1.cs(画面側の処理)

Form1.cs
using System.ComponentModel;
namespace DataGridView_RefreshBoundData
{
public partial class Form1 : Form
{
// DataGridView にバインドするための通知機能付きリスト
private BindingList<MemberModel> _members = new();
public Form1()
{
InitializeComponent();
InitializeDataGridView();
}
private void InitializeDataGridView()
{
// 初期データをリストに追加します
_members.Add(new MemberModel("M001", "山田 太郎", "開発部"));
_members.Add(new MemberModel("M002", "佐藤 花子", "営業部"));
// DataGridView のデータソースに BindingList を指定します
dataGridView1.DataSource = _members;
}
private void btnUpdate_Click(object sender, EventArgs e)
{
// DataGridView で現在選択されている行のデータ(オブジェクト)を取得します
if (dataGridView1.CurrentRow?.DataBoundItem is MemberModel selectedMember)
{
// プロパティの値を変更します
// INotifyPropertyChanged の働きにより、この一行で画面も自動更新されます
selectedMember.Department = "海外事業部";
}
}
}
}

MemberModel.cs(データモデルクラス)

MemberModel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace DataGridView_RefreshBoundData
{
public class MemberModel : INotifyPropertyChanged
{
// プロパティ変更時に発生するイベント
public event PropertyChangedEventHandler? PropertyChanged;
// 自動プロパティ(今回は変更通知が不要な固定値とします)
public string Id { get; set; }
public string Name { get; set; }
// 変更通知が必要なプロパティ(バッキングフィールドを用意します)
private string _department = string.Empty;
public string Department
{
get => _department;
set => SetProperty(ref _department, value);
}
// コンストラクタ
public MemberModel(string id, string name, string department)
{
Id = id;
Name = name;
_department = department;
}
// 値が変更された場合にのみイベントを発行するヘルパーメソッド
protected void SetProperty<T>(ref T storage, T value, [CallerMemberName] string? propertyName = null)
{
if (Equals(storage, value)) return;
storage = value;
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
}
}
}

ソースコード

サンプルアプリケーション

DataGridView_RefreshBoundData.zip
※ サンプルアプリケーションは、.NET 9 をターゲットに作成されています。

実行結果

「データ更新」ボタンをクリックすると、M0001 の Department が「開発部」から「海外事業部」に更新されることが分かります。

よくある間違いと注意点

バグに直面した際、「とりあえず動けばいい」と焦ってトリッキーなコードを書いてしまうと、思わぬ副作用(パフォーマンス低下や操作性の悪化)を招くことがあります。特に実務でやってしまいがちな、以下の3つのアンチパターンには注意してください。

List<T> をそのまま DataSource に指定している

List<T> は非常に軽量で扱いやすいコレクションですが、UI への変更通知機能を持っていません。要素の追加や削除を行っても DataGridView は無反応になります。バインド用途では BindingList<T> を選択してください。

DataGridView.Refresh() で解決しようとする

Refresh() メソッドは、コントロールの見た目を強制的に描き直すだけの機能です。データソース自体が「変更された」というイベントを出していない場合、DataGridView は古いデータをそのまま描き直すだけなので、表示は変わりません。

DataSource に一度 null を代入して再設定している

dataGridView1.DataSource = null;
dataGridView1.DataSource = _members;

このように記述すると、強制的にデータが読み直されるため画面は更新されます。しかし、この処理が走った瞬間に、「ユーザーが現在選択していたセルの位置(CurrentCell)」や「スクロール位置」、「列のソート状態」がすべてリセットされてしまいます。 業務アプリの操作性を著しく損ねるため、おすすめしません。

まとめ

今回は、DataGridView にバインドしたデータの変更が画面に反映されない原因と、その解決方法について解説しました。

DataGridView の画面更新トラブルは、Windows Forms 開発において、ほぼすべてのエンジニアが一度は直面し、時間を溶かしてしまう定番の壁と言えます。

しかし、今回ご紹介した BindingList<T>INotifyPropertyChanged の組み合わせを一度テンプレート化してしまえば、無駄な画面リフレッシュ処理や DataSource の再設定コードで消耗することはなくなります。

データ層(ロジック)をいじるだけで UI が綺麗に連動する、バグの少ない快適なアプリケーション開発の参考になれば幸いです。


💬 コメント