【VBA】リストボックスでデータ選択画面を作る方法(コピペOK)

VBA
スポンサーリンク
スポンサーリンク
  1. この記事でわかること
    1. どんな場面で使う?
  2. 完成イメージ(Before / After)
  3. 実行前の準備
    1. バックアップを取る
    2. Excelをマクロ有効ブック(.xlsm)で保存する
  4. 手順(UserFormの作成 → コード貼り付けまで約10分)
    1. Step 1: VBE(コードを書く画面)を開く
    2. Step 2: ユーザーフォームを挿入する
    3. Step 3: ListBoxを配置する
    4. Step 4: コマンドボタンを配置する
    5. Step 5: コードを貼り付ける
    6. Step 6: 標準モジュールにフォーム表示用コードを貼り付ける
    7. Step 7: 実行する
  5. コード(基本版)– AddItemでリストに項目を追加して1つ選択
    1. UserFormモジュールに貼り付けるコード
    2. 標準モジュールに貼り付けるコード
    3. 書き換えポイント
  6. コード(実務版)– シートのデータを読み込み → 複数選択 → 別シートに転記
    1. UserFormモジュールに貼り付けるコード
    2. 標準モジュールに貼り付けるコード
    3. 書き換えポイント
    4. シート構成の例
  7. よくある落とし穴5選
    1. 1. RowSourceで別シートのデータが表示されない
    2. 2. AddItemとRowSourceを同時に使ってエラーになる
    3. 3. MultiSelectなのにListIndexだけで取得して1件しか取れない
    4. 4. ListBoxのインデックスが0始まりでExcelの行番号とずれる
    5. 5. Unload Meを忘れてフォームがメモリに残り続ける
    6. VBAのListBoxに項目が表示されないときの対処法
    7. VBAのListBoxで複数選択した項目を取得できないときの対処法
  8. FAQ
    1. Q1: AddItemとRowSourceはどちらを使うべき?
    2. Q2: ListBoxに複数列を表示するには?
    3. Q3: 選択をリセット(全解除)するボタンを付けるには?
    4. Q4: 応用: リストの項目数が多いとき、検索で絞り込むには?
    5. Q5: ListBoxとComboBoxの違いは?
  9. まとめ
    1. 関連記事
  10. 次にやりたくなること

この記事でわかること

  • ListBoxにAddItemで項目を追加して、ユーザーに1つ選ばせる基本操作
  • MultiSelect + Selectedで複数項目を選択させる方法
  • シートのデータをListBoxに読み込み→選択→別シートに転記する実務コード

対象: Excel 2016以降 / Microsoft 365、Windows 10/11

どんな場面で使う?

  • 商品リストや社員リストからユーザーに複数項目を選ばせたいとき
  • 手作業のスクロールとコピペを選択画面で置き換えたいとき
  • 選択した項目だけを別シートに自動転記したいとき
  • 検索キーワードで絞り込めるデータ選択UIを作りたいとき

完成イメージ(Before / After)

Before(手作業):

シートの一覧を目視でスクロール → 必要な行を見つけてコピー → 別シートにペースト → これを必要な件数分繰り返す

After(リストボックスで選択):

ユーザーフォームを開く → ListBoxに一覧が表示される → 必要な項目をクリックで選択(複数可) → 「転記」ボタンを押す → 選択データだけが別シートに自動転記される

自分も以前、シートにある100件以上の商品リストから必要な10件を手動で探してコピペしていた。スクロールして目視で探すのが正直しんどかった。リストボックスで選択画面を作ってからは、チェックして「実行」を押すだけ。探す時間がゼロになった。同じように一覧から手作業で探している人が、この記事で選択画面をサクッと作れるようになればうれしい。

ListBoxを使えば、シートのデータを一覧表示して、ユーザーに必要な項目だけを選ばせることができる。

なお、UserFormの基本的な作り方(フォームの挿入・コントロールの配置)は 入力フォーム(UserForm)で手入力ミスを防ぐ方法 を参照。この記事はListBoxの操作に集中する。

実行前の準備

バックアップを取る

実務版コードは転記先シートにデータを書き込む。必ずファイルのコピーを別フォルダに保存してから実行する。

Excelをマクロ有効ブック(.xlsm)で保存する

