業務UIの神髄:NumericUpDownの呪縛を解き、TextBoxを「数値編集の要」へ昇華させる
長年、VB.NETによる基幹業務システムの現場に身を置いていると、「NumericUpDown」というコントロールがいかに実務の足枷であるか、痛感させられる。スピンボタンの誤操作、独自のフォーマット制約、そして何より「UIとしての融通の利かなさ」。
真のプロフェッショナルは、標準コントロールの仕様に屈しない。今回は、`TextBox`を継承し、フォーカス状態に応じて「入力用(生数値)」と「表示用(カンマ・通貨編集)」をシームレスに切り替える、高信頼性カスタムコントロールの実装法を伝授する。
—
1. なぜ「継承」であり「UserControl」ではないのか
まず設計思想の話をする。`UserControl`に`TextBox`を配置する構成は、一見手軽だが、プロパティの多重露呈やフォーカス制御のオーバーヘッドを生む。我々が目指すのは、「既存のTextBoxと完全な互換性を持ちつつ、挙動だけを極限までチューニングした部品」だ。
したがって、`Inherits TextBox`は絶対条件である。これにより、`Control`クラスのライフサイクルを直接制御し、メモリ消費を最小化しつつ、Windows APIの介入も容易にする。
2. 実装:`FormattedNumericTextBox`のコア
この実装の肝は、`GotFocus`と`LostFocus`(または`Enter`/`Leave`)のイベントをフックし、`Text`プロパティの内容を「生データ」と「整形データ」で書き換えるタイミングの制御にある。
Imports System.ComponentModel
Imports System.Globalization
”’
”’
Public Class FormattedNumericTextBox
Inherits TextBox
‘ 通貨記号やフォーマットを保持するプライベート変数
Private _rawText As String = “”
Public Property CurrencySymbol As String = “¥”
‘ 入力中は数値のみを許可するよう制御
Protected Overrides Sub OnKeyPress(e As KeyPressEventArgs)
‘ 数値、バックスペース、マイナス記号以外を弾く
If Not (Char.IsDigit(e.KeyChar) OrElse e.KeyChar = ControlChars.Back OrElse e.KeyChar = “-“) Then
e.Handled = True
End If
MyBase.OnKeyPress(e)
End Sub
‘ フォーカス取得時:数値のみに戻す
Protected Overrides Sub OnEnter(e As EventArgs)
MyBase.Text = _rawText
MyBase.OnEnter(e)
End Sub
‘ フォーカス喪失時:カンマと通貨記号を付与
Protected Overrides Sub OnLeave(e As EventArgs)
_rawText = MyBase.Text
Dim val As Decimal
If Decimal.TryParse(_rawText, val) Then
‘ 3桁区切り+通貨記号
MyBase.Text = String.Format(“{0}{1:N0}”, CurrencySymbol, val)
End If
MyBase.OnLeave(e)
End Sub
‘ メモリ最適化:不要なリソースは即座に解放
Protected Overrides Sub Dispose(disposing As Boolean)
If disposing Then
_rawText = Nothing
End If
MyBase.Dispose(disposing)
End Sub
End Class
3. シニアエンジニアが意識すべき「極限の知見」
① Windows APIによる描画の最適化
もし、大量のデータをグリッド状に配置した画面でこのコントロールを多用する場合、`WM_PAINT`イベントがボトルネックになる可能性がある。その際は、`SetStyle`メソッドで`UserPaint`と`DoubleBuffer`を有効にすることを忘れてはならない。
Public Sub New()
‘ ダブルバッファリングでちらつきを抑制
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer Or ControlStyles.AllPaintingInWmPaint, True)
End Sub
② バインディングとビジネスロジックの分離
この実装の唯一の弱点は、データバインディング時に「整形済み文字列」がDTOに送られてしまうことだ。これを防ぐには、`DataBindings.Add`の際、`Binding.Parse`イベントと`Binding.Format`イベントをオーバーライドする必要がある。これが、業務システムの堅牢性を担保する「設計の境界線」である。
③ レガシー環境でのメモリ管理
VB.NETのガベージコレクタは優秀だが、Windows Formsのコンポーネントはしばしば`Handle`を解放し損ねる。`Dispose`メソッドをオーバーライドし、明示的な変数クリアと`GC.SuppressFinalize(Me)`の呼び出しを行うことで、長時間稼働する業務アプリの「メモリリークによるスローダウン」を確実に防ぐ。
4. 最後に:職人の矜持
UI部品は、使う側が「何も考えずに配置すれば動く」のが理想だ。しかし、その裏側にあるのは、フォーカス移動のたびにメモリを汚さない計算と、Windowsの描画サイクルを熟知した計算である。
「たかがテキストボックスのカンマ編集」と侮るなかれ。この細部に宿る品質こそが、エンドユーザーが毎日触れる業務システムの「使い心地」という名の信頼を形作る。
君たちのコードが、明日も現場で安定して稼働することを願っている。
