Excel (VBA) |
|
(Windows 11 Pro : Excel 2016)
選択肢の範囲を変数にした複数選択が可能なプルダウンリストを作りたい
投稿日時: 24/04/19 10:08:48
投稿者: nari99
|
---|---|
ActiveXコントロールのリストボックスを使って"選択肢を複数選択可能で、選択されたデータをセルに書き込むプルダウンリスト"をExcelに実装しようとしています。選択肢となる範囲は、Excelの機能である「名前の定義」でOFFSET関数で設定した可変の範囲を、リストボックスのプロパティの"Listfillrange"に設定しています。リストボックスには"ショップ"と名前を付けています。
Option Explicit Sub リスト表示非表示() With ActiveSheet.Shapes("ショップ") If .Visible = True Then Call リスト非表示 Else Call リスト表示 End If End With End Sub Sub リスト表示() With ActiveSheet.ショップ .Visible = True .Activate End With End Sub Sub リスト非表示() With ActiveSheet.ショップ .Visible = False End With End Sub Sub セルの値表示() Dim セルの値 As String Dim I As Long セルの値 = "" With ActiveSheet.ショップ For I = 0 To .ListCount - 1 ' リストの先頭から最後までループ If .Selected(I) = True Then ' リストが選択されていたら セルの値 = セルの値 & "," & .List(I) ' カンマ区切りでリストの値をつなげる End If Next I End With セルの値 = Mid(セルの値, 2) Range("B2").Value = セルの値 End Sub 上記のコードで ・コード実行時の選択肢範囲を正しく取得し、プルダウンリストに反映すること ・選択肢から選択されたデータをセルに書き込むこと これはできたのですが ・選択肢の範囲が増えたときに増えた分の選択肢をプルダウンに反映できない ・選択肢範囲が減った時にプルダウンに減った分の空白の選択肢が残ってしまう という問題がありました。 原因はListfillrangeに設定された範囲の広さが変わった時(選択肢の数が変わった時)にリストボックスが更新されないことかと想定し、いろいろやって現在以下のようになっています。 Option Explicit Dim 初期化済 As Boolean Sub リスト表示非表示() With ActiveSheet.Shapes("ショップ") If .Visible = True Then Call リスト非表示 Else Call リスト表示 End If End With End Sub Sub リスト表示() With ActiveSheet.Shapes("ショップ") .Visible = True ' 初めてリストを表示する場合にのみ初期化を行う If Not 初期化済 Then 初期化リストボックス 初期化済 = True End If End With End Sub Sub リスト非表示() With ActiveSheet.Shapes("ショップ") .Visible = False ' リスト非表示時にリストボックスの選択をクリアする With .OLEFormat.Object.Object .MultiSelect = fmMultiSelectMulti .Selected(-1) = False End With End With End Sub Sub セルの値表示() Dim セルの値 As String Dim i As Long セルの値 = "" With ActiveSheet.Shapes("ショップ").OLEFormat.Object.Object For i = 0 To .ListCount - 1 If .Selected(i) = True Then セルの値 = セルの値 & "," & .List(i) End If Next i End With セルの値 = Mid(セルの値, 2) Range("B2").Value = セルの値 End Sub Sub 初期化リストボックス() Dim ws As Worksheet Dim rng As Range Dim lb As MSForms.listBox Dim i As Long Set ws = ThisWorkbook.Sheets("Sheet2") ' Set rng = ws.Range("SHOP") ' Set lb = ActiveSheet.Shapes("ショップ").OLEFormat.Object.Object ' リストボックスの内容をクリア lb.Clear ' リストボックスに新しい範囲の値をセット For i = 1 To rng.Rows.Count lb.AddItem rng.Cells(i, 1).Value Next i End Sub 現在発生しているエラーは lb.Clear ここで「予期せぬエラー」が発生しています。 何かヒントをご教示いただければと思います。 よろしくお願い致します。 |
投稿日時: 24/04/19 10:28:02
投稿者: nari99
|
|
---|---|
プルダウンが表示されていないときにマクロ登録で"リスト表示非表示"を登録したボタンを押すと
|
投稿日時: 24/04/19 11:24:29
投稿者: sk
|
|
---|---|
引用: 引用: ListFillRange プロパティの設定を活かしたまま そのコードを実行しているからではないかと。 |
投稿日時: 24/04/19 12:16:44
投稿者: nari99
|
|
---|---|
nari99 さんの引用: コメントの種類を間違えていたので再度コメントします |
投稿日時: 24/04/19 12:21:30
投稿者: nari99
|
|
---|---|
skさん
|
投稿日時: 24/04/19 14:16:34
投稿者: sk
|
|
---|---|
引用: ListFillRange プロパティに任意のセル範囲のアドレス/名前が 設定されているリストボックスについては、参照された範囲から 取得したリストを直接編集できないため、Clear, AddItem, RemoveItem など リストのアイテムの追加/削除に関わるメソッドを呼び出すことは出来ません。 それらのメソッドを使用したければ、そのリストボックスの ListFillRange プロパティの値は空白にしておけばよい、 と申し上げただけです。 引用: 引用: 引用: いずれにせよ、名前定義[SHOP]の参照範囲として設定した数式(のみ)によって そのリストボックスに表示させるリストの増減を適切に制御することが 出来ているのか否かはまた別の問題です。 例えば、あるセルに何らかの値を入力すると OFFSET 関数によって 返される範囲の行数が増減するような数式が名前定義[SHOP]の 参照範囲として設定されているのであれば、そのセルの値が 変更されたタイミングでリストボックスの ListFillRange プロパティを 再設定するようになさればよいでしょう。 実際にどのような手法を採るべきか、どのモジュールに どのようなコードを記述すべきかについては、 [SHOP]に設定されている具体的な数式が判らない限り 今のところ回答のしようがありません。 |
投稿日時: 24/04/19 14:41:24
投稿者: nari99
|
|
---|---|
引用: これを理解できておりませんでした。ご教示頂きありがとうございます。 引用: おっしゃる通りで、名前定義[SHOP]に設定している数式は以下です。 =OFFSET(Sheet2!$A$4,0,0,COUNTA(Sheet2!$A$4:$A)) 引用: 大変恐縮ですが、もしよろしければご教示頂きたいです。 よろしくお願い致します。[/quote] |
投稿日時: 24/04/19 16:43:51
投稿者: sk
|
|
---|---|
引用: こういう参照の仕方は Excel では出来ないでしょう、 という点は置いておくとして。 引用: COUNTA 関数によって得られるのは、あくまで 「参照範囲の中に含まれる空白セル以外のセルの個数」です。 例えば、その時点における Sheet2 の最後のデータ行が 13 行目であるとして、 A4 セルから A13 セルまでの範囲に空白セルが含まれていなければ A4 セル以降の行を参照範囲とする COUNTA 関数の戻り値は 10 となります。 その値が更に OFFSET 関数の第 4 引数(高さ)に渡された結果、 OFFSET 関数が返す範囲は A4:A13 となります。 しかし、もし A5 セルのみが空白セルだった場合、A4 セル以降の 行を参照範囲とする COUNTA 関数の戻り値は 9 となります。 その結果、OFFSET 関数が返す範囲は A4:A12 となり、 空白である A5 セルを含み、かつ空白セルではない A13 セルが 含まれていない状態となります。 前者のように「先頭行から最終行までの間に空白セルが含まれていない」ことが 確実に保証されているのであればそのような数式でもよいでしょうが、 後者のような状態になり得るのであれば、上記のような数式を使用した 名前定義を用いるのはなるべく止めた方がよいでしょう。 他にも懸念すべき問題はありますが、ひとまず前提条件を 次のように仮定します。 ・Sheet2 の A 列の最初のデータ行は 4 行目からとする。 ・Sheet2 の A 列の最初のデータ行から最後のデータ行までの間に 空白セルが含まれることはない。 引用: ここでの「あるセル」とは Sheet2 の A 列のいずれかのセルですので、 Sheet2 の Change イベントの発生時に次のようなイベントプロシージャを 実行するようにします。 ( Sheet2 のシートモジュール) --------------------------------------------------------------- Private Sub Worksheet_Change(ByVal Target As Range) If Intersect(Target, Me.Range("A:A")) Is Nothing Then Exit Sub End If Dim rngFirst As Range Dim rngLast As Range Dim rngListSource As Range Dim oleListBox As Excel.OLEObject With Me Set rngFirst = .Cells(4, 1) Set rngLast = .Cells(.Rows.Count, 1).End(xlUp) Set rngListSource = .Range(rngFirst, rngLast) Debug.Print "参照範囲のアドレス:" & rngListSource.Address Set oleListBox = .OLEObjects("ショップ") oleListBox.ListFillRange = rngListSource.Address End With Set oleListBox = Nothing Set rngFirst = Nothing Set rngLast = Nothing Set rngListSource = Nothing End Sub --------------------------------------------------------------- |
投稿日時: 24/04/19 17:58:48
投稿者: nari99
|
|
---|---|
質問させて頂いていることに加え、別の懸念点までご指摘いただきありがとうございます。
引用: そうでした。スプレッドシートをよく使う癖で記述してしまいました。失礼いたしました。 引用: 確かにおっしゃる通りですね、、まったく考慮できていませんでした。 であれば、例えばSheet2のB4セルに =FILTER(IF(A4:A2000="","",A4:A2000),A4:A2000<>"") のような関数を入れて形で間に空白行が入らないようにした範囲を作り、名前の定義設定する式を =OFFSET(Sheet2!$B4,0,0,COUNTA(Sheet2!$B4:$B2000)) とすれば、OFFSET関数の4 引数=範囲の高さになるようにできるかと思いました。 引用: こちら教えて頂いてありがとうございます。 範囲指定の仕方さえ正しくできていれば、おそらく正しく動かせていると思います。 |
投稿日時: 24/04/19 18:26:40
投稿者: sk
|
|
---|---|
引用: FILTER 関数を使用できる環境( Microsoft 365 など)ならば そういう方法も可能ですが、Excel 2016 では FILTER 関数を 使用することはできません。 引用: また懸念すべき問題の 1 つとして、OFFSET 関数も COUNTA 関数も 揮発性関数である、ということが挙げられます。 これらの関数は、シートの再計算が発生する都度に呼び出されます。 今回の場合、リストボックスの ListFillRange プロパティに 名前定義[SHOP]を設定しているわけですが、[SHOP]の参照範囲として 上記の数式が設定されているならば、シートの再計算が発生するたびに OFFSET 関数と COUNTA 関数が呼び出され、それに伴ってリストボックスの リストの再読み込み処理が実行され、その時点で複数選択されていたアイテムの 選択状態も全てリセットされることになります。 シートの再計算が発生してもリストのアイテムの選択状態を維持して おきたいのであれば、動的範囲を返す名前を ListFillRange プロパティを 設定しないことをお奨めします。 |
投稿日時: 24/04/22 10:49:04
投稿者: nari99
|
|
---|---|
引用: そうなんですね、、、何か別の方法を考えます。 引用: この点は実際に運用するときに不都合になることがありそうなので、範囲設定の仕方も考え直す必要がありそうです。 ご指摘いただきありがとうございます。 当初お伺いした質問の内容は解決して頂いたので解決済みにさせて頂きます。 |