Quantcast
Channel: t-hom’s diary
Viewing all articles
Browse latest Browse all 493

VBA フォームのボタンの反応が遅い理由と対策

$
0
0

今回はVBAでユーザーフォームに配置したボタンの反応が遅い理由とその対策について紹介する。
検証のため、以下のようなカウンターフォームを作った。
f:id:t-hom:20171207210933g:plain

作成方法

フォームに配置した各オブジェクトは以下のように名前を変更した。
f:id:t-hom:20171207210921p:plain

コードは以下のとおり。

PrivateSub cmdCountUp_Click()Me.lblCounter.Caption =CLng(Me.lblCounter.Caption)+1EndSubPrivateSub cmdReset_Click()Me.lblCounter.Caption =0EndSub

課題

ぽちっぽちっと丁寧に押していく分には問題ないのだが、連続して速くクリックしたときに反応が悪い。
↓9クリックしてるのに5しかカウントされてない。
f:id:t-hom:20171207211316g:plain

この事象、単にVBAのフォームが遅いためと思い込んでいる方もいるかもしれないが、原因は別のところにある。
試しにCountUpボタンにフォーカスが当たっている状態でスペースキーを連打するとちゃんと連打スピードについてくるのだ。つまりキーでボタンを押した場合は問題ないのに、マウスでクリックした場合は遅いということになる。

原因

原因は、コマンドボタンがダブルクリックイベントを拾っているため。
連続で速くクリックすると、次のように判定される。

  1. クリック
  2. ダブルクリック
  3. クリック
  4. ダブルクリック
  5. クリック
  6. ダブルクリック

つまり、クリック間隔が短いと、偶数回目のクリックがダブルクリック扱いになってしまうため、クリックイベントとしては半分しか判定されないのだ。

対策

この対策は簡単で、単にダブルクリックイベントをキャッチアップしてシングルクリックイベントのプロシージャを呼んでやれば良い。

PrivateSub cmdCountUp_Click()Me.lblCounter.Caption =CLng(Me.lblCounter.Caption)+1EndSubPrivateSub cmdCountUp_DblClick(ByValCancelAs MSForms.ReturnBoolean)Call cmdCountUp_Click
EndSubPrivateSub cmdReset_Click()Me.lblCounter.Caption =0EndSub

この対策を施した結果がこちら。
f:id:t-hom:20171207212052g:plain

ちゃんとクリックした分カウントアップされている。

別の問題

前述の対策は、とても良さそうに思える。
ただし、コマンドボタンの無効化と組み合わせると、ボタンが陥没して戻ってこないという別の問題が多発する。

試しに数値が10に達したらボタンを無効化するようコードを書き換えてみた。

PrivateSub cmdCountUp_Click()Me.lblCounter.Caption =CLng(Me.lblCounter.Caption)+1IfCLng(Me.lblCounter.Caption)>=10ThenMe.cmdCountUp.Enabled =FalseEndIfEndSubPrivateSub cmdCountUp_DblClick(ByValCancelAs MSForms.ReturnBoolean)Call cmdCountUp_Click
EndSubPrivateSub cmdReset_Click()Me.lblCounter.Caption =0Me.cmdCountUp.Enabled =TrueEndSub

すると、このようにボタンが陥没するようになった。
f:id:t-hom:20171207212527g:plain

無効化を解除してもボタンは凹んだままで、クリックすると戻る。
ボタンの陥没は、ダブルクリックイベント時にボタンを無効化すると発生するようで、シングルクリック時は発生しない。

以下は検証の結果。

事象が発生するパターン

  1. クリック
  2. ダブルクリック
  3. クリック
  4. ダブルクリック
  5. クリック
  6. ダブルクリック
  7. クリック
  8. ダブルクリック
  9. クリック
  10. ダブルクリック ←ここで無効化されるので陥没する

事象が発生しないパターン1

  1. クリック
  2. ダブルクリック
  3. クリック
  4. ダブルクリック
  5. クリック
  6. ダブルクリック
  7. クリック
  8. ダブルクリック
  9. クリックし、次がダブルクリックにならないようしばらく時間を置く。
  10. クリック ←ここで無効化されるので陥没しない

事象が発生しないパターン2

  1. クリックし、次がダブルクリックにならないようしばらく時間を置く。
  2. クリック
  3. ダブルクリック
  4. クリック
  5. ダブルクリック
  6. クリック
  7. ダブルクリック
  8. クリック
  9. ダブルクリック
  10. クリック ←ここで無効化されるので陥没しない

ボタン陥没の回避方法

この事象はフォームのRepaintをしてみたり、DoEventsを挟んでみたりしたけれど改善されなかった。
ということで根本的な解決策は見つかっていない。(あるいは存在しない)
ひょっとするとWinAPIなどで何とかする方法はあるのかもしれないが、少なくともVBA単体では見つからなかった。

今のところ、ボタンの無効化を諦めるか、最後がダブルクリックにならないように回避コードを書くしかなさそうだ。

回避コードとしては以下のように、無効化の一歩手前でダブルクリックイベントからのシングルクリックイベント呼び出しをやめること。

PrivateSub cmdCountUp_Click()Me.lblCounter.Caption =CLng(Me.lblCounter.Caption)+1IfCLng(Me.lblCounter.Caption)>=10ThenMe.cmdCountUp.Enabled =FalseEndIfEndSubPrivateSub cmdCountUp_DblClick(ByValCancelAs MSForms.ReturnBoolean)IfCLng(Me.lblCounter.Caption)<9ThenCall cmdCountUp_Click
    EndIfEndSubPrivateSub cmdReset_Click()Me.lblCounter.Caption =0Me.cmdCountUp.Enabled =TrueEndSub

ただ軽快にカウントアップされて最後だけ1回「スカ」が入るので、イマイチだな。
↓9になるとダブルクリックイベントを無視するので最後だけボタンを2回クリックしている。
f:id:t-hom:20171207214833g:plain


Viewing all articles
Browse latest Browse all 493

Trending Articles