VC++6のMFCでダイアログベースの画面を作成しています。
リストビューを更新する際のチラつきを抑えるためにダブルバッファリングが有効だと聞
き、サンプルを調べていますがビットマップのものしか見つけられず困っています。
ビットマップの場合、バッファへ一旦画像を書き出してから実際の画面にコピーするとい
うのは理解出来たのですが、
リストビューの場合はバッファへどのようにリストビューを書き込む?作成する?のかが
わわかりません。
以下サイトを参考にしました。
http://www.t-recipe.com/vc/flicker.html
ちょっと想像がつきませんが
CListCtrl(?)の項目を
SetRedrawぐらいではダメなほどに頻繁に更新するのでしょうか?
>リストビューの場合は
同じ場所にコントロールを2つ重ねて置いて
交互に表示非表示してやる…とか??
>リストビューを更新する際のチラつきを抑えるためにダブルバッファリングが有効だと
聞
そもそも、これはどこの情報?
リストビューのチラつきを抑えるのにビットマップを使用するタイプの
ダブルバッファリングはほとんど効果がありません。というか、スクロールに
うまく対応できないため、大変な労力がかかるため、コスト的にまったくあいません。
即座に検討をとりやめにしましょう。
気になるチラつきの発生する原因やタイミングによりますが、概ね次の
部分を修正すると良くなることがほとんどです。
1.リストビューを保持している親(MDIフレームやDLG)のクラススタイルに
CS_VREDRAWとCS_HREDRAWが設定してあれば、これを取り外す
レポートスタイルの場合で、アイテム行数が1000程度を超える場合は
2.LVS_OWNERDRAWFIXEDを指定して、全て自分で描画する。
自分は上記対策を行っていますが、50行×10カラムを秒間20回以上の
再描画更新中に、リストコントロールのサイズを変更してもまったく
チラつきませんです。
PCはcorei3 + NVIDIA Quadro FX580という平凡よりややへぼいものを
使ってます(笑)。
リストビューのブルバッファリングはOSによって難易度が異なります。
VC6じゃちょっと厳しい気はしますが、シェルバージョンが6以降であれば、拡張スタ
イルのLVS_EX_DOUBLEBUFFERを指定するだけで、そこそこ高速かつ自動的に処理してくれ
ます。
それ以前のものでダブルバッファリングを行いたいのであれば、オーナードローにし
て自前でビットマップと同じように描画するしかないです。
ただ、ちらつきを抑えるには、ダブルバッファリング以前にすべきことがたくさんあ
るので、まずはそちらを把握すべきだと思います。
ぐぐったら仲澤@失業者さんの適切な回答が他にも出てきましたので、こちらも参考
にしてはいかがでしょうか?
http://social.msdn.microsoft.com/forums/ja-JP/vcgeneralja/thread/4dfab71d-6eb2-
482f-8dfe-06b97d838262/
みなさん回答ありがとうございます。
ダブルバッファは難しそうなのでやめときます。
質問の意図がわかりにくく大変申し訳ありませんでした。
やりたいことは、30行*10カラム程のアイテムを表示するリストビューを用意し、
各アイテムの値を100msec周期に更新します。
画面やコントロールのリサイズはしません。
SetRedrawを使用してもちらつきます。(一瞬コントロール内が白くなるような感じです)
そもそも更新の仕方が悪いのでしょうか?
現在仮想リストビューを使用し、100msec周期にInvalidateRectを使用し再描画しています。
仮想リストビューは以下を参考にしました。
http://homepage1.nifty.com/takuhiro/technique/programming/virtuallistctrl/index.htm
また、リストボックスの場合も同様に100msec周期に文字列を追加したいのですがちらつ
きます。
仮想リストビュー(LVS_OWNERDATA)は、保持できる行数(アイテム数)を増やすのには
効果がありますが、すばやい描画を行うことには、たいした役には立ちません。
すばやく描画するにはLVS_OWNERDRAWFIXEDを指定して、DrawItem()時に、
自分でTextOut()するのが最も効率的です。
さらにチラ付きを抑制するにはグリッドラインの再描画を自分で行うか、
又は再描画させないようにします。
あとは、チラ付きを抑制の基本ですが、WM_ERASEBKGNDはFALSEを戻して、背景が
塗りつぶされないようにしたほうが良いでしょう。背景は必要ならば自分で
塗りつぶします。
リストコントロールはデータの描画スピードが(特に行数が増えると)とっても
カメなので、コントロールに描画をさせていてはらちがあきませんです(vv;)。
じゃあ、こんなのはどうでしょう
リストビューで保持すべきデータを持っておく
これはリストでも配列でもベクタでもなんでもいいと思います
で,更新があるのかどうかをそのデータで判定する
更新がある場合のみ再描画する
更に実データのシャドーを持つサブクラスなんか作って
このデータを書き換えるだけのメソッドを用意しておく
で、再描画周りもこのクラスに持たせてしまってしまうとか
首題とそれましたねwww
これでいかがでしょうか。
リソースファイルの IDD_MYDIALOG の STYLEに WS_CLIPCHILDRENを追加し、
Invalidate()の引数 bEraseをFALSEにする
追加です。
LPSTR_TEXTCALLBACK
など、CALLBACKを使ってください
たびだびすみません
> リソースファイルの IDD_MYDIALOG の STYLEに WS_CLIPCHILDRENを追加し、
これ不要なようです
みなさんご親切にありがとうございます。
試してみたいと思います。
不明点があったり解決しましたらまた書き込みさせて頂きますね!
> やりたいことは、30行*10カラム程のアイテムを表示するリストビューを用意し、
> 各アイテムの値を100msec周期に更新します。
100mS周期で更新しないとだめですか?
ちらつかないのが何msec周期か確かめてみましたか?
スクロール等の操作上もんだいがなければ表示のみ周期を
遅らせることも可能かと思いますが、如何でしょうか?
ちらつきの原因は、LVM_SETITEMで文字列を更新した場合、
::InvalidateRect(hndListView, &rect, TRUE)と似たことが起こり、
WM_ERASEBKGNDが生じ、背景を塗りつぶした後に文字が書かれるからです。
対策としては
仲澤@失業者さんが示したような方法も一手ですし、
私が述べた方法でもできますが、
暇なので、LVM_SETITEMで文字列を更新する手も考えました。
方法1(強引なやり方)
LVM_SETITEMで文字列を更新した後(複数行なら全部更新した後)、
::ValidateRect(hwndListView, 0);
::InvalidateRect(hwndListView, 0, FALSE);
更新領域を GetUpdateRgnで取得して、ValidateRgn/InvalidateRgnするほうが
穏やかかもしれません。
別の方法(トリッキー)
LVM_SETITEMで全部更新する前に
GWL_STYLEからWS_VISIBLEをクリアし、
更新後にWS_VISIBLEをを元に戻し、
InvalidateRect(hwndListView, 0, FALSE)を呼ぶ。
# ShowWindowではなく、単にスタイルのフラグを操作するだけです。
この方法ならば、WM_SETREDRAWと違って、WM_ERASEBKGNDは生じません。