はもちくわ

コードについて自分なりの解釈を書いてます。

【Excel】結局さ、フォームコントロールのリストボックスの複数選択は飾りってことなの?!(ユーザーフォームを使わないリストボックスウインドウ)

ちょっと私のスキルではどうにもならんかったので、記事に書いてます(笑)
私、今はWinのExcel使っていますが、もともとMacがメインだったので、業務効率化するためにVBA使う場合、「ActiveXコントロール」とか「ユーザーフォーム」とか「ファイルシステムオブジェクト」とか、なるべく使わない処理を心がけているんです。だってMacじゃ使えないですから(笑)。

で、いま作ってる業務効率化ツールで、視覚的にわかりやすいものにしたい箇所がありまして、フォームコントロールのリストボックスを使うことにしました。
でも、、、、これ、、、、単一選択なら問題なく動きますが、複数選択は使えないことないですか??選択方法が複数と拡張の2種類あるのに(笑)
で、いろいろ他のサイトでも調べてみました。
「ユーザーフォーム」や「ActiveXコントロール」についてはたくさんでてきますが、「フォームコントロール」は「使わないほうがいい」の一点張り(笑)すくないながら、紹介があっても、「マクロで扱うやつだから複数は省略」っておいおい(笑)。Microsoftサポートに至っては「ActiveXを使うことをお勧めします」って(笑)

いやー、たしかにMacユーザー少ないだろうけど無視はいかん(笑)。
それに、リストボックスの設定ウインドウでは、複数選択を選択できますし、リストを複数選択すればちゃんと選択されています。
ということは、選択されたことをリストボックスは認識できているわけなので、おそらく、値を抽出する方法はあるだろうって思ったわけです。

よし!それじゃ、なんとかしてやろうじゃないかっ!て、ひねくれ者の私は頑張ってみたのです。。。。これが1日前のワタシ(笑)



で、今のワタシは、、、、
いやー、だめでした〜〜〜〜!(笑)
で、やったことなんですが、結局どこのサイトにも説明がないので、リストボックスをオブジェクト型の変数にぶち込んで、ローカルウインドウで見てみることにしました。

Dim li_ob As Object
Set li_ob = ActiveSheet.Shapes("list_test").ControlFormat
このあとにブレークポイント入れてあとでローカル確認

こんなかんじ
すると、、、
あれ〜、単一でも複数でもListCount、ListIndexは「0」
ListIndexは配列にでもなってのかなって思ったんですが、、、、、
でも、どちらも「0」っておかしくないですか??

よしっ!それじゃあ、それぞれをMsgBoxで表示させてみますか、、、

上のコードにつづけて、
MsgBox "ListIndex =" & li_ob.ListIndex
MsgBox "ListCount =" & li_ob.ListCount
ここはブレークポイント取っておいてもいいかな。。

これで、単一と複数で動かすと、、、、
当然、単一は問題ないっす。だって正常に動いてるからあたりまえっす。
で、、、、問題の複数さんはどうなったかといいますと、、、、、
ListIndexは、「ListBoxクラスのListIndexプロパティを取得できません。」とエラー。
ListCountは正常。。。。まあ、リストの数は複数だろうと単一だろうとかわらないから当然か。。。
クラスに実装してないのかな?そういう使い方は想定してないってことか?配列でだしてくれりゃいいのに(笑)
さて、クラスの中身は私にはわからんのでMicrosoft Learnの日本語訳を見ながら確認していくことにしてみました。
ExcelVBAのオブジェクトのリファレンス見ながらやったんですが、、、、とてもわかりやすい!!ちょっと日本語おかしな点がありますが、十分理解できる。
で、、、、結果なんですが、 多分、私では無理(笑)
わかったことは、

  1. LinkedCellは複数では使えない
  2. Listindexは複数では使えない
  3. Multiselectで単一と複数を選べるけど、単一以外の使い方は「使えない」ことしか書いてない
  4. なぜか、Multiselectの例文が複数になってる(笑)使えんのに(笑)
  5. Outlookのほうだけど、クラスをNewしてコンストラクタしないでねって変な日本語でかいてある

以上っす。
また、気が向いてなんとかしてやろうと思ったら挑戦します。
こんなことに無駄な時間を使ってしまった(笑)

