見出し画像

エクセルVBA のリストボックス(ListBox)をマウスでスクロールさせる方法

 最近は、仕事において DX と叫ばれて、数年経ったかと思うが、私はプライベートにおいても、少しでも自分の時間を確保するために、単純で定期的なファイルの名前変更やコピーは、WSH(JScript)を使ったり、エクセルVBA でシステム化したりしている。

 後者のエクセルVBA によるファイルコピーは、単に、ダウンロードした各種公共料金明細やクレジットカードの利用明細などを所定のフォルダに移動するだけのエクセルシステムである。

 いや、一応、タブ型のファイラー(Tablacus Explorer)を常用しているので、毎回フォルダをたどる必要もなく、一つひとつ手動でファイルを移動させるにしてもクリック数回程度で済む。とはいえ、そんなクリック数回でさえも、これら対象のファイルが増えれば、それなりに時間がかかるものだ。

 これら作業のイメージに類似したものといえば……例えば、受信したメールの自動振り分けに近いだろうか。メールの自動振り分けのように、ファイルの様々な属性などから、条件をつけてルール化できれば、完全な自動化もできるのだろうが、ファイルの種類も多く、そんなに単純に条件化できそうでもなかったことから、振り分け先の判断は、人間(私自身)に残っている。

 


○マウススクロールしないリストボックス(ListBox)

 さて、そんなシステムの具体的な仕様なのだが……エクセルのシート上に配置したリストにあるファイルやフォルダを選択し、[移動]ボタンを押すことでファイルを実際に移動させる程度のもので、まあ、特定のファイル移動に特化した簡易ファイラーと言ってもいいものだろうか。

 そして、そのリストというのは、正確には「リストボックス(ListBox)」になるのだが、実際に運用が始まると、すぐに違和感がある。
(ん? ……マウスのスクロールで、リストボックスがスクロールしないんだが?)

 調べたところ、やはり同じ悩みの方は多いようで、その対策としては、「リストボックスを使う代わりに、リストビュー(ListView)コントロールを使えばいい」とある。はて、これらは何が違って、どうして同じようなものが2つ存在しているのだろうか?

 

○リストボックス(ListBox)とリストビュー(ListView)の違いとは?

 この違いは、ざっくり言うと、前者がエクセルVBA に標準で付属する ActiveXコントロールで、後者はいわゆる拡張機能であり、別途追加するもので使えるようになるものだ。そして、この後者は、実は Visual Basic 6.0 のために用意されたコントロールであり、次のいずれかの条件を満たす場合は、エクセルで利用が可能なようだ。

(条件)
・エクセルが2000以降で32bit 環境である
・エクセルが2019以降で64bit 環境である

 ただし、上記条件を満たしていても利用できない場合があるようだ。その場合は、「手動で、MSCOMCTL.OCX を登録しなおせばよい」というのもあったので、上の条件を満たすのに使えない場合は、試す価値はあるだろう。

 さて、肝心の私の環境だが……残念ながら、この条件には合致しない。ちょうどこの狭間の環境にいるというか、単に、(将来性を見据えて)64bit のエクセルを導入したものの、引き続き古いエクセルを使い続けているだけであり……要は、まあ、このリストビューコントロールが使えないのだ! 一応、手動で、MSCOMCTL.OCX を登録したりしてみたのだが……やはりうまくいかない。

 

○リストボックスをマウススクロールさせる方法の検討

 では、このような場合に、どのようにしてリストボックスをマウスで使ってスクロールさせたら良いのか……? ネットを徘徊すれば、みな、いろいろと試行錯誤している。

 ひとつは、いわゆるサブクラス化という手法だ。具体的には、該当となるコントロールのウィンドウハンドルを取得し、メッセージフックを開始したり――と、いわゆるウィンドウズプログラミングの文化を知らない方だと、何をやっているのか分からないことをする手法だ。

 幸い、私は、20年以上前に、ちょうどこの辺をゴリゴリコーディングしていた経験があるので、何をやっているのかは分かるのだが……それでも、やはり強引なやり方をしているように感じる。実際、ネットでも「非常に不安定」という表現があり、私の環境で試したところ……なんと! エクセルが固まってしまった……。

 私は、ただ単に、リストボックスをマウスでスクロールさせたいだけなのだが……もっとシンプルにできないのだろうか?

 

○実は、私の(過去の)専門分野か?

 余談だが、それこそ私が20年以上も昔にやっていた仕事というのが……会社で販売するパッケージシステムに特化した独自コントロールの作成業務であり、それは、単に Microsoft が用意した標準のコントロールを組み合わせたものだったり、拡張させたりするものだった。当時は、C言語系というか、Visual C++(MFC)で作成していたのだが……今回は、エクセルのオマケみたいな VBA で、このような拡張ができるのだろうか――という心配はある。

 無駄な車輪の再発明はしたくないし、先ほどのサブクラス化のようなゴリゴリコーディングもしたくはない。

