ダイアログ上にスクロールバーを置き、
スクロール位置の変更に合わせて表示物を描き換える処理を作成しています。
この表示物の描画は多少時間がかかるため、
スクロールバーのボタンを連打していたりすると
描画直後にまた描画処理が走ったりしてしまうため、描画終了時に
while (PeekMessage(&msg, NULL, WM_MOUSEFIRST, WM_MOUSELAST, PM_REMOVE));
として、マウスイベントを捨ててしまうようにしてみました。
ところが今度はスクロールバーのボタンが押しっぱなし状態になってしまいました。
これはマウスが放されたことまで捨ててしまっているからかなと思うのですが、
スクロールバーに限らず、ドラッグ時の終了情報は見逃してしまわないように
マウスイベントを破棄するには、どのようにメッセージを捨てればよいのでしょうか。
while (PeekMessage(&msg, NULL, WM_MOUSEMOVE, WM_MOUSEMOVE, PM_REMOVE));
while (PeekMessage(&msg, NULL, WM_LBUTTONDOWN, WM_LBUTTONDOWN, PM_REMOVE));
while (PeekMessage(&msg, NULL, WM_RBUTTONDOWN, WM_RBUTTONDOWN, PM_REMOVE));
while (PeekMessage(&msg, NULL, WM_MBUTTONDOWN, WM_MBUTTONDOWN, PM_REMOVE));
while (PeekMessage(&msg, NULL, WM_MOUSEWHEEL, WM_MOUSEWHEEL, PM_REMOVE));
while (PeekMessage(&msg, NULL, WM_LBUTTONDBLCLK, WM_LBUTTONDBLCLK, PM_REMOVE));
while (PeekMessage(&msg, NULL, WM_RBUTTONDBLCLK, WM_RBUTTONDBLCLK, PM_REMOVE));
while (PeekMessage(&msg, NULL, WM_MBUTTONDBLCLK, WM_MBUTTONDBLCLK, PM_REMOVE));
と、BUTTONUP以外に絞って一つ一つ消していくしかないのでしょうか?
またこの場合、BUTTONDOWNが発生していないのに
BUTTONUPだけがズラズラ発生してしまう状態になりますが、
これはなにか問題は考えられませんでしょうか?
別案として描画を別スレッドに追い出してしまって
描画終了までは、イベントが来ても知らん振りすると言う
方法もあると思います。マウスイベントを捨てるとなると
何処まで捨ててよいのかとかありそうなので上記の方が
考えやすくはあると思います。
描画部分をマルチスレッド化するのは
確かMSDNのマルチスレッドのサンプルにあったと思います。
まあ、参考程度に。
そもそもマウスイベントに限って破棄する意味が分からないのだが。
キーボードでもスクロールし得るし、一部のホイールマウス用ユーティリティは直接
スクロールメッセージ飛ばすと説明されているものもある。
スクロールメッセージで処理すべきかと。
そもそも描画終了時に次の描画を止めようとしたら、スクロール開始直後の状態
で描画結果が固定されてしまうのでは。
マルチスレッドないし、タイマ処理を使うべきかと。
負荷軽減が目的として、マルチスレッドなら「描画の必要性がある場合にのみ描
画を行い、描画終了後に適度な時間Sleepさせ、これらをループする」、タイマ
なら「描画終了後に適度な時間の後飛んでくるような単発のメッセージタイマを
設定して、タイマメッセージ処理後に描画の必要があるなら、描画してメッセージ
タイマを再設定する」等のようなやり方が考えられます。
メッセージ処理がもたつくと言うだけなら描画を別スレッドにするだけであっさり終
了ですが。
PeekMessage SendMessage PostMessage 等で使われるメッセージは、送ってから
受け取るまでに確か100ms以上かかるはずです。
(周辺の動作環境によって変わると思います。)
「スクロールバーのボタンを連打していたりすると描画直後にまた描画処理が走ったり
してしまうため、」
このような動作になってもおかしくないような気がします。
「スクロールバーのボタンを連打していたりする」対応するためには、
リアルタイムでスクロールバーの値を監視するしかないと思います。
もしくは、「スクロールバーのボタンを連打していたりする」に対応しないで
ある程度無視するかですね。
ところでスクロールバーの処理の中で描画をしているのですか?
OnVScrollからコールされる関数の中で画面更新をし、
マウスイベントだけでなく、キーボードイベントも捨てています。
ちなみに、スクロールバーといっても、表示位置のスクロールではなく、
「表示番号切り替え」のような役割です。
スクロールバー位置を変更するたびに画面全体が変わります。
機能的にはツールバーなどでもよいのですが、
番号位置を視覚的に知りたいという要望があったため、
今回のようにスクロールバーを使いました。
ツールバーなどのコマンドは、ボタンが離されてから初めてコールされるため、
1コマンドごとにマウスイベントを破棄してしまえば
とりあえず連打の問題には対応できるのですが
(上記のPeekMessage(WM_MOUSEFIRST~WM_MOUSELAST)はネット上で拾いました)、
スクロールバーのほうは押した瞬間(離す前)にコールされるため、
今回のような問題になるのだろうなと認識しています。
とりあえずBUTTONUP以外を捨てているぶんには動いているようなのですが、
安心なのかどうかを断言できる情報がありませんし、
やはり本格的にやろうと思えばスレッドとかになるんでしょうね…。
以下雑談みたいなものです。
クリック時は「表示番号」を切り替えておくだけにして描画更新せず
InvalidateRectで更新領域を指定するだけにする。
WM_PAINTで「表示番号」が前回と変化していなければ前回の描画を行う。
変化していたら「表示番号」に従った画面更新をし画面へ描画をお行う。
という風にすると
連打すると表示番号が変わるが描画更新は一切しないで
連打をやめたときに最後の表示番号で画面更新をすることになる。
でもこれだと、せっかちなユーザは連打しすぎてしまうか。
図を各ツールでマウス操作時の描画は簡易描画することで高速にして
マウス操作を止めてちょっと間をおいてから
時間のかかる描画をするツールもあったな。
# もしかして特許とか?
マウスイベントを破棄するのではなく、他のウィンドウに取らせてはどうでしょうか?
描画処理直前に「描画処理中ダイアログ」を表示してしまって、描画処理終了時にこのダ
イアログを終了するようにすれば、スッキリします。(主観ですが)
この場合は、描画処理自体はsubスレッドにするなり「描画処理中ダイアログ」に実装す
るなりしないといけませんが、イベントの破棄をON,OFFするより保守性が高く、易
しい構成だと思いますよ。