こんにちは!Windows Formsアプリケーションの開発で、こんな壁にぶつかったことはありませんか?
「DataGridViewを使って、エクセルみたいな複雑な表を作りたい……!」
「上の行と値が同じだから、セルを結合してすっきり見せたい!」
「セルの中にアイコンと文字を綺麗に並べて、かつサクサク動かしたい!」
標準のDataGridViewを使おうとすると、「あれ、意外とできることが少ないぞ……?」と絶望しがちですよね。ネットで調べても「サードパーティ製の有料コンポーネントを買いましょう」なんて冷たい答えが出てきたりして。
でも、安心してください。
Visual Basic (VB.NET) の `CellPainting` イベント を完全掌握すれば、標準のDataGridViewだけで、エクセル顔負けの自由なレイアウトと爆速描画を手に入れることができます。
今回は、マクロの記録やなんとなくのコーディングから一歩抜け出し、「UIの裏側で何が起きているのか」を理解したいあなたへ、プロの知見をたっぷり詰め込んだ極上のテクニックを伝授します。ここをクリアすれば、VB.NETでのUI開発に怖いものはありませんよ!
—
なぜ標準のDataGridViewでは複雑な表が作れないのか?
まず敵を知ることから始めましょう。DataGridViewは非常に強力なコントロールですが、デフォルトの機能では「1つのセル=1つの値、1つの表現」という厳格なルールで作られています。
エクセルのように「A1とA2のセルを結合して中央にドンと文字を置く」「セルの途中に斜線を引く」といったレイアウトは、そのままでは実現できません。
ここで多くの開発者がやってしまう失敗が、「セルの中に無理やりパネルやピクチャボックスを配置する(コントロールの乱用)」というアプローチです。これをやると、スクロールするたびにカクカクと画面がちらつき(フリッカ)、メモリを食いつぶしてアプリが重くなります。
プロが選ぶ正解:描画の主導権を奪い取る
プロのエンジニアは、コントロールを無駄に置くようなことはしません。画面の描画タイミング(イベント)をフックし、「OSに代わって、自分がセルの見た目をキャンバスに直接描き込む(カスタム描画)」という手法を使います。
その主役となるのが、今回解説する `CellPainting` イベント です。
—
CellPaintingイベントの基本と「描画の作法」
`CellPainting` は、DataGridViewの各セルが「今から画面に描画されるよ!」という瞬間に必ず発生するイベントです。このイベントの中で、「ここは自分で描くから、Windowsさん、デフォルトの描画はしなくていいよ!」と宣言(Handled = True)することで、自由な装飾が可能になります。
まずは、基本のコードを見てみましょう。VB.NETでの実装例です。
Private Sub DataGridView1_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
‘ ヘッダー行や、まだ画面上に実体のない行・列は処理対象外とする
If e.RowIndex < 0 OrElse e.ColumnIndex < 0 Then Return
' 例:特定の列(例: 2列目)の特定の条件を満たすセルだけカスタム描画する
If e.ColumnIndex = 2 Then
' 1. まず背景や枠線などのデフォルトの振る舞いを描画させる
e.Paint(e.ClipBounds, DataGridViewPaintParts.All)
' 2. 独自の装飾(例:右上に「NEW」というミニバッジを描く)
Using brush As New SolidBrush(Color.Red)
e.Graphics.FillRectangle(brush, New Rectangle(e.CellBounds.Right - 30, e.CellBounds.Top + 2, 28, 14))
End Using
Using font As New Font("Meiryo UI", 7, FontStyle.Bold),
brush As New SolidBrush(Color.White)
e.Graphics.DrawString("NEW", font, brush, e.CellBounds.Right - 29, e.CellBounds.Top + 2)
End Using
' 3. 「このセルの描画は自分が完了させた」とOSに伝える
e.Handled = True
End If
End Sub
ここがポイント!初心者が陥りやすい罠
上記のコードにある `e.Handled = True`。これが非常に重要です。
もしこれを書き忘れると、自分が描いた「NEW」の上から、DataGridViewのデフォルトの文字や背景が上書きされてしまい、描画がバグったり消えたりします。
また、`e.Graphics` などのGDI+オブジェクトは、使い終わったら必ず `Using` ステートメントなどで解放(Dispose)してください。これを怠ると、メモリリーク(メモリのゴミ溜まり)を起こし、アプリやPC全体の動作が重くなります。
—
実践:エクセルライクな「セル結合」と「斜線」の完全制御
それでは、より実践的なテクニックとして、「隣り合うセルの値が同じ場合に、見た目上だけセルを結合して中央に文字を表示する」処理と、「特定のセルに斜線を引く」実装を行ってみましょう。
以下のコードをフォームに組み込んでみてください。
Private Sub DataGridView1_CellPainting(sender As Object, e As DataGridViewCellPaintingEventArgs) Handles DataGridView1.CellPainting
‘ ヘッダーは除外
If e.RowIndex < 0 OrElse e.ColumnIndex < 0 Then Return
' --- 実装1:特定列(例: 0列目)の連続する同じ値を結合して見せる ---
If e.ColumnIndex = 0 AndAlso e.RowIndex > 0 Then
Dim currentValue As String = DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value?.ToString()
Dim prevValue As String = DataGridView1.Rows(e.RowIndex – 1).Cells(e.ColumnIndex).Value?.ToString()
‘ 上の行と同じ値の場合、セルの境界線(上側)を消して結合されているように見せる
If currentValue = prevValue Then
‘ 背景を普通に描画
e.Paint(e.ClipBounds, DataGridViewPaintParts.Background Or DataGridViewPaintParts.SelectionBackground)
‘ 枠線をカスタムで描画(上以外の3方を描く)
Using p As New Pen(DataGridView1.GridColor)
‘ 上の境界線をスキップし、左右下を描画
e.Graphics.DrawLine(p, e.CellBounds.Left, e.CellBounds.Bottom – 1, e.CellBounds.Right – 1, e.CellBounds.Bottom – 1)
e.Graphics.DrawLine(p, e.CellBounds.Left, e.CellBounds.Top, e.CellBounds.Left, e.CellBounds.Bottom – 1)
e.Graphics.DrawLine(p, e.CellBounds.Right – 1, e.CellBounds.Top, e.CellBounds.Right – 1, e.CellBounds.Bottom – 1)
End Using
‘ テキストは描画せず、結合の親セル(一番上のセル)に任せるか、必要ならここで描画する
e.Handled = True
Return
End If
End If
‘ — 実装2:特定の条件(例: 1列目で値が「100」以上)のセルに斜線を引く —
If e.ColumnIndex = 1 Then
Dim val As Integer
If Integer.TryParse(DataGridView1.Rows(e.RowIndex).Cells(e.ColumnIndex).Value?.ToString(), val) AndAlso val >= 100 Then
‘ 背景を描画
e.Paint(e.ClipBounds, DataGridViewPaintParts.All And Not DataGridViewPaintParts.ContentForeground)
‘ 左下から右上へ斜線を引く
Using p As New Pen(Color.Gray, 1.5)
e.Graphics.DrawLine(p, e.CellBounds.Left, e.CellBounds.Bottom, e.CellBounds.Right, e.CellBounds.Top)
End Using
‘ 文字を上から重ねて描画
e.PaintContent(e.CellBounds)
e.Handled = True
End If
End If
End Sub
コードの解説とプロの知見
1. 「見た目だけの結合」のからくり
本物のセル結合をプログラムでやろうとすると、内部のデータ構造が複雑になり破綻します。プロは「データはそのままに、境界線を消すことで結合しているように錯覚させる」というアプローチをとります。これにより、並び替えやフィルタリングを行ってもデータが壊れません。
2. ちらつき(フリッカ)防止の極意
DataGridViewは標準でダブルバッファリング(画面の裏側で一度絵を作ってから一気に転送する技術)が有効になっていない場合があります。もしスクロール時にちらつく場合は、フォームのロード時などに以下のリフレクション(またはカスタムコントロール化)を使ってダブルバッファを有効にしてください。
‘ DataGridViewのダブルバッファを有効化する魔法のコード
Dim dgvType As Type = DataGridView1.GetType()
Dim pi As Reflection.PropertyInfo = dgvType.GetProperty(“DoubleBuffered”, Reflection.BindingFlags.Instance Or Reflection.BindingFlags.NonPublic)
pi.SetValue(DataGridView1, True)
これを入れるだけで、どれだけ複雑なカスタム描画をしても、嘘のように滑らかにスクロールするようになります。
—
まとめ:ここをクリアすれば、VB.NETのUI開発はバッチリ!
今回は、DataGridViewの `CellPainting` イベントを完全制御し、エクセルライクな複雑な表レイアウトを高速かつ滑らかに実現する方法を解説しました。
- 標準機能の限界は「カスタム描画」で突破する
- `e.Handled = True` でOSに描画のバトンタッチを宣言する
- 境界線やGDI+(Graphics)を操作して、自由な表現を手に入れる
- ダブルバッファリングで、プロ品質の「ちらつきのない滑らかな動作」を担保する
一見すると難しそうに見えるカスタム描画ですが、一つひとつの仕組みを紐解いていけば、VB.NETの強力で素直な一面が見えてきます。マクロの記録の枠を飛び出し、自分の手でユーザービリティの極まったUIを作り上げられた時の達成感は格別です。
ぜひ、あなたのアプリケーションにもこのテクニックを取り入れて、まわりの開発者をあっと言わせるようなリッチな画面を作ってみてくださいね。応援しています!
