【実務・中級編】Windows Formsのコントロール配列(Control Array)の代替実装:動的に生成したボタンやテキストボックスのイベントをスマートに共有する – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

VB.NETの悪習「コントロール配列」を脱却せよ:動的UI生成の極意とイベント一元管理術

VB6時代、私たちは`Index`プロパティを振るだけで簡単にコントロール配列を作成できました。しかし、.NETの時代になり、その仕組みは過去のものとなりました。

多くの開発者が、無理やり「コントロール配列のようなもの」を実装しようとして、`Control.ControlCollection`を漁ったり、無意味な継承を繰り返してコードを肥大化させています。本稿では、VB.NETの流儀である`AddHandler`を駆使し、メモリを浪費せず、保守性の高い「動的UI管理」の極致を伝授します。

—

1. なぜ「コントロール配列」の模倣は危険なのか

VB.NETにおいて、単にコントロールをリストに詰め込むだけでは不十分です。

  • メモリリークの温床: 動的生成したコントロールを破棄する際、`RemoveHandler`を怠れば、GC(ガベージコレクション)が回収不能なゾンビオブジェクトが発生します。
  • スパゲッティコード: 各コントロールのイベントを個別に書くのは、保守性の観点から「罪」です。

私たちが目指すべきは、「イベントの集約」と「データバインディング的な考え方の導入」です。

—

2. 実践:動的生成とイベントの一元管理

ボタンを10個動的に生成し、クリックした際に「何番目のボタンか」を正確に判定する。この要件を最もスマートに解決するコードが以下です。

Imports System.Drawing

Public Class MainView
‘ 生成したコントロールを保持するリスト(管理用)
Private ReadOnly _dynamicButtons As New List(Of Button)

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
‘ 10個のボタンを動的に配置
For i As Integer = 0 To 9
Dim btn As New Button With {
.Name = “btnDynamic_” & i,
.Text = “No.” & i,
.Location = New Point(10, 10 + (i 30)),
.Tag = i ‘ インデックスをTagに格納しておくのが定石
}

‘ ここが重要:AddHandlerでイベントを一つにまとめる
AddHandler btn.Click, AddressOf CommonButton_Click

Me.Controls.Add(btn)
_dynamicButtons.Add(btn)
Next
End Sub

”’

”’ すべての動的ボタンのイベントをここで一元管理する
”’

Private Sub CommonButton_Click(sender As Object, e As EventArgs)
‘ senderを元の型にキャスト
Dim clickedBtn As Button = DirectCast(sender, Button)

‘ Tagプロパティから情報を取得
Dim index As Integer = CInt(clickedBtn.Tag)

MessageBox.Show($”ボタン {index} が押されました。処理を開始します。”)

‘ データベース連携等の重い処理は、この後に分離すべき
ExecuteBusinessLogic(index)
End Sub

‘ フォームクローズ時のメモリ解放はエンジニアの義務
Private Sub MainView_FormClosing(sender As Object, e As FormClosingEventArgs) Handles MyBase.FormClosing
For Each btn In _dynamicButtons
RemoveHandler btn.Click, AddressOf CommonButton_Click
btn.Dispose()
Next
End Sub
End Class

—

3. 業務システムで「バグらせない」ための3つの鉄則

動的生成を取り入れる際、現場で必ず守るべき規律があります。

① インデックスよりも「Tag」や「ViewModel」を信じろ

`Controls`コレクションのインデックスに依存してはいけません。コントロールの追加・削除によって順序は容易に変わります。必ず`Tag`プロパティにIDを格納するか、あるいは生成したコントロールとデータモデルを対にした`Dictionary(Of String, DataModel)`を保持する設計にしてください。

② イベントの「重さ」を意識せよ

UIスレッドでデータベース接続や重い計算を行ってはいけません。`CommonButton_Click`内で行うのは「どのデータに対して処理を行うか」というIDの特定までです。実際の処理は`Task.Run`や非同期処理へ逃がし、UIのフリーズを回避してください。

③ 破棄(Dispose)の徹底

動的生成したコントロールは、Formのライフサイクルとは別に管理する必要があります。上記コードのように`List(Of T)`で参照を保持し、`FormClosing`で確実に`RemoveHandler`と`Dispose`を行ってください。これを怠ると、長時間稼働する業務端末で確実にメモリ不足を引き起こします。

—

エンジニアへの提言

VB.NETは「古い言語」と揶揄されることもありますが、それは使い手が言語の力を引き出せていないだけです。`AddHandler`による疎結合なイベント処理は、現代の疎結合アーキテクチャの先駆けとも言える手法です。

「とりあえず動く」コードから、「メンテナンスを考慮した堅牢なコード」へ。
あなたが書くその1行が、数年後の開発者を助けるか、あるいは苦しめるか。その責任感を持って、UIを制御してください。

次回の記事では、この動的UIとデータベースをMVVMパターンで結合する、一歩進んだ設計論について深掘りします。期待していてください。

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