トップ > 逆引きで学ぶ ユーザーフォーム&コントロール > 追加章 現場で活かせる!実践ユーザーフォーム > B-03 セルの入力値に応じた候補リストを作成・表示(ListBox)

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、標準モジュールにそれぞれ記載します。


【Sheet1】
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
【UserForm1】
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

ページトップへ戻る
Copyright(C) 2009- 坂江 保 All Rights Reserved.