拡張子が .xlsx のままだとマクロが保存できない。

  1. 「ファイル」→「名前を付けて保存」
  2. ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
  3. 保存

手順(UserFormの作成 → コード貼り付けまで約10分)

Step 1: VBE(コードを書く画面)を開く

  1. Excelで Alt + F11 を押す

Step 2: ユーザーフォームを挿入する

  1. VBEのメニュー →「挿入」→「ユーザーフォーム」
  2. 空のフォーム(UserForm1)が表示される
  3. フォームのサイズを幅350×高さ300程度に調整する

Step 3: ListBoxを配置する

  1. ツールボックスが表示されていない場合は、メニュー→「表示」→「ツールボックス」
  2. ツールボックスの「リストボックス」(格子状のアイコン)をクリック
  3. フォーム上でドラッグしてListBoxを配置する(名前は ListBox1 のまま)
  4. ListBoxのサイズを幅300×高さ200程度にする

Step 4: コマンドボタンを配置する

  1. ツールボックスの「コマンドボタン」アイコンをクリック
  2. フォーム上でドラッグしてボタンを配置(名前は CommandButton1 のまま)
  3. ボタンのCaptionプロパティを「実行」に変更する(プロパティウィンドウで変更)

Step 5: コードを貼り付ける

  1. フォーム上でダブルクリック(または右クリック→「コードの表示」)
  2. UserFormのコードウィンドウが開く
  3. 下記のコードをそのまま貼り付ける

Step 6: 標準モジュールにフォーム表示用コードを貼り付ける

  1. VBEのメニュー →「挿入」→「標準モジュール」
  2. フォーム表示用のSubプロシージャを貼り付ける

Step 7: 実行する

  1. Alt + F8ShowListBoxForm を選んで「実行」

ボタンに割り当てれば毎回Alt+F8を押さなくて済む。方法は マクロをボタン1つで実行する方法 を参照。

コード(基本版)– AddItemでリストに項目を追加して1つ選択

まずはListBoxの動きを確認する最小コード。フォームを開くとリストに固定の項目が表示され、1つ選んで「実行」ボタンを押すとMsgBoxで選択結果を表示する。

UserFormモジュールに貼り付けるコード


'============================================================
' ■ ListBox基本版(UserFormモジュールに貼り付け)
'   → フォームを開くとリストに項目を表示
'   → 1つ選んで「実行」ボタンでMsgBox表示
'============================================================
Private Sub UserForm_Initialize()

    '--- ListBoxに項目を追加(AddItem方式)
    With Me.ListBox1
        .Clear                          '← 既存の項目をクリア
        .AddItem "商品A - 単価500"
        .AddItem "商品B - 単価800"
        .AddItem "商品C - 単価1200"
        .AddItem "商品D - 単価300"
        .AddItem "商品E - 単価950"
    End With

End Sub

Private Sub CommandButton1_Click()

    '--- 選択チェック
    If Me.ListBox1.ListIndex = -1 Then
        MsgBox "項目を選択してください。", vbExclamation
        Exit Sub
    End If

    '--- 選択された項目を取得
    Dim selectedItem As String
    selectedItem = Me.ListBox1.List(Me.ListBox1.ListIndex)

    MsgBox "選択: " & selectedItem, vbInformation

    Unload Me

End Sub

標準モジュールに貼り付けるコード


'============================================================
' ■ フォーム表示用(標準モジュールに貼り付け)
'============================================================
Sub ShowListBoxForm()
    UserForm1.Show
End Sub

書き換えポイント

箇所 説明 初期値
AddItem "商品A - 単価500" リストに表示する項目 自分のデータに合わせて変更
ListBox1 ListBoxのコントロール名 ListBox1(既定名のまま)
CommandButton1 ボタンのコントロール名 CommandButton1(既定名のまま)

ポイント: ListIndex は0始まり。未選択時は -1 を返す。選択チェックには ListIndex = -1 を使う。

コード(実務版)– シートのデータを読み込み → 複数選択 → 別シートに転記

この方法を覚えてからは、月次の発注処理でもリストボックスから選択→自動転記にしている。手作業のコピペミスがなくなった。

前提のシート構成:

  • 「データ」シート: A列に商品コード、B列に商品名、C列に単価(1行目はヘッダー)
  • 「転記先」シート: 選択したデータの書き込み先(自動作成される)

