WinFormsアプリケーション向けに中間一致サジェスト機能付きテキストボックスを自作しましたので、実装方法をまとめます。
- 日本語IMEにも対応
- 中間一致(部分一致)検索
- 候補はListBoxで表示
- リストはユーザーコントロール外に動的追加してZオーダー問題を回避
- 最小化/復元にも自然対応
- 非常にシンプル&安定
業務アプリにも安心して使えるレベルを目指しました✨
1.前提条件
- Visual studio 2022 Version 17.13.6
- .Net 9
なお、すべてのソースコードを公開しています。
2.目標 🎯
普通のテキストボックスに入力すると、その下にサジェスト候補が中間一致で表示され、選択できるUIを作ります。
- 入力確定は Enter キー or 候補リストクリック
- 上下キーでリスト内移動
- ESCキーでリストを閉じる
- リストはテキストボックスの下にポップアップ表示
- Zオーダー問題なし(親フォームに直追加)
3.画面イメージ
中間一致サジェスト(リストに表示されていない「かな」からも検索している)
サジェスト選択内容からテキストを修正するとIDが自動でリセットされる
デザイン時のプロパティ(プレースホルダやリストへの表示件数など指定可能)
4.実装ポイント 📄
A) リストボックスはユーザーコントロールの外に出す!
内部にListBoxを持つと、他のコントロールに隠れたり、最前面制御が難しいため、
親フォームに直接ListBoxを追加して管理しています。
_parentForm.Controls.Add(_listBox);
_parentForm.Controls.SetChildIndex(_listBox, 0);
これで、常に自然な前面表示ができます!
B) 「↓」または「エンター」でサジェストを呼び出す
検索文字列が1文字ずつ変化するたびにサジェストすると邪魔。「↓」と「エンター」で呼び出す。
日本語入力中(変換中)に無理にサジェストすると、UXが悪化するため、IME確定後(Ime変換確定後イベント)にサジェストを開始します。
C) 入力キーはすべてテキストボックス側で処理
ListBoxにフォーカスを取らせず、キーイベントは常にテキストボックス側で受け取り、自然な操作感を実現しています。
D) リストのサイズは項目数と最大表示数で動的計算
表示件数が少ないときでも、リストサイズは動的に決まります。
int height = Math.Max(30, itemHeight * Math.Min(最大表示件数, マッチ.Count));
E) 親フォームが閉じる時にリストボックスをDispose
親フォームが閉じた後にリストが残らないよう、
_parentForm.FormClosed += (s, e) => _listBox.Dispose();
で安全に破棄しています。
5.最終構成 📦
- ImeSuggestTextBox(ユーザーコントロール)
- ImeTextBox(カスタムテキストボックス、IME確定検出用)
- ListBox(親フォーム直下に動的配置)
- サジェスト候補クラス(サジェストアイテム)
6.ソースコード(抜粋)🔥
public override string Text
{
get => _textBox.Text ?? string.Empty;
set => _textBox.Text = value ?? string.Empty;
}
private void ShowPopupサジェスト()
{
HidePopup();
var マッチ = _候補リスト
.Where(x => x.ID.Contains(入力値) || x.名称.Contains(入力値) || x.補助検索キー.Contains(入力値))
.Take(最大表示件数)
.ToList();
if (マッチ.Count == 0) return;
_listBox.DataSource = null;
_listBox.Items.Clear();
_listBox.DataSource = マッチ;
_listBox.DisplayMember = nameof(サジェストアイテム.ToString);
var screenPos = _textBox.PointToScreen(new Point(0, _textBox.Height));
var clientPos = _parentForm!.PointToClient(screenPos);
_listBox.Location = clientPos;
int height = Math.Max(30, _listBox.ItemHeight * Math.Min(最大表示件数, マッチ.Count));
_listBox.Size = new Size(this.Width, height);
_listBox.Visible = true;
_listBox.BringToFront();
}
7.最後に 📝
この構成は、
✅ シンプル
✅ 軽量
✅ 実務アプリでも安定
をすべて両立できることを目指しました。
中間一致サジェストは業務アプリで大活躍するので、
ぜひ参考にしてもらえたら嬉しいです🌸