【テクニカル・上級編】Windows FormsのDataGridViewにおけるカスタム列(DataGridViewColumn)の自社開発:社内ニッチ要件を満たす特殊入力セルを作る – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

鋼鉄のUIを鋳造せよ:DataGridViewカスタム列開発の極致

我々が戦っているのは、ただの「画面」ではない。業務の血流そのものだ。
標準の `DataGridViewTextBoxColumn` や `ComboBoxColumn` で満足しているうちは、まだお遊びの段階と言っていい。現場の泥臭いニッチな要件――バーコードスキャナの入力割込み制御、独自のビジネスロジックを内包したモーダルポップアップなど――を実装するには、`DataGridView` の内部構造をハックし、GDI+とWin32 APIの境界線を掌握する必要がある。

今日は、その「深淵」への入り方を伝授する。

—

1. アーキテクチャの基本:3つの柱を理解せよ

`DataGridView` のカスタム列を作る際、多くのプログラマは `Cell` クラスだけをいじって満足する。それは大きな間違いだ。以下の3つのクラスを継承し、正しく連携させなければ、メモリリークと描画パフォーマンスの低下という「死」が待っている。

  • DataGridViewColumn: 列のプロパティを保持する。
  • DataGridViewCell: セルのデータ構造と描画ロジックを司る。
  • DataGridViewEditingControl: 編集モード時のUIを制御する。

これらは単なるクラスではない。`DataGridView` という巨大な仮想化システムの一部だ。特に `EditingControl` は、編集終了後に破棄・再利用されるサイクルを意識しなければ、ガベージコレクション(GC)を圧迫し、大規模データ読み込み時にアプリをフリーズさせる。

—

2. 実装の要諦:カスタムEditingControlの作成

バーコードスキャナや特殊入力用UIを作成する場合、最も重要なのは「入力フォーカス」の制御だ。以下は、独自入力UIを実装するためのテンプレートである。

”’

”’ カスタムEditingControlの基盤
”’ IDataGridViewEditingControlを実装し、Win32 APIでのフックも辞さない姿勢を見せよ
”’

Public Class MyCustomEditingControl
Inherits TextBox
Implements IDataGridViewEditingControl

‘ 編集時の値変更をDataGridViewに通知するためのプロパティ
Public Property EditingControlValueChanged As Boolean Implements IDataGridViewEditingControl.EditingControlValueChanged
Public Property EditingControlRowIndex As Integer Implements IDataGridViewEditingControl.EditingControlRowIndex

‘ フォーカスが外れた際の挙動を制御(ここでバーコードスキャナのバッファフラッシュを制御する)
Protected Overrides Sub OnLostFocus(e As EventArgs)
‘ 必要に応じてWin32 APIでキーボード入力をクリアするなどの調整をここで行う
MyBase.OnLostFocus(e)
End Sub

‘ 編集コントロールのスタイルをDataGridViewと同期させる
Public Sub ApplyCellStyleToEditingControl(cellStyle As DataGridViewCellStyle) Implements IDataGridViewEditingControl.ApplyCellStyleToEditingControl
Me.Font = cellStyle.Font
Me.ForeColor = cellStyle.ForeColor
Me.BackColor = cellStyle.BackColor
End Sub

‘ 以下、必要なインターフェース実装…
End Class

—

3. GDI+による描画の最適化(Paintイベントの制御)

`DataGridViewCell.Paint` メソッドをオーバーライドする際、最も陥りやすい罠が「無駄なオブジェクト生成」だ。`Pen` や `Brush` を `Paint` 内で `New` してはならない。

数千行の描画が発生した瞬間、あなたのアプリはGCの嵐に巻き込まれ、描画はカクつく。リソースはStaticメンバとして保持し、使い回せ。

‘ インスタンス生成コストを排除するためのキャッシュ戦略
Private Shared ReadOnly CellBorderPen As New Pen(Color.FromArgb(200, 200, 200))
Private Shared ReadOnly FocusedBrush As New SolidBrush(Color.AliceBlue)

Protected Overrides Sub Paint(graphics As Graphics, clipBounds As Rectangle, cellBounds As Rectangle, …)
‘ グラフィックスの描画ロジック
‘ Graphics.FromHwnd 等のコストの高い処理は徹底的に排除する

‘ 描画の極意:Graphicsオブジェクトを直接触る際は、クリッピング領域を意識する
‘ DrawString ではなく TextRenderer.DrawText を使うのが、Windows Formsにおける正義だ
TextRenderer.DrawText(graphics, Me.Value?.ToString(), Me.DataGridView.Font, cellBounds, Color.Black, TextFormatFlags.VerticalCenter)
End Sub

—

4. 伝説的アーキテクトからの忠告

メモリの管理について

`IDisposable` を実装したオブジェクトを `DataGridView` のセル内に保持する場合、必ず `DataGridView.CellDispose` イベントを購読し、明示的に `Dispose()` を呼ぶこと。Windowsのハンドルは有限だ。特に古いレガシー環境では、この管理一つで「数日稼働させると重くなる」という事象を根絶できる。

Win32 APIとの調和

社内システム特有の「バーコードスキャナがシリアルポート経由ではなくキーボードエミュレーションで入力される」といった事象に対しては、`NativeWindow` クラスを継承し、`WndProc` をオーバーライドしてメッセージを傍受しろ。`WM_KEYDOWN` を監視し、特定のヘッダ文字列を検知した瞬間にバッファをクリアしてセルの値を書き換える。これが、フレームワークの制約を超越する唯一の道だ。

—

結論

`DataGridView` をハックすることは、OSの魂に触れることと同義だ。
「標準機能でできないから」と諦めるのは、エンジニアとしての死を意味する。今回示したのは氷山の一角だが、このクラス設計とリソース管理の作法さえ身につければ、どんなに複雑な業務UIもあなたの手の内にある。

もし実装で壁にぶつかったなら、自身のコードが「フレームワークに甘えていないか」を自問せよ。
真の自動化エンジニアは、APIの深層を泳ぎ、ハードウェアとの対話を恐れない。

健闘を祈る。貴殿の書くコードが、明日の現場の生産性を数倍に引き上げることを期待している。

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