※ 「転記先」シートの既存データはクリアされます。実行前にバックアップを取ってください。

UserFormモジュールに貼り付けるコード


'============================================================
' ■ ListBox実務版(UserFormモジュールに貼り付け)
'   → 「データ」シートの一覧をListBoxに読み込み
'   → 複数選択 → 「転記」ボタンで「転記先」シートに書き出し
'============================================================

'--- ★書き換えポイント ---
Private Const SOURCE_SHEET As String = "データ"      '← データ元のシート名
Private Const TARGET_SHEET As String = "転記先"       '← 転記先のシート名
Private Const START_ROW As Long = 2                   '← データの開始行(1行目ヘッダー)
Private Const COL_COUNT As Long = 3                   '← 表示する列数(A〜C列の3列)
'--- ★ここまで ---

Private Sub UserForm_Initialize()

    '--- ListBoxの設定
    With Me.ListBox1
        .Clear
        .ColumnCount = COL_COUNT            '← 複数列を表示
        .ColumnWidths = "80;120;60"         '← 各列の幅(ポイント単位)
        .MultiSelect = fmMultiSelectMulti   '← クリックで複数選択
    End With

    '--- データシートから項目を読み込み
    Dim wsSource As Worksheet
    On Error Resume Next
    Set wsSource = ThisWorkbook.Worksheets(SOURCE_SHEET)
    On Error GoTo 0

    If wsSource Is Nothing Then
        MsgBox "「" & SOURCE_SHEET & "」シートが見つかりません。", vbExclamation
        Unload Me
        Exit Sub
    End If

    Dim lastRow As Long
    lastRow = wsSource.Cells(wsSource.Rows.Count, 1).End(xlUp).Row

    If lastRow < START_ROW Then
        MsgBox "データがありません。", vbExclamation
        Unload Me
        Exit Sub
    End If

    '--- AddItemでシートのデータを読み込み
    Dim r As Long
    Dim c As Long
    For r = START_ROW To lastRow
        Me.ListBox1.AddItem wsSource.Cells(r, 1).Value   '← 1列目を追加
        For c = 2 To COL_COUNT
            Me.ListBox1.List(Me.ListBox1.ListCount - 1, c - 1) = _
                wsSource.Cells(r, c).Value                '← 2列目以降を設定
        Next c
    Next r

    '--- ボタンのキャプション設定
    Me.CommandButton1.Caption = "選択データを転記"
    Me.Caption = "データ選択画面"

End Sub

Private Sub CommandButton1_Click()

    '--- 選択チェック(Selected をループして確認)
    Dim i As Long
    Dim c As Long
    Dim hasSelection As Boolean
    hasSelection = False

    For i = 0 To Me.ListBox1.ListCount - 1
        If Me.ListBox1.Selected(i) Then
            hasSelection = True
            Exit For
        End If
    Next i

    If Not hasSelection Then
        MsgBox "項目を1つ以上選択してください。", vbExclamation
        Exit Sub
    End If

    '--- 転記先シートの準備
    Dim wsTarget As Worksheet
    On Error Resume Next
    Set wsTarget = ThisWorkbook.Worksheets(TARGET_SHEET)
    On Error GoTo 0

    '--- 転記先シートがなければ新規作成
    If wsTarget Is Nothing Then
        Set wsTarget = ThisWorkbook.Worksheets.Add(After:= _
            ThisWorkbook.Worksheets(ThisWorkbook.Worksheets.Count))
        wsTarget.Name = TARGET_SHEET
    End If

    '--- 転記先をクリアしてヘッダーを書き込み
    wsTarget.Cells.Clear
    wsTarget.Cells(1, 1).Value = "商品コード"
    wsTarget.Cells(1, 2).Value = "商品名"
    wsTarget.Cells(1, 3).Value = "単価"

    '--- 選択された項目を転記
    Dim writeRow As Long
    writeRow = 2
    Dim transferCount As Long
    transferCount = 0

    For i = 0 To Me.ListBox1.ListCount - 1
        If Me.ListBox1.Selected(i) Then
            For c = 0 To COL_COUNT - 1
                wsTarget.Cells(writeRow, c + 1).Value = Me.ListBox1.List(i, c)
            Next c
            writeRow = writeRow + 1
            transferCount = transferCount + 1
        End If
    Next i

    MsgBox transferCount & " 件のデータを「" & TARGET_SHEET & "」シートに転記しました。", vbInformation

    Unload Me