とりあえず、今作っているものは、よくシステムにあるような、リストボックスを左右にならべて、左が選択前の一覧、右を選択したもの一覧ってことにして、選択してボタンを押すと左から右へ持っていくやつにしますわ。それならまあ、単一しか使えない状況でも複数選ぶような感じになるかなぁ。。。。いちいち手操作するよりゃ少しはマシでしょう!!あと、全選択とか削除ボタンもオマケでつけておきますかっ!!

さて、、、、やるか(笑)



追記:複数選択できなかったのでこうしました例です(笑)

このように、ユーザーフォームでなくて、オートシェイプで先にウインドウっぽく作ります。ボタンもリストボックスもフォームコントロールを使ってます。フォームのボタンはActiveXに比べてデザインがあまり変えれないので、いっそボタンの自作っていう手もあります(笑)

動作を考えながらウインドウを先にきれいに作ったら、グループ化して名前つけて、「検索と選択」でグループごと見えなくしちゃいます。このウインドウはあえてシート側の操作を邪魔する位置におくのが都合がいいですよ〜。シートの処理されてもこまりますからね(笑)本当にシートをガードしたいときは、ウインドウのすぐ下に、画面いっぱいのオートシェイプ作っておいて、ウインドウと一緒に表示させればいいですよ。で、Protect DrawingObjectsでガードしちゃいえばOK!!あ〜、でもこの方法取るときは、リストボックスはロック外しておかないと、選択できなくなっちゃいますのでご注意を。。。。。

で、シート側にウインドウを開くキッカケ(ボタンでもDoubleClickでも)を作っておいて、
Shapes("名前").Visble=Trueで表示させるわけです。
そして、この表示のキッカケとともに、リスト化するデータを次のように配列で作成します。このときの配列はモジュール内で使い回せるように、プロシージャ内で配列を宣言せずに、モジュールの方に宣言しておきます。Publicでもいいっす。別に配列じゃなくてシートで管理でもいいです。シートの方がうっかり消えちゃったってことがないからいいかもしれませんが、、、、遅いっす(笑)

ここでは、説明しやすいように項目行を設けてますが、データ扱うときには邪魔になるのでなくていいです(笑)わたしは入れてません。
はい、、、ここのポイントですが、リストボックスはあくまで表示させるツールであって、データ自体の管理は、配列でするということです。なので、この2カラム目のファイル名のように、実際に使いたいデータが、リスト表示させたものとは違うとき便利になります。
リスト表示の状況判断は、3カラム目のリスト位置でやります。0が選択前、1が選択後ということです。

このように、ウインドウを表示させる時点ではすべて選択前なので配列の3カラム目は全部「0」でよいわけです。
そして、「0」のリストボックスから「1」へリストを移動させるときは、、、

このようにします。
ここでは、移動させるキッカケは矢印にマクロを登録してクリックしたら動くようにしました。
選択されてなかったらっていうガード忘れずに(笑)
こうみますと、現在のリストボックスの表示、順番はあまり関係ないのがわかりますか?
要は、いまの配列がどうなっているか分けて表示しているだけです。リストのデータを覚えておいて動かしているわけじゃないんですね〜。
なので、「1」から「0」に移動させるときは、この逆をすればいいだけです。

ほら簡単。
スクショでは、「全選択」、「全解除」というボタンを作ってあるのがわかると思いますが、これも、リストの表示は関係なくて、配列のリスト位置を全部「0」か「1」にして、そのとおりに一方は全リストを作り、もう一方は、全リストを消せば解決です。最後に選択されたリストを抽出したいときは、配列の3カラム目の「1」だけ検索すればOKです!
そして、「キャンセル」ボタンを押したら、RemoveAllItemでリストボックスすべてを消して、グループ化したウインドウのVisibleをFalseにすれば、ウインドウが消えたみたいになるって仕掛けです!

うーん、複数選択さえできれば、リストボックスは1つで解決したのに、、、、矢印作ってマクロ登録して、、、ボタンも増やして、、、、、、たかだかファイル作成するデータを選ぶだけの作業に、、、、Excelって、めんどくさいなぁ(笑)