🚀The world's best VBA AI has evolved. ExcelMaster is now an autonomous Agent.Read more →
Back to Blog

Excel VBA の ListBox — リストの作り方、選択の読み取り、MultiSelect の落とし穴

|

Excel VBA の ListBox — リストの作り方、選択の読み取り、MultiSelect の落とし穴

TL;DR — ListBox は一覧を表示し、ユーザーが選んだものを覚えています。 ListBox1.List = Range("A2:A50").Value で一気に埋めましょう。単一の選択は .ListIndex(0 始まりの行番号、 何も選ばれていなければ -1)と .Value(選ばれたテキスト)で読みます。落とし穴はこうです。.MultiSelect をオンにした瞬間に .Value は Null になり、何も教えてくれなくなります — すべての選択を集めるには .Selected(i) をループしなければなりません。

' 単一選択の ListBox:唯一の選択を読み取る
Private Sub cmdOK_Click()
    If ListBox1.ListIndex = -1 Then
        MsgBox "Please pick a name first."
        Exit Sub
    End If
    MsgBox "You picked " & ListBox1.Value      ' .Value = 選択されたテキスト
End Sub

ListBox は UserForm 上のコントロールで、まず腹に落としておきたいのは すべての コントロールに共通するひとつの考え方です。コントロールとは、値そのものではなく、プロパティを通して読み取る 生きたオブジェクトだ ということです。項目は .List に、ユーザーの選択は 別の プロパティ — .ListIndex や .Selected — にあり、ボックス全体の中にあるのではありません。「ListBox が何も返さない」という質問のほとん どは、間違ったプロパティを読んでいるだけなのです。

この記事で学べること

  • ListBox・ComboBox・CheckBox を結びつける考え方
  • ListBox を埋める二つの方法 — .List = Range.Value で一気に、あるいはループの中で .AddItem
  • .ListIndex と .Value で単一の選択を読む方法、そして -1 が何を意味するか
  • 最大の落とし穴:MultiSelect のもとで .Value は Null になるので .Selected(i) をループする必要がある
  • 一覧を空にする方法、複数の列を表示する方法、.RowSource でシートにバインドする方法
  • ListBox が正しいコントロールになるのはいつで、ComboBox のほうが合うのはいつか

考え方の軸:ボックスは一覧を持ち、プロパティが選択を持つ

ListBox は、項目の一覧をのぞく窓だと考えてください。二つの異なるものが、二つの異なる場所にあります。

ListBox1.List           ' 項目(ITEMS)- ボックスに表示される中身
ListBox1.ListIndex      ' 選択(PICK)- どの行が選ばれているか(0 始まり、-1 = なし)
ListBox1.Value          ' 選択のテキスト(TEXT)- 単一選択のときだけ意味がある
ListBox1.Selected(i)    ' 行 i の True/False - MultiSelect では唯一信頼できる読み取り

この分離こそがコントロールのすべてです。.List を設定するとき、あなたは項目を読み込んでいます。ユーザーが行 をクリックしても、VBA は .List を変えません — 変えるのは .ListIndex で、.Selected を切り替えます。です から「ユーザーは何を選んだのか?」は、一覧を見ても決してわかりません。答えるのは 選択 のプロパティを読むこと です。ここをはっきりさせれば、あとは細部です。

ListBox を埋める:ループより一発の代入

一覧を埋める方法は二つ見かけます。まず手を伸ばすべきなのは、列全体を一度に代入するやり方です。

Private Sub UserForm_Initialize()
    ' ワークシートの一列を一度の代入で埋める - 速くて単純
    ListBox1.List = Sheet1.Range("A2:A50").Value
End Sub

.List は 2 次元配列を受け取り、Range.Value は まさに 2 次元配列です(VBA Range を 参照)。だから一行で 50 行を読み込めます。.AddItem を使うのは、項目を一つずつ計算していて、手元に配列がない ときだけにします。

Dim i As Long
For i = 1 To 5
    ListBox1.AddItem "Quarter " & i    ' その場で組み立てた項目を追加する
Next i

大きなループの中の .AddItem は遅い道です — 呼び出しのたびに、コントロールへの往復が別々に発生します。データ がすでに範囲や配列に収まっているなら、.List を一度だけ代入しましょう。埋め直す前にボックスを空にするには、 ListBox1.Clear を呼びます。

単一の選択を読む:ListIndex と Value

ふつうの単一選択の ListBox では、二つのプロパティがすべてに答えます。.ListIndex は選択の 0 始まりの行番号 で、何も選ばれていないときは -1 です。これが、いつも最初に確かめるべきガードです。.Value は選ばれた行 のテキストです。

If ListBox1.ListIndex = -1 Then
    MsgBox "Nothing selected."          ' -1 は「まだ何も選ばれていない」という合図
Else
    MsgBox "Row " & ListBox1.ListIndex & ": " & ListBox1.Value
End If

.Value を読む前に .ListIndex = -1 を確かめるかどうかが、ユーザーを導くフォームと、空の選択に対して動いて しまうフォームとの分かれ目です。Find の結果を使う前にガードするのと同じ心得です。

最大の落とし穴:MultiSelect が .Value を役立たずにする

人を堂々巡りさせる間違いがこれです。ListBox には MultiSelect プロパティがあります。既定 (fmMultiSelectSingle)のままなら .Value は動きます。ユーザーが複数の行にチェックを入れられるよう fmMultiSelectMulti や fmMultiSelectExtended に設定すると — .Value は Null になります。もう選択を まったく反映しません。三つの行が選ばれているとき「その値」は意味をなさないからです。