End Sub

標準モジュールに貼り付けるコード


'============================================================
' ■ フォーム表示用(標準モジュールに貼り付け)
'============================================================
Sub ShowListBoxForm()
    UserForm1.Show
End Sub

書き換えポイント

変数/定数 説明 初期値
SOURCE_SHEET データ元のシート名 "データ"
TARGET_SHEET 転記先のシート名 "転記先"
START_ROW データの開始行 2(1行目はヘッダー)
COL_COUNT 表示する列数 3(A〜C列)
ColumnWidths 各列の表示幅 "80;120;60"
ヘッダーの文字列 転記先シートのヘッダー 自社のデータに合わせて変更

シート構成の例

「データ」シート(データ元):

A列(商品コード) B列(商品名) C列(単価)
P001 ボールペン(黒) 120
P002 ノートA5 250
P003 クリアファイル 80
P004 付箋(大) 350
P005 ホッチキス 480

「転記先」シート(実行後) — P001, P003, P005 を選択した場合:

A列(商品コード) B列(商品名) C列(単価)
P001 ボールペン(黒) 120
P003 クリアファイル 80
P005 ホッチキス 480

よくある落とし穴5選

1. RowSourceで別シートのデータが表示されない

自分もこれでハマった。RowSourceに "A2:A100" だけ指定して、別シートがアクティブのときにフォームを開いたら空リストが表示された。原因に気づくまで30分溶かした。

原因: RowSourceにシート名を含めないと、アクティブシートの範囲が参照される。

対策: 必ずシート名を含める。"データ!A2:C100" のように書く。AddItem方式ならこの問題は起きない。

2. AddItemとRowSourceを同時に使ってエラーになる

原因: RowSourceが設定されている状態で AddItem を呼ぶと「プロパティまたはメソッドが無効です」というエラーが出る。

対策: AddItem方式を使う場合は、先に ListBox1.RowSource = "" でRowSourceをクリアする。プロパティウィンドウでRowSourceが空になっていることも確認する。

3. MultiSelectなのにListIndexだけで取得して1件しか取れない

原因: MultiSelectモードでは ListIndex は最後にクリックした項目のインデックスしか返さない。複数選択した項目は Selected(i) でしか取得できない。

対策: For i = 0 To ListBox1.ListCount - 1 のループで Selected(i) をチェックする。実務版コードのパターンをそのまま使えばOK。

4. ListBoxのインデックスが0始まりでExcelの行番号とずれる

原因: ListBoxの List(i)Selected(i) は0始まり。Excelの行番号は1始まり。さらにヘッダー行があるとさらに1ずれる。

対策: シートの行番号に変換するには 行番号 = i + START_ROW のように開始行を足す。実務版コードではAddItemで値を直接格納しているので行番号の変換は不要。

5. Unload Meを忘れてフォームがメモリに残り続ける

原因: Me.Hide は非表示にするだけで、フォームオブジェクトはメモリに残る。連続で ShowListBoxForm を呼ぶと前回のデータが残る場合がある。

対策: 処理完了後は Unload Me でフォームを破棄する。次回表示時に UserForm_Initialize が再実行されるので、常に最新データが読み込まれる。

VBAのListBoxに項目が表示されないときの対処法

「ListBoxを配置したのに項目が空のまま」という場合、原因はRowSourceとAddItemの競合か、シート名の未指定だ。RowSourceを使うならシート名を含めて指定する。AddItemを使うならRowSourceを空にしてから追加する。

VBAのListBoxで複数選択した項目を取得できないときの対処法

「MultiSelectモードなのに選択が1件しか取れない」という場合、原因はListIndexだけで取得しようとしていること。MultiSelectモードでは Selected(i) をループで全件チェックする必要がある。

FAQ

Q1: AddItemとRowSourceはどちらを使うべき?

条件付きでデータを絞り込みたい場合はAddItem。シートのデータをそのまま表示するだけならRowSourceが手軽。

