B-03 セルの入力値に応じた候補リストを作成・表示(ListBox)2022.05.23
次の書籍の第1章~5章を公開しています。
「Excel VBA 逆引きで学ぶ ユーザーフォーム&コントロール」
「セルに入力した単語を、データベースから検索し、該当するデータを候補リストとして選択できるようにしたい」といったことはないでしょうか。
今回は、セルに入力した値に応じて候補リストが表示されるフォームを作成します。
リストボックスの実践応用サンプルです。
コンボボックスのバージョンとは異なり、フォームはキーボードのみで操作できる仕様としています。
【例】セルに「ブラック」と入力すると データベースから「ブラック」を含むデータを抽出しリスト表示する
【動画】
・セルに入力した値が含まれる商品名のリストを作成
・リストを選択すると対象セルの一つ左のセル(B列)に商品コードが入力される
(D列・E列はB列のコードを参照し商品名・サイズを表示する数式が入力されています)
このような動作の実現にはいくつかの方法が考えられますが、最も簡単だと思われる方法で実装します。
サンプルファイル ダウンロード
準備 1
シートを2枚準備します。
■「入力・検索用」シート
B列に商品コードを入力すると、D列に商品名、E列にサイズが表示されるように数式を設定します。C列は検索欄として使用します。
■「データ」シート
データベース用のシートです。
準備 2
ユーザーフォームを挿入し、ListBoxコントロールを追加します。
■ListBoxコントロール
役割:リスト表示
オブジェクト名:ListBox1
個数:1
サンプルコード
コードは、Sheet1、UserForm1、標準モジュールにそれぞれ記載します。
Option Explicit '【定数】 Const TGT_RANGE As String = "C2:C11" '検索欄表示対象のセル範囲
'■シートイベント(Sheet1) '+++ Changeイベント +++ 'セルが変更された場合に発生するイベント Private Sub Worksheet_Change(ByVal Target As Range) '変更したセルが検索欄表示対象セル範囲外の場合は抜ける If Intersect(Range(TGT_RANGE), Target) Is Nothing Then Exit Sub '変更したセルが複数の場合は抜ける If 1 < Target.Cells.Count Then Exit Sub '変更したセルの値が空の場合は抜ける If Len(Target.Value) = 0 Then Exit Sub '変更したセルのアドレスをグローバル変数に格納 strTarget = Target.Address 'フォームを表示 UserForm1.Show End Sub
Option Explicit '■フォームイベント(UserForm) '+++ Initializeイベント +++ Private Sub UserForm_Initialize() Dim strValue As String Dim sinLeft As Single Dim sinTop As Single With ActiveSheet.Range(strTarget) strValue = .Value '対象セルの値を取得 sinLeft = .Offset(, 1).Left '対象セルの一つ右のセル水平位置取得 sinTop = .Offset(, 1).Top '対象セルの一つ右のセル垂直位置取得 End With '検索ウィンドウ(フォーム)の表示位置設定 Dim dblX As Double Dim dblY As Double With ActiveWindow dblX = .PointsToScreenPixelsX(0) / 96 * 72 + sinLeft * .Zoom / 100 dblY = .PointsToScreenPixelsY(0) / 96 * 72 + sinTop * .Zoom / 100 End With With Me .StartUpPosition = 0 .Left = dblX .Top = dblY - 28 '-28:フォーム垂直位置調整(見出しの高さ分) End With '「商品データ」シートから商品コードと商品名を取得し変数に格納 With Sheet2 Dim varList As Variant Dim lngLastRow As Long lngLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row If lngLastRow = 2 Then lngLastRow = 3 varList = .Range("A2:B" & lngLastRow).Value End With '対象セルの値が商品名に含まれていたらListBox1のリストに追加する Dim i As Long Dim c As Long With ListBox1 For i = 1 To UBound(varList) If 0 < InStr(1, varList(i, 2), strValue, vbTextCompare) Then .AddItem varList(i, 2), c 'リスト1列目に商品名を追加 .List(c, 1) = varList(i, 1) 'リスト2列目に商品コードを追加 c = c + 1 End If Next .ListIndex = 0 '最初のリストを選択 End With End Sub
'+++ QueryCloseイベント +++ 'フォームを閉じる際に発生 Private Sub UserForm_QueryClose(Cancel As Integer, CloseMode As Integer) Range(strTarget).ClearContents If CloseMode = vbFormControlMenu Then Range(strTarget).Offset(, -1).ClearContents End Sub
'■リストボックスイベント(ListBox1) '+++ Changeイベント +++ 'リストボックスに変更があった場合に発生 Private Sub ListBox1_Change() Range(strTarget).Offset(, -1).Value = ListBox1.List(ListBox1.ListIndex, 1) End Sub
'+++ KeyDownイベント +++ 'リストボックスでキーを押した場合に発生 Private Sub ListBox1_KeyDown(ByVal KeyCode As MSForms.ReturnInteger, ByVal Shift As Integer) Select Case KeyCode Case vbKeyReturn Unload Me Case vbKeyEscape Range(strTarget).Offset(, -1).Value = "" Unload Me End Select End Sub
Option Explicit '+++ 宣言セクション +++ 'グローバル変数 Public strTarget As String
簡単な解説
■シートイベント
Sheet1のイベントの目的は、検索欄で値を変更したセルのアドレスを取得し、フォームを表示することです。正確なセルアドレスを取得するために、セル範囲の確認、セル個数の確認、入力値の確認を行っています。
コード内のRange(TGT_RANGE)は、検索欄であるセル範囲[C2:C11]を表し、フォームの表示対象セルです。
If Intersect(Range(TGT_RANGE), Target) Is Nothing Then Exit Sub は、
Range(TGT_RANGE)と変更されたセル(Target)の共通セルを確認しています。
共通セルがなければ、変更されたセルは[C2:C11]の範囲外なので、イベントを抜けるようにしています。
■フォームの表示位置
フォームは、Sheet1のセル範囲[C2:C11]に変更があった場合に、アクティブセルの一つ右のセルに表示されます。
フォーム表示位置を設定する詳細はこちらをご覧ください。
フォームの表示対象セル範囲を変更する場合は、Sheet1モジュールの赤文字箇所を変更します。
Const TGT_RANGE As String = "C2:C11"
■リストの作成
フォームのInitializeイベントでListBoxのリスト作成しています。
Sheet2(「商品データ」シート)のA列の最終行を取得し、A列・B列(商品コード・商品名)の値を変数varListに格納しています。
変数varListに、セルの入力値が含まれているかどうかの判定は、次のコードで行っています。
If 0 < InStr(1, varList(i, 2), strValue, vbTextCompare) Then※
※vbTextCompareは大文字・小文字や全角・半角を区別しません
区別させる場合は vbBinaryCompare を指定します
リストは、AddItemメソッドで追加し、1列目には商品名、2列目には商品コードを設定しています。(リスト内では、1列目は「0」、2列目は「1」のように、0から始まる番号で管理されています。行も同様になります。)
複数列表示する設定にしていないため、リストには1列目の商品名のみ表示されます。
リストの追加を終えたら、
ListBox1.ListIndex = 0 で最初のリストを選択状態にしています。
■リストの選択と決定
リストボックスのChangeイベントは、リストボックスの選択に変更があった場合に発生します。
リストボックスの選択の変更は、マウスでクリックするか、方向キー「↑」「↓」を押します。
リストボックスに変更があった場合、選択したリストの2列目(商品コード)の値を、対象セルの一つ左のセルに入力します。
またKeyDownイベントでは、引数のKeyCodeで押されたキーを判定し、[Enter]キーか[Esc]キーが押された場合に処理を実行しています。
※KeyCode
引数 KeyCodeは押されたキーのコードを返します。
返されるコードは整数値ですが、対応した組み込み定数が準備されています。
一般的にはそれを利用することが多いです。型としてはvbKey○○になります。
[ MSDN KeyCode組み込み定数一覧 ]
■フォームのクローズ時
フォームを閉じる際に、対象となったセルの値をクリアしています。
また[×]ボタンを押して閉じる場合は「キャンセル」とみなし、商品コードをクリアしています。
[×]ボタンの判定
書籍紹介140以上のサンプルファイル付き!
知りたいがすぐわかる! やりたいがすぐできる!
「Excel VBA 逆引きで学ぶ ユーザーフォーム&コントロール」(Kindle版)
ユーザーフォームを扱えると、VBAでできることが大きく広がります!
本書では、知りたいこと、やりたいことから、逆引きで学びを深められます。
■ 購入:amazon