(エクセルVBA における、リストボックスでの MouseMove イベントで取得できる情報は……マウスのボタンやシフトキーなどの押下情報とマウスポインタの座標か……。)

(もしかしたら「マウスポインタが下向きや上向きに動いたときに、シフトキーや右クリックが押されていたら、その方向にスクロールさせる」という方法で実装ができないだろうか?)

 こういうのは、実際に試してみないと、その感触なども分からない。そして、実際にやってみたところ、意外に感触のいいものが出来上がったので、それをここに残しておくものである。

 

○リストボックスをマウスでスクロールさせる方法

 ということで、いきなり関連ソースコードから貼り付ける。取り急ぎ使いたい方は、以下をコピペすれば使えるかと思うが……注意が一つ! これは、マウスホイールを使ってスクロールさせるのではなく、リストボックスを右クリック押下状態で、マウスを上下することによりスクロールさせるものである。ちょうど、PDFファイルをつかんでスクロールさせるようなイメージを持ってもらったら伝わるかと思う。

Option Explicit

Dim m_bSkipChangeEvent As Boolean

Private Sub ScrollListbox(ByRef lbScroll As Variant, iButton As Integer, iShift As Integer, iY As Integer)
    ' スクロール方向の決定(同じ位置の場合は方向はそのまま)
    Static s_iYOld As Integer
    Static s_iAddIndex As Integer
    If (iY - s_iYOld > 0) Then
        s_iAddIndex = 1
    ElseIf (iY - s_iYOld < 0) Then
        s_iAddIndex = -1
    End If
    s_iYOld = iY
    
    ' マウス右ボタン又はシフトキー押下時のみスクロール動作
    If ((iButton <> 2) And (iShift <> 1)) Then
        Exit Sub
    End If
    
    ' 速さ調節
    Static s_iWait As Integer
    s_iWait = s_iWait + 1
    If (s_iWait < 2) Then
        Exit Sub
    End If
    s_iWait = 0
    
    ' スクロールと最大最小調整
    Static s_iIndex As Integer
    s_iIndex = s_iIndex + s_iAddIndex
    s_iIndex = WorksheetFunction.Max(0, s_iIndex)
    s_iIndex = WorksheetFunction.Min(s_iIndex, lbScroll.ListCount - 1)
    m_bSkipChangeEvent = True
    lbScroll.ListIndex = s_iIndex
    lbScroll.ListIndex = -1
    m_bSkipChangeEvent = False
End Sub

Private Sub ListBox1_Change()
    If (m_bSkipChangeEvent) Then
        Exit Sub
    End If
    ' 通常の ListBox1_Change イベントはここ以降に記載
End Sub

Private Sub ListBox1_MouseMove(ByVal Button As Integer, ByVal Shift As Integer, ByVal X As Single, ByVal Y As Single)
    Call ScrollListbox(ListBox1, Button, Shift, CInt(Y))
End Sub

 適当にコメントも入れているので、特に説明は不要とは思うが、一応、スクロールの量や向き、押下ボタンの種類などは、それぞれの環境に応じて適宜調整されたい。

 

○こんなことしなくたって……

 繰り返すが、このプログラムは、右クリック押下状態でスクロールさせる動作であり、ホイールの回転に対応しているわけではない。しかしながら、それでも、マウスだけでスクロールさせることができるのはありがたい。

 とはいえ、
「わざわざこんなことしなくても、単に左クリック(選択)したまま、上下に動かせば事足るのでは?」
という声が聞こえてきそうではあるが、それはそれで、環境によっては問題があるのだ。

 その問題というのは、具体的には、青い選択枠が変更になる度に ListBox1_Change 関数が呼び出されることになり、状況によっては、不必要な処理が(スクロールの度に大量に)発生してしまうことがある。例えば――これは今回の私のシステムの話ではあるが――このリストにはフォルダ一覧が登録されてあり、選択が変わる度に、該当フォルダにディスクアクセスが行き、そのフォルダ内にあるファイルが、また別のリストに表示されるようにしている。つまりは、リストがスクロールするだけで、無駄なディスクアクセスが頻繁に発生するため、決して気持ちがいいものではないのだ。

 今回、私が実装した処理は、ListBox1_Change 内に一部処理を追加することで、このようなことが起こらないような工夫をしている。また、ScrollListbox の処理は、関数化しているため、一応、複数のリストボックスにも対応し、例えば、ListBox2 においても、簡単に処理を追加することができる。とはいえ……スクロールの位置は共有される(s_iYOld)ため、気持ちいい動作にはならないこともあるが……まあ、ここは、そんなに大きな問題はないだろう(問題があれば、適宜、変数を用意されたい。)。

 

○まとめ

 ということで、今回は、具体的なコードをつけた記事とした。過去、MSX のプログラムを掲載したことがあるが、エクセルは今回が初めてか。利用者も多いエクセルVBA を投稿するなど、おこがましくも思うが、このようなサンプルを見つけられなかったので、今回、投稿してみたものである。誰かの何かのヒントにでもなれば幸いだ。

この記事が気に入ったらサポートをしてみませんか?