場面 推奨
シートの一覧をそのまま表示 RowSource(ListBox1.RowSource = "データ!A2:C100"
条件に合うデータだけ表示 AddItem(ループ内でIf文で判定してから追加)
フォーム上で動的に項目を追加/削除 AddItem + RemoveItem

AddItemで条件付き追加をするパターンは、InputBoxで条件を入力させて絞り込むのにも使える。詳しくは InputBoxで条件を入力させて処理を動的に変える方法 を参照。

Q2: ListBoxに複数列を表示するには?

ColumnCount で列数を指定し、ColumnWidths で各列の幅を設定する。


With Me.ListBox1
    .ColumnCount = 3                '← 3列表示
    .ColumnWidths = "80;120;60"     '← 各列の幅
End With

AddItemで複数列のデータを入れる場合は、1列目は AddItem で追加し、2列目以降は List(行, 列) で設定する。


Me.ListBox1.AddItem wsSource.Cells(r, 1).Value     '← 1列目
Me.ListBox1.List(Me.ListBox1.ListCount - 1, 1) = wsSource.Cells(r, 2).Value  '← 2列目
Me.ListBox1.List(Me.ListBox1.ListCount - 1, 2) = wsSource.Cells(r, 3).Value  '← 3列目

Q3: 選択をリセット(全解除)するボタンを付けるには?

フォームにもう1つCommandButtonを追加し、以下のコードを書く。


Private Sub CommandButton2_Click()
    Dim i As Long
    For i = 0 To Me.ListBox1.ListCount - 1
        Me.ListBox1.Selected(i) = False
    Next i
End Sub

Q4: 応用: リストの項目数が多いとき、検索で絞り込むには?

TextBoxを1つ追加し、Changeイベントで入力文字に合致する項目だけをAddItemし直す。


'※ 実務版コードの定数(SOURCE_SHEET, START_ROW, COL_COUNT)を使用
Private Sub TextBox1_Change()
    Dim keyword As String
    keyword = Me.TextBox1.Value

    Me.ListBox1.Clear

    Dim wsSource As Worksheet
    Set wsSource = ThisWorkbook.Worksheets(SOURCE_SHEET)
    Dim lastRow As Long
    lastRow = wsSource.Cells(wsSource.Rows.Count, 1).End(xlUp).Row

    Dim r As Long
    For r = START_ROW To lastRow
        '--- 商品コードまたは商品名にキーワードが含まれていれば追加
        If InStr(1, wsSource.Cells(r, 1).Value, keyword, vbTextCompare) > 0 Or _
           InStr(1, wsSource.Cells(r, 2).Value, keyword, vbTextCompare) > 0 Then
            Me.ListBox1.AddItem wsSource.Cells(r, 1).Value
            Me.ListBox1.List(Me.ListBox1.ListCount - 1, 1) = wsSource.Cells(r, 2).Value
            Me.ListBox1.List(Me.ListBox1.ListCount - 1, 2) = wsSource.Cells(r, 3).Value
        End If
    Next r
End Sub

リストの重複を除去してから表示したい場合は Dictionaryで重複チェック・集計を高速化する方法 が参考になる。

Q5: ListBoxとComboBoxの違いは?

比較 ListBox ComboBox
表示 常にリストが見える クリックで展開するドロップダウン
複数選択 MultiSelectで可能 不可(1つだけ)
フォーム上の面積 大きい 小さい
用途 複数選択が必要な場面 1つだけ選ばせる場面

1つだけ選ばせるなら場所を取らないComboBoxが向いている。複数選択が必要ならListBox一択。UserFormでのComboBox活用は ユーザーフォームで本格的な入力画面を作る方法 を参照。

まとめ

  • AddItem: 項目を1つずつ追加。条件付き絞り込みに向いている
  • RowSource: セル範囲を一括バインド。シートデータをそのまま表示するなら手軽
  • MultiSelect: fmMultiSelectMulti でクリック選択/解除、fmMultiSelectExtended でShift/Ctrl選択
  • Selected(i): ループで全項目をチェックして、選択された項目だけ処理する
  • 未選択チェック: ListIndex = -1(単一選択時)または Selected(i) のループ(複数選択時)で確認
  • Option Explicit: UserFormモジュールの先頭に追加しておくと、変数名のタイプミスを防げる

関連記事

次にやりたくなること

コメント

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