DataGridViewを「ただの表」で終わらせるな。極限のUIパフォーマンスとUXを実現する行ヘッダー制御術
業務システムにおいて、`DataGridView`は避けて通れないUIコンポーネントだ。しかし、多くの開発者が陥る罠がある。それは、「行番号を表示するためにDataTableにわざわざ連番列を追加する」ことや、「バリデーションの結果をセル一つひとつに書き込んで再描画コストを浪費する」ことだ。
いいか、UIの描画はコストだ。数万行のデータでシステムが重くなるのは、コントロールの使い方を間違えているからに過ぎない。今日は、Visual Basic(VB.NET)の低レイヤーに近い「描画イベント」を直接制御し、UIとデータを完全に分離したまま、最高速で堅牢な行番号・ステータス表示を実装する術を伝授する。
—
1. なぜ「データバインド」だけで解決してはいけないのか
初心者は、`DataGridView`のデータソースに全ての情報を詰め込もうとする。しかし、行番号はデータの属性ではないし、バリデーションエラーのアイコンもデータベースには存在しない「UI上の状態」だ。
これらをデータ側に持たせると、ソートやフィルタリングのたびに計算コストが発生し、コードの保守性は地に落ちる。我々がとるべき戦略は、「描画の瞬間に、必要な情報を動的に描画する(OnRowPostPaintを掌握する)」ことだ。
—
2. 実装の心臓部:RowPostPaintイベントのオーバーライド
`CellPainting`ではなく`RowPostPaint`を使う。これは行全体の描画の最後に実行されるため、標準の行ヘッダーの描画を上書きするのに最適だ。
実装コード:パフォーマンスを犠牲にしないカスタム描画
このコードは、行番号の描画と、エラー状態に基づいた警告アイコンの描画を同時に行う。
Imports System.Drawing
Public Class CustomDataGridView
Inherits DataGridView
‘ エラー行のインデックスを保持するハッシュセット(高速検索用)
Public Property ErrorRows As New HashSet(Of Integer)
Protected Overrides Sub OnRowPostPaint(e As DataGridViewRowPostPaintEventArgs)
MyBase.OnRowPostPaint(e)
‘ 1. 行番号の描画
Dim rowIdx As String = (e.RowIndex + 1).ToString()
Dim centerFormat As New StringFormat() With {
.Alignment = StringAlignment.Center,
.LineAlignment = StringAlignment.Center
}
Dim headerBounds As Rectangle = New Rectangle(e.RowBounds.Left, e.RowBounds.Top, Me.RowHeadersWidth, e.RowBounds.Height)
‘ 文字列描画(フォントは固定でキャッシュしておくのがベスト)
e.Graphics.DrawString(rowIdx, Me.Font, SystemBrushes.ControlText, headerBounds, centerFormat)
‘ 2. バリデーションエラーアイコンの描画
If ErrorRows.Contains(e.RowIndex) Then
‘ システムアイコン(警告マーク)を描画
‘ 実務ではリソースから読み込んだアイコンを使うと美しい
Dim iconSize As Integer = 16
Dim iconRect As New Rectangle(e.RowBounds.Left + 2, e.RowBounds.Top + (e.RowBounds.Height – iconSize) \ 2, iconSize, iconSize)
e.Graphics.DrawIcon(SystemIcons.Warning, iconRect)
End If
End Sub
End Class
—
3. 運用・保守の勘所
この設計には、実務で戦い抜くための「3つの規律」がある。
① `HashSet`によるバリデーション管理
エラー行の管理に `List(Of Integer)` を使ってはいけない。`Contains`メソッドの計算量はデータ量に比例する。`HashSet`であれば定数時間(O(1))で状態判定ができる。数千行のデータでも、スクロール中の描画ラグは皆無だ。
② 描画コストの最適化(GDI+の作法)
`OnRowPostPaint`内で`New Pen`や`New Brush`を生成してはいけない。ガベージコレクション(GC)が頻発し、画面がカクつく原因になる。描画に使用するブラシやフォントは、クラスのメンバとして静的に保持(キャッシュ)しておくのが、プロフェッショナルな設計だ。
③ データの完全性(DB連携時の注意)
この手法はあくまで「見た目の制御」だ。バリデーションのロジックはドメイン層に分離し、UIはあくまでその結果を投影する鏡であるという原則を崩すな。DBへ保存する際は、この`ErrorRows`の内容ではなく、元のエンティティに対してバリデーションを再実行すること。UIの状態とデータの状態を混同させないことが、バグを生まない唯一の道だ。
—
結論:UIは「機能」ではなく「体験」であれ
「データが入っているから行番号を出す」のではなく、「ユーザーが見やすいように行番号を提示する」。この視点の転換が、業務自動化ツールの品質を決定づける。
今回紹介した`RowPostPaint`によるオーバーライドは、`DataGridView`の機能を拡張する際の「正攻法」だ。これをマスターすれば、行番号やエラーアイコンだけでなく、進捗バーやスパークラインのような高度な可視化も自由自在になる。
現場の要件に振り回されるな。システムを設計し、制御せよ。それが、我々エンジニアの矜持だ。
