【実務・中級編】DataGridViewの大量行一括更新におけるパフォーマンス劣化の解消:BindingListとSuppressNotificationの極意 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

DataGridViewの死を回避せよ:数万件のバインドを「一瞬」で終わらせる極意

現場でよく見る光景がある。「数万件のレコードをDataGridViewに表示したらUIが数秒間フリーズする」という嘆きだ。これはコードの問題ではない。「DataGridViewとBindingListの仕様」に対する、エンジニアの無知が招いた人災だ。

結論から言おう。`BindingList(Of T)` をそのまま使ってループで `Add` し続けているなら、今すぐそのコードを捨てろ。GUIの更新通知は、1件ごとに走らせるほど安くない。

なぜ、あなたのDataGridViewはフリーズするのか

`BindingList(Of T)` は、アイテムが追加されるたびに `ListChanged` イベントを発行する。DataGridViewはこの通知をトリガーに、「行が追加されたから再描画しなきゃ!」と血眼になってレイアウト計算を走らせる。

10,000件追加すれば、10,000回の再描画リクエストが発生する。UIスレッドが悲鳴を上げるのは当然だ。我々アーキテクトがやるべきは、この「無駄な通知」を黙らせることである。

—

極意:通知を遮断し、バッチで叩き込む

解決策はシンプルだ。「通知を抑制(Suppress)し、一括で追加し、最後に一度だけ通知する」。これだけでパフォーマンスは数100倍変わる。

堅牢な実装:`SuppressibleBindingList`

標準の `BindingList` には、残念ながら `RaiseListChangedEvents` を制御するプロパティが隠蔽されている。派生クラスを作成し、これを制御できるようにするのが、プロフェッショナルの定石だ。

Imports System.ComponentModel

”’

”’ 大量データ更新時の再描画を抑制する高速バインディングリスト
”’

Public Class SuppressibleBindingList(Of T)
Inherits BindingList(Of T)

‘ 再描画通知の切り替え用
Public Shadows Property RaiseListChangedEvents As Boolean
Get
Return MyBase.RaiseListChangedEvents
End Get
Set(value As Boolean)
MyBase.RaiseListChangedEvents = value
End Set
End Property

”’

”’ 大量追加を安全に行うための拡張メソッド的アプローチ
”’

Public Sub AddRange(items As IEnumerable(Of T))
‘ 通知を停止
Me.RaiseListChangedEvents = False
Try
For Each item As T In items
Me.Add(item)
Next
Finally
‘ 最後に一度だけ通知を再開し、リスト変更を通知
Me.RaiseListChangedEvents = True
Me.ResetBindings()
End Try
End Sub
End Class

—

現場で即戦力となる適用コード

DBから取得したデータをDataGridViewに流し込む際の「正しい作法」は以下の通りだ。

‘ フォームの初期化時
Private _bindingSource As New BindingSource()
Private _dataList As New SuppressibleBindingList(Of MyDataModel)()

Private Sub InitializeGrid()
_bindingSource.DataSource = _dataList
DataGridView1.DataSource = _bindingSource
End Sub

‘ データベースから数万件を取得して表示するメソッド
Private Sub LoadData(dataFromDb As List(Of MyDataModel))
‘ UIのチラつき防止
DataGridView1.SuspendLayout()

‘ 一括追加(通知を抑制して高速化)
_dataList.AddRange(dataFromDb)

DataGridView1.ResumeLayout()
End Sub

この設計が「堅牢」である理由

1. `SuspendLayout` / `ResumeLayout` の併用: `BindingList` の通知だけでなく、DataGridView自体のレイアウトエンジンも停止させる。二段構えの防壁だ。
2. `Try…Finally` ブロック: 万が一、データの追加中に例外が発生しても、確実に `RaiseListChangedEvents = True` が実行されるようにしている。これで、万が一のバグ時にもUIが二度と更新されない、といった致命的な不具合を回避できる。
3. 疎結合: データモデルとUIが `BindingSource` を介して分離されているため、後からフィルタリング機能を追加する際も修正コストが最小限で済む。

—

エンジニアへの戒め:APIを信じすぎるな

今回紹介した手法は、いわば「フレームワークの裏側を覗き込み、手綱を握る」技術だ。
VB.NETやWindows Formsは古い技術と揶揄されることもあるが、「なぜその挙動になるのか」を理解して制御すれば、現代のWebフロントエンドの泥沼のようなレンダリング地獄よりも、遥かに高速で堅牢なUIを構築できる。

データが増えてアプリが重くなるのは、ハードウェアのせいではない。あなたのコードが「無駄な報告」を繰り返しているからだ。

今日から、DataGridViewに対して「黙っていろ、全部終わったら合図する」と命じる設計に変えろ。それだけで、あなたの書くコードは一流の領域に一歩近づく。

タイトルとURLをコピーしました