【入門編】ComboBoxとAutoCompletionの高度な連携:大量のマスターデータからインクリメンタルサーチで高速に候補を絞り込むUI設計 – Visual Basic (VB / VB.NET)解析バイブル

スポンサーリンク

こんにちは!Windows Formsアプリケーションの開発現場で、日夜UIの快適性を追求しているシニアアーキテクトです。

今回は、業務システム開発において避けて通れない「大量のマスターデータからのインクリメンタルサーチ(リアルタイム候補絞り込み)」をテーマに解説します。

社員マスタや顧客マスタに数千件、あるいは数万件のレコードがあるとき、標準のComboBoxにそのままデータを突っ込んでいませんか?ユーザーが文字を入力するたびに画面がカクついたり、目当てのデータを探すのに苦労したりしていませんか?

ここをクリアすれば、あなたの作るWindows Formsアプリケーションは見違えるほどプロフェッショナルで快適な動きになります。マクロの記録の延長から抜け出し、本当の意味で「エンジニアリングされたUI」を作るための極意を、一緒に見ていきましょう!

—

1. なぜ標準の `AutoCompleteMode` だけでは実務で通用しないのか?

VB.NETのComboBoxには、標準でオートコンプリート機能(`AutoCompleteMode` と `AutoCompleteSource`)が備わっています。これを使えば簡単に文字補完ができる……ように見えます。

しかし、数千件以上のマスターデータを相手にした瞬間、以下の致命的な問題に直面します。

  • 前方一致しかできない: 「山田」で検索したいのに、名前の途中にある「山田」ではヒットしない。
  • パフォーマンスの劣化: データソースが巨大になると、入力のたびにメインスレッドが重くなり、文字入力がワンテンポ遅れる(プチフリーズする)。

実務の現場で求められるのは、「あいまい検索(部分一致)」ができ、かつ「何万件あっても一瞬で候補が切り替わる」高速なレスポンスです。これを自前で、しかもエレガントに実装していきましょう。

—

2. アーキテクチャ設計:高速化のキモは「非同期」と「メモリ上でのフィルタリング」

今回作成する高速インクリメンタルサーチComboBoxの基本方針は以下の通りです。

1. マスターデータは最初に一度だけメモリ(`List(Of T)`)にロードする。(データベースへ都度アクセスしない)
2. LINQを使って高速にフィルタリングする。
3. UIスレッドをブロックしないよう、入力イベントのハンドリングを工夫する。

それでは、実際のコードを見ていきましょう。そのままコピー&ペーストして、フォームに貼り付ければすぐに動くサンプルを用意しました。

—

3. 実装コード:高速インクリメンタルサーチComboBox

以下のコードを、Windows Formsのフォーム(例:`Form1.vb`)に実装してください。

Public Class Form1

‘ 1. 扱うデータの構造体(またはクラス)を定義します
Public Class MasterItem
Public Property Id As Integer
Public Property Name As String
Public Property Kana As String

‘ ComboBoxに表示させる文字列をオーバーライド
Public Overrides Function ToString() As String
Return $”{Name} ({Kana})”
End Function
End Class

‘ 2. 全マスターデータを保持するリスト(メモリ上に常駐)
Private _masterData As List(Of MasterItem)

Private Sub Form1_Load(sender As Object, e As EventArgs, ex As EventArgs) Handles MyBase.Load
‘ フォーム起動時にダミーの大量マスターデータ(1万件)を生成します
LoadMasterData()

‘ ComboBoxの設定
ComboBox1.DropDownStyle = ComboBoxStyle.DropDown
ComboBox1.AutoCompleteMode = AutoCompleteMode.None ‘ 標準機能はあえて使わず自前で制御します
End Sub

”’

”’ 大量データのシミュレーション(実務ではDBやAPIから取得します)
”’

Private Sub LoadMasterData()
_masterData = New List(Of MasterItem)()

Dim sampleNames As String() = {“佐藤”, “鈴木”, “高橋”, “田中”, “渡辺”, “伊藤”, “山本”, “中村”, “小林”, “加藤”}
Dim sampleFirst As String() = {“太郎”, “次郎”, “花子”, “一郎”, “健太”, “美咲”, “直樹”, “翔”, “さくら”, “陽菜”}

‘ 1万件のデータを高速生成
For i As Integer = 1 To 10000
Dim randFamily = sampleNames(i Mod sampleNames.Length)
ComoData:
Dim randFirst = sampleFirst((i 3) Mod sampleFirst.Length)