MultiSelect のもとで唯一信頼できる読み取りは、すべての行をたどって .Selected(i) を調べることです。

Dim i As Long, chosen As String
For i = 0 To ListBox1.ListCount - 1     ' 行は 0 .. ListCount - 1
    If ListBox1.Selected(i) Then
        chosen = chosen & ListBox1.List(i) & vbCrLf
    End If
Next i
MsgBox "You chose:" & vbCrLf & chosen

このループの成否を分ける細部が二つあります。ループは 0 から .ListCount - 1 まで回ります。行が 配列の範囲 と同じく 0 始まりだからで — ここで 1 ずれると、最後の行が黙って抜け落ちま す。そして項目のテキストは .Value ではなく .List(i) で読みます。このモードでは .Value が Null だから です。フォームを作ってから後で複数選択をオンにしたら選択が見えなくなった、というなら理由はこれです。

複数の列と、シートへのバインド

「ただの一覧では足りない」というよくある場面を、二つのプロパティがカバーします。.ColumnCount と .List (2 次元配列)を使えば ListBox は複数の列を表示でき — ID と名前を並べて — .BoundColumn は .Value がどの 列を返すかを決めます。そして .RowSource はボックスをワークシートの範囲に直接バインドし、埋めるコードなしに セルを追従させます。

ListBox1.ColumnCount = 2
ListBox1.RowSource = "Sheet1!A2:B50"    ' 2 列、シートにライブでバインド
ListBox1.BoundColumn = 1                ' .Value は 1 列目(ID)を返す

.RowSource は便利ですが、フォームを特定のシートのレイアウトに縛りつけます。配列から .List を代入すれば、 データがどこにあるかからフォームを切り離せます。一つのブックより長く生きるデータ入力フォームなら、配列の代入 を選びましょう。

ListBox か ComboBox か:選択がどう感じられるべきかで選ぶ

ListBox と ComboBox はほとんど同じように読むので、構文ではなく振る舞いで選びます。 選択肢が 一目で見えている べきとき、あるいはユーザーが 複数 を選ぶかもしれないときは ListBox に手を 伸ばします — 複数選択は ListBox だけの芸当です。スペースが限られていて、答えが単一の値で、一行に畳まれるドロ ップダウンのほうがすっきりするなら ComboBox にします。行ごとにチェックボックスが欲しくなってきたら、たぶん 欲しいのは複数選択の ListBox のほうです。

ExcelMaster の活用

このページの二つの間違いは、本当に時間を奪います。複数選択の ListBox で .Value を読んで(Null を受け取 る)こと、そして .ListIndex = -1 を先に確かめずに選択に対して動くこと。どちらも書きやすく、静かに失敗します。

ExcelMaster には、フォームを普通の言葉で 伝えられます — 「ユーザーが複数選択できる顧客の一覧を作って、選ばれたものをシートに書き出して」 — すると UserForm を組み立て、あなたのデータから ListBox を埋め、選択を正しいやり方で読みます(複数選択がオンなら .Selected(i) をループし、そうでなければ .ListIndex をガードする)。ブックとコードはあなたの手元に残ります。

よくある質問

VBA で範囲から ListBox を埋めるには?

範囲の値を一文で .List に代入します。ListBox1.List = Sheet1.Range("A2:A50").Value です。Range.Value は 2 次元配列で .List はそれを受け取るので、これで列全体を一度に読み込めます。.AddItem はループの中で一つずつ 組み立てる項目のときだけ使い、すでに行のあるボックスを埋め直すなら先に .Clear を呼びましょう。

VBA の ListBox から選択された項目を取得するには?

単一選択の ListBox なら、テキストは .Value、0 始まりの行番号は .ListIndex で読みます。何も選ばれていない と .ListIndex は -1 なので、まずそれを確かめます。複数選択の ListBox では .Value は Null です — For i = 0 To ListBox1.ListCount - 1 をループして、.Selected(i) が True のすべての行を集めます。

ListBox の .Value が空、または Null になるのはなぜですか?

MultiSelect がオンになっているからです。.MultiSelect が fmMultiSelectMulti や fmMultiSelectExtended のとき、.Value プロパティは常に Null です — 複数の選択された行を表せません。代わりに 0 から .ListCount - 1 まで .Selected(i) をループして選択を読み、各項目は .List(i) で読みます。

ListBox からすべての項目を消すには?

ListBox1.Clear を呼びます。これですべての行が消えます。ボックスが .RowSource でバインドされているなら、先 に .RowSource = "" を設定します。バインドされた一覧は範囲から自分を作り直すからです。項目を消さずに現在の選 択だけを解除するには、.ListIndex = -1(単一選択)にするか、.Selected(i) = False をループ(複数選択)します。

ListBox と ComboBox の違いは何ですか?

ListBox は複数の行を一度に表示し、複数選択を許せます。ComboBox は一行に畳まれ、ド ロップダウンを表示し、選択は一つだけ(必要なら自由入力も)許します。選択肢が見えているべき、あるいは複数選び たいときは ListBox、スペースが限られていて答えが一つで足りるときは ComboBox を使いましょう。

検証環境

検証環境: Excel 365 (Windows 11)、VBA 7.1 — 最終確認 2026-09-25。

関連ガイド: VBA ComboBox · VBA CheckBox · VBA UserForm · VBA Range · VBA For Loop