次の方法で共有


[Excel 2016 VBA] Excel 2013 の VBA で複数選択可能リストボックスの Change イベント

質問

2019年4月25日木曜日 5:41

Excel 2016 VBA のユーザーフォーム上に複数選択可能リストボックス (ListBox.MultiSelect = fmMultiSelectMulti) を
設置してその Change イベント内で ListIndex の値を -1 に設定すると実行時に

実行時エラー '-2147417848 (80010108)':

オートメーション エラーです。
起動されたオブジェクトはクライアントから切断されました。

というエラーが出たり、Excel 自体が強制終了してしまったりします。
(以下は再現性のための最小限の単純なサンプルで実際には特定の条件下でのみ動くようにする予定です)

Private Sub ListBox1_Change()
    If ListBox1.ListIndex > -1 Then
        i = ListBox1.ListIndex
        ListBox1.ListIndex = -1
    End If
End Sub

Private Sub UserForm_Initialize()
    ListBox1.MultiSelect = fmMultiSelectMulti
    ListBox1.RowSource = "a1:d30"
End Sub

なお、ブレークポイントを設定し一旦停止させてから再実行したりすると再現しません。

ボタンなどで操作したときは問題ないし、これに関する情報と回避策はないでしょうか。

Private Sub CommandButton1_Click()
    ListBox1.ListIndex = -1
    ListBox1.SetFocus
End Sub

すべての返信 (7)

2019年4月25日木曜日 14:19

複数選択を許可している場合は、ListIndexではなく、Selectedプロパティで判定・制御しないとだめなようですよ。

参考↓https://docs.microsoft.com/ja-jp/office/vba/language/reference/user-interface-help/listindex-property?f1url=https%3A%2F%2Fmsdn.microsoft.com%2Fquery%2Fdev11.query%3FappId%3DDev11IDEF1%26l%3Dja-JP%26k%3Dk(fm20.chm2001430)%3Bk(TargetFrameworkMoniker-Office.Version%3Dv15)%26rd%3Dtrue


2019年4月26日金曜日 2:14

返信ありがとうございます。

ただ、それは知っています。

やりたいのは選択されている項目を調べたいのではなく特定の項目を選んだ時にその項目を選択している
フォーカス (?) を消したいのです。
複数選択リストボックスでは選択する対象になっているリスト項目が ListIndex の値になります。

ListIndex の値を -1 に設定すると本来ならこのフォーカスが消えます。

(リストボックス自体のフォーカスは消えない)


2019年4月28日日曜日 2:42

試行錯誤しましたが、やはり「Changeイベント」内で「ListBox1.ListIndex = -1」とすると、オートメーションエラーが発生しますね。

代替案として、コントロール自体にフォーカスを持たせないというのはどうでしょうか?

(ListBoxコントロールにフォーカスがない限り、点線は見えないはず)

Private Sub ListBox1_Enter()
    CommandButton1.SetFocus
End Sub

Private Sub UserForm_Initialize()
    ListBox1.MultiSelect = fmMultiSelectMulti
    ListBox1.RowSource = "a1:d30"
End Sub


2019年5月3日金曜日 5:14

返信ありがとうございます。
また、返事が遅くなってすみません。

対策などいろいろと考えて頂いて申し訳ありませんがこの件は取り敢えず保留とします。

ListBox.ListIndex = -1 がもし仕様上、使えないのであったとしてもオートメーションエラーになったり
強制終了するのはおかしい動作だと思います。
というより ListBox.Change イベント自体がいろいろとおかしいみたいで同様に Change イベント内で
そのリストボックスコントロールの RowSource を変更した場合に RowSource や ListBox.ListCount の
値は正常に変更されるのにリストボックスの内容が更新されない問題もあります。
ボタンなどから更新したときは正常に動作するのですが。

Private Sub CommandButton1_Click()
    ListBox1_Change
    ListBox1.SetFocus
    ListBox1.ListIndex = ListBox1.ListCount - 1
End Sub

Private Sub ListBox1_Change()
    Dim rng As Range
    If ListBox1.RowSource <> "" Then
        Set rng = Range(ListBox1.RowSource)
        If rng.Rows.Count < Rows.Count Then
            ListBox1.RowSource = Replace(rng.Resize(rng.Rows.Count ; 1).Address, "$", "")
            Debug.Print ListBox1.RowSource; " / ListBox1.ListCount: " & ListBox1.ListCount
        End If
    End If