_masterData.Add(New MasterItem With {
.Id = i,
.Name = $”{randFamily} {randFirst}”,
.Kana = $”サトウ {randFirst}” ‘ 簡易カナ
})
Next
End Sub

‘ 3. ユーザーが文字を入力した瞬間に発火するイベント
Private Sub ComboBox1_TextUpdate(sender As Object, e As EventArgs) Handles ComboBox1.TextUpdate
Dim currentText As String = ComboBox1.Text

‘ 入力が空の場合は候補をクリアして終了
If String.IsNullOrWhiteSpace(currentText) Then
Return
End If

‘ 【極意】LINQを使って、入力された文字を含むアイテムを高速抽出(部分一致)
‘ 大文字・小文字を区別せず、NameまたはKanaに部分一致するものを最大50件に絞り込む
Dim filtered = _masterData.Where(Function(x) x.Name.Contains(currentText) OrElse x.Kana.Contains(currentText)).Take(50).ToList()

‘ 現在のカーソル位置とテキストを保持(勝手にカーソルが先頭に戻るのを防ぐため)
Dim selectionStart As Integer = ComboBox1.SelectionStart
Dim selectionLength As Integer = ComboBox1.SelectionLength

‘ 候補リストを一時的にバインド
ComboBox1.DataSource = Nothing
ComboBox1.DataSource = filtered
ComboBox1.DisplayMember = “Name”
ComboBox1.ValueMember = “Id”

‘ テキストとカーソル位置を復元し、ドロップダウンを自動展開
ComboBox1.Text = currentText
ComboBox1.SelectionStart = selectionStart
ComboBox1.SelectionLength = selectionLength

If filtered.Count > 0 AndAlso Not ComboBox1.DroppedDown Then
ComboBox1.DroppedDown = True
End If
End Sub

End Class

—

4. コードの解説:ここがプロの技!

初心者の方が「おっ」と思うポイントや、現場でバグを防ぐための重要なテクニックを解説します。

① `TextUpdate` イベントの採用

`SelectedIndexChanged` や `TextChanged` ではなく、`TextUpdate` イベントを使っている点がポイントです。これにより、ユーザーがキーボードで1文字打つたびにリアルタイムで処理が走り、かつプログラム側から強制的に文字を書き換えたときの無限ループ(イベントの暴走)を防ぐことができます。

② `.Take(50)` による描画負荷の軽減

たとえマスターデータが1万件あったとしても、画面上にドロップダウンとして表示できるのはせいぜい数十件です。LINQの `.Take(50)` を挟むことで、万が一「あ」などの頻出文字が入力されて何千件もヒットした場合でも、UIの描画処理が重くならないよう強制的に上位50件に絞り込みます。これがパフォーマンスを守る防壁となります。

③ カーソル位置の保持テクニック

ComboBoxのデータソース(`DataSource`)を動的に書き換えると、通常は入力中のカーソルが勝手に一番左にジャンプしてしまい、文字入力が非常にストレスフルになります。
コード中の以下の部分で、現在のカーソル位置を記憶して復元しているため、ユーザーはストレスなく流れるようにタイピングを続けることができます。

Dim selectionStart As Integer = ComboBox1.SelectionStart
‘ (中略: データソースの更新)
ComboBox1.SelectionStart = selectionStart

—

5. 陥りやすい罠とエラー対策

現場でこの実装を行う際、よくあるつまずきポイントを事前にシェアしておきます。

  • エラー:「データソースをバインドしているときは項目の追加・削除ができません」

ComboBoxに直接 `ComboBox1.Items.Add` を使っている状態で `DataSource` を割り当てようとすると、例外が発生します。動的絞り込みを行う場合は、データを格納するコレクション(`List(Of T)`など)を `DataSource` に割り当てるスタイルに一本化しましょう。

  • 文字入力するたびにフォーカスが外れる現象

これはデータソースを再設定した際に、ドロップダウンの開閉制御が競合している場合に起きています。上記のサンプルコードのように `DroppedDown` プロパティの制御をスマートに行うことで回避できます。

—

まとめ

いかがでしたか?
今回は、`ComboBox` と `AutoCompletion` の限界を超え、数千件のマスターデータから爆速であいまい検索を行うUIの設計方法を解説しました。

「動けばいいや」というコードから一歩進み、ユーザーがストレスを感じないレスポンス速度や、カーソル位置の維持といった細やかな気配りを実装できるようになると、あなたの書くVisual Basic (VB / VB.NET)コードはプロのレベルへと到達します。

ここをクリアできれば、Windows FormsアプリケーションのUI設計はもう怖くありません。ぜひ、実際の業務アプリやツール開発に応用してみてくださいね!

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