【VB.NET極限知見】Windows Forms継承コントロール:コピペ地獄を断ち切る「最強の入力パーツ」の設計
レガシーシステムの改修現場に足を踏み入れると、決まって目にする光景がある。
どの画面を開いても、`TextBox`が置かれ、そのイベントハンドラには似たようなボイラープレートコードが延々とコピー&ペーストされている。「フォーカス取得時にテキストを全選択する」「Enterキーで次のコントロールへフォーカスを移す」「不正入力時に背景色を赤く染める」――。
これらをすべての画面、すべてのテキストボックスに対して実装しているうちは、アーキテクトとは呼べない。コードの重複は悪であり、保守性の最大の敵だ。
今回は、VB.NETのオブジェクト指向の恩恵を最大限に引き出し、「フォーカス取得時に全選択」「エンターキーで次へ移動」「バリデーション色変更」を完全カプセル化した、社内共通の派生コントロール(Custom Control)の設計と実装を解説する。
—
1. なぜ「継承コントロール」なのか?
ユーザーコントロール(UserControl)ではなく、既存コントロールの継承(Inherits TextBox)を選択する理由がここにある。
中途半端なスキルを持つ開発者は、複数のコントロールを並べた「ユーザーコントロール」を作りたがるが、単一の入力フィールドの振る舞いを拡張したい場合、それはオーバーエンジニアリングであり、レイアウトの柔軟性を殺す元凶となる。
`System.Windows.Forms.TextBox`を直接継承すれば、元来のテキストボックスが持つすべてのプロパティ、メソッド、パフォーマンス特性をそのまま維持しながら、DNAレベルで振る舞いを書き換えることができる。
—
2. 実装コード:`ExTextBox` の全貌
以下のコードは、現場の即戦力となる完全なVB.NETのクラス定義である。
メッセージループのフック、Windows APIの低レベル制御、メモリリークを防ぐためのイベント解除の作法まで、プロフェッショナルの実装を凝縮した。
Imports System.ComponentModel
Imports System.Drawing
Imports System.Windows.Forms
”’
”’
Public Class ExTextBox
Inherits TextBox
‘ バリデーションエラー時の背景色を保持するプライベートフィールド
Private ReadOnly _errorBackColor As Color = Color.FromArgb(255, 230, 230)
‘ 通常時の背景色をキャッシュ
Private _defaultBackColor As Color
”’
”’
Public Sub New()
MyBase.New()
‘ デフォルトの背景色を記憶
_defaultBackColor = MyBase.BackColor
End Sub
Region “機能1: フォーカス取得時のテキスト全選択”
Protected Overrides Sub OnEnter(e As EventArgs)
MyBase.OnEnter(e)
‘ BeginInvokeを使うことで、マウスリックによるフォーカス移動時の選択解除バグを防ぐ
‘ メッセージループのキューに選択処理を積むことで、OSのフォーカス処理と競合させない
Me.BeginInvoke(New Action(Sub()
If Not String.IsNullOrEmpty(Me.Text) Then
Me.SelectAll()
End If
End Sub))
End Sub
End Region
Region “機能2: Enterキーによる次コントロールへのシームレスな移動”
”’
”’
Protected Overrides Sub OnKeyDown(e As KeyEventArgs)
If e.KeyCode = Keys.Enter Then
‘ マルチiline(複数行)TextBoxの場合は改行を許可する
If Not Me.Multiline Then
‘ 次のコントロールへフォーカスを移動
‘ SendKeysよりも直接FormのSelectNextControlを叩く方が確実で高速
Dim frm As Form = Me.FindForm()
If frm IsNot Nothing Then
frm.SelectNextControl(Me, True, True, True, True)
e.Handled = True ‘ ビー音(警告音)を完全に抑制
e.SuppressKeyPress = True
End If
End If
End If
MyBase.OnKeyDown(e)
End Sub
End Region
Region “機能3: 視覚的バリデーション色変更”
Private _hasError As Boolean = False
”’
”’
Public Property HasError As Boolean
Get
Return _hasError
Get
Set(value As Boolean)
_hasError = value
UpdateBackgroundColor()
End Set
End Property
Private Sub UpdateBackgroundColor()
If _hasError Then
MyBase.BackColor = _errorBackColor
Else
MyBase.BackColor = _defaultBackColor
End If
End Sub
”’
”’
Public Overrides Property BackColor As Color
Get
Return MyBase.BackColor
Get
Set(value As Color)
If Not _hasError Then
_defaultBackColor = value
End If
MyBase.BackColor = value
End Set
End Property
End Region
End Class
—
3. チーフアーキテクトが解説する「実装の急所」
上記のコードには、数々の現場で血を流しながら得た知見(ハック)が盛り込まれている。中級者が見逃しがちなポイントを解説しよう。
① `BeginInvoke` によるフォーカス全選択の調教
マウスでテキストボックスをクリックしてフォーカスを奪った場合、Windowsの標準挙動は「クリックした位置にカーソルを置く」である。そのため、単に `OnEnter` 内で `SelectAll()` を呼んでも、OSのマウスイベントが後から実行されて選択状態が解除されてしまう。
これを解決するため、`BeginInvoke` を用いてメッセージキューの末尾に `SelectAll()` を遅延実行させている。この一手間で、UIの挙動が劇的に洗練される。
② Enterキーの「ビー音(警告音)」の根絶
Windows Formsにおいて、テキストボックスで `Enter` キーを押すと、デフォルトではシステム警告音(ビープ音)が鳴る。これはチャイルドコントロールがエンターキーを処理しきれずにフォームへ伝播するためだ。
`e.Handled = True` と `e.SuppressKeyPress = True` の両方を立てることで、キーイベントのバブリングを完全に断ち切り、エレガントに次のコントロールへと旅立たせている。
③ デザイナとの調和とカプセル化
`HasError` プロパティを定義し、エラー時に背景色を自動変更するようにした。これにより、業務ロジック側(フォーム側)は `exTextBox1.HasError = True` と書くだけで、UIの見た目(UX)を統一的にコントロールできる。色コードを各画面に散在させる悪習とはこれでオサラバだ。
—
4. 実戦投入:コンパイルと全画面展開のステップ
1. 共通ライブラリプロジェクトの作成
アプリケーション本体とは別に、Class Library (.NET Framework または .NET 6/8 Windows) プロジェクトを作成し、上記のコードを配置してビルドする。
2. ツールボックスへの自動統合
ビルドが成功すると、Visual Studioのツールボックスに自動的に `ExTextBox` が現れる(表示されない場合はプロジェクトのビルド構成やTarget Frameworkを確認すること)。
3. 既存画面の置き換え
フォームデザイナ上の標準 `TextBox` を削除し、`ExTextBox` にドラッグ&ドロップで置き換える。あるいは、デザイナファイル(`.Designer.vb`)の `Friend WithEvents TextBox1 As System.Windows.Forms.TextBox` を `ExTextBox` に置換する一括置換職人技を発動しても良い。
—
5. 終わりに:技術負債の連鎖を断て
「とりあえず動くからコピペでいいや」という妥協の積み重ねが、数年後のレガシーシステムを生み出す。
たかがテキストボックス、されどテキストボックス。こうした細部へのこだわりこそが、メンテナンスコストを激減させ、変更に強い真のエンタープライズシステムを構築する唯一の道である。
社内共通パーツの標準化を進め、退屈で無駄なコーディングから開発陣を解放せよ。