End Sub

Private Sub UserForm_Initialize()
    ListBox1.MultiSelect = fmMultiSelectMulti
    ListBox1.RowSource = "a1:a1"
End Sub

2019年5月3日金曜日 8:28

Change イベントではエラーになり
ボタンなどから更新したときは正常に動作するのは
ListBox1.ListIndex = -1
は、
自身(ListBox1)にフォーカスがある状態では
許してくれないからだろうと思います。

つまり、
ボタンを押したときに
自身(ListBox1)からボタンにフォーカスが移り
その後
ListBox1.ListIndex = -1
が実行されるからエラーにならないという動作と思います。

ListBox1から他のボタンなどにフォーカスが移り
タブキーを数回押すことで再び
ListBox1がフォーカスを得たときに
指摘の破線が非常になればいいのであれば
ListBox1_Exit イベントで実行する対応があると思います。

いやそうではなく、
リストの何れかを選択、あるいは非選択した直後も、
指摘の破線が目障りとのことであれば
私には手に負えません。m(__)m


2019年5月4日土曜日 9:31

Change イベントで、自身の設定を変更するのは危険な気がします。実際、問題が出ていますし。

AfterUpdate イベントの方が安全だと思います。

テキストボックスでもChangeイベントで自身の値を変更したりすると、無限連鎖イベント状態になって落ちることがあるので。

あと、そもそも破線を消したいのはどのような理由からでしょうか。目障りだということですかね。

あの破線にもちゃんと役割があって、キーボードから選択、非選択をする場合、上下キーで破線を移動させて、スペースキーで選択/非選択の切り替えという役割があります。その破線を消しちゃうとキーボードでの操作ができなくなります。

マウスだけで操作できればいいというならいいですが。その場合は、上のminmin312さんの選択したらフォーカスを移動する方法で破線は表示されないようにできます。


2019年5月13日月曜日 13:49

こんにちは

ご指摘の通り、強制終了等の状況を確認しました。

問題点と思われることを以下に記します。

1.Listbox1.ListIndex は、LisBoxの選択された項目の情報を得ることはできます(MultiSelectの場合は別のやり方で取得すべきことは御承知のようなので、置いておきます。)が、MultiSelectの場合には、ListBoxの選択状態を変える(選択項目を設定する)ことはできないのではないでしょうか。(ちなみにCommandButton1_Click()で強制終了はしませんが、選択状態は変えられませんでした。また、CommandButton1_Click()のListbox1.ListIndex =-1を2としても、2に対応する項目が選択されることはありませんでした。リストボックスの3番目の項目がと一瞬ちらちらとするので何か反応が起きているのでしょうけれど反転表示されて選択されたされたことが分かる、という様にはなりませんでした。MultiSelectでない場合は、Listbox1.ListIndex =xで選択状態が変わることを確認しました。すなわち、=2で2に対応する3番目の項目が選択され、=-1で選択が解除されました。)

2.選択状態を解除する趣旨で ListBox1.ListIndex =-1 と記しているのであれば、少なくともMultiSelectの場合は、次のように変更すべきです。

  For i =0 to Me.ListBox1.ListCount -1

      Me.ListBox1.Selected(i)=false

  Next i

3.強制終了される点については、良く分かりませんが、1.でListbox1.ListIndex はListBoxの選択状態を変える(選択項目を設定する)ことはできないのではないか、と記しましたが、それでも何らかの評価が行われて、強制終了という流れになっているのではないか、と言う気がします。単に小生の素人の感覚でしかなく、申し訳ありません。

4.ちなみに、ListBox1_Change() の中の ListBox1.ListIndex = -1 を

        For j = 0 To Me.ListBox1.ListCount - 1
          Me.ListBox1.Selected(j) = False
        Next j

  と書き換えると、強制終了はしなくなります。また、リストを選択したとたんに選択が解除されてしまうので、青く背景が変色することもなく、どれが選択されて解除されたのかが良く分かりません。

5.リストの1つに点線の囲みが残りますが、これはそういうものなのだと思うしかないような…。ちなみにMultiSelectでない場合にListbox1.ListIndex =-1で選択を解除したときも、点線の囲みは残りました。小生の環境は、Windows10 64bit ; Excel2016です。

以上、特に根拠を示すことはできませんが、試行錯誤のVBAの動作の反応を見ての小生の理解を記してみました。参考になれば幸いです。