none
MFC のダイアログウィンドウで、コントロール移動時のちらつきについて

    質問

  • 現在 MFC でダイアログウィンドウを使ったアプリを作成していて、ウィンドウのリサイズに
    対応して各コントロールを変形、移動するような物を作成しております。これについては正常
    に動作しています。

    そして、今回リサイズではなくリストコントロールのエッジ等をマウスでドラッグして各コン
    トロールを変形、移動させる処理を追加しました。コントロールの移動処理はリサイズと同じ
    処理を使用し、マウスの MOVE イベントで呼び出して処理をしています。

    この場合、コントロールの移動自体は出来るのですが、ドラッグ中に各コントロールがバタバ
    タとちらつきます。ウィンドウのリサイズ時は全くちらつきがなくスムーズに移動出来ていま
    す。コントロールの移動処理は同じものを使用しているのに、マウスイベントから呼び出すと
    ちらつきが起きて見苦しくなります。

    マウスイベントからでもスムーズに移動できるようにならないでしょうか。

    コントロールの移動処理は、BeginDeferWindowPos() DeferWindowPos() EndDeferWindowPos()
    で行っています。ウィンドウのリサイズ時は OnSize() から呼び出しています。

    マウスのドラッグによるコントロールの変形は以下の手順です。

    (1) ドラッグする領域の LBUTTONDOWN でドラッグ開始。SetCapture() を実行。
    (2) OnMouseMove() でマウス移動を検出し point 変数から、各コントロールの変更位置を計算。
    (3) リサイズ時と同じ、コントロールの移動処理を呼び出す。
    (4) OnLButtonUp() でドラッグ終了。ReleaseCapture() を実行。

    マウスでドラッグして移動させた結果は正しく移動出来ているのですが、ドラッグ中が非常にばた

    ばたして見苦しいです。何とかならないでしょうか。

    2017年12月21日 8:42

すべての返信

  • すみません。まだ詳細に検証を行っていませんが、ダイアログのウィンドウスタイルには WS_CLIPCHILDREN (リソースエディタでのプロパティ名は "Clip Children")を設定していますでしょうか?

    また、開発環境 Visual Studio バージョンとちらつきが発生する環境の OS は何でしょうか?

    ※ Visual Studio 2015 & Windows 10 (x64) 環境でダイアログの OnMouseMove でリストボックスのリサイズを行ってみましたが、ちらつきは気になりませんでした。

    2017年12月21日 9:06
  • 各コントロール間の、特にリストコントロールとその他のコントロールの親子関係が不明なので一般論になります。
    CListCtrlの描画がチラつく原因は二つ考えられます。

    (1)CListCtrlの親が、再描画されるとき、当該リストコントロールの領域も含めて背景消去している場合
    (2)CListCtrlのOnDraw()やOnSizeの処理が重いため

    (1)の場合は親のウインドウスタイルにWS_CLIPCHILDRENを設定すると改善する可能性があります。
    (2)の場合、当該CListCtrlを派生して、受領したWM_ERASEBKGNDを無視する方法があります。
    つまり
     BOOL MyListCtrl::OnEraseBkgnd(CDC* pDC){ return FALSE;}//消去無用を戻す
    ですね。

    2017年12月21日 9:38
  • ありがとうございます。ご指摘の設定はされてませんでしたので、メインダイアログの
    プロパティで Clip Children を TRUE にしてみました。

    そのところ、以前のちらつきは相当改善されました。これならば実用レベルではないか
    と思います。ただ1点、エディットコントロールがドラッグ中に残像が残る現象があり
    ました。まあ気にしなければいいとは思うのですが、これも何か他の設定で改善できる
    のでしょうか。

    今色々試してみている所です。

    ちなみにこの現象は Windows 10 では起きますが、Windows 7 では起きないようです。

    2017年12月21日 10:08
  • 仲澤@失業者様。ありがとうございます。

    リストコントロールやツリーコントロール、エディットコントロール、ボタンやテキスト
    等を連動させています。全てメインダイアログの子です。

    (1) の WS_CLIPCHILDREN でかなり改善されました。ということは、ウィンドウのリサイズ
    時は各コントロールの背景が消去されないが、今回のマウスドラッグ時は消去されていた
    ということですね。勉強になりました。

    まだ、エディットコントロールの変形時に、コントロール内部に一瞬残像が見えるような
    現象があるのですが、これは背景とはまた違った原因なのでしょうか。

    2017年12月21日 10:41
  • 色々な考え方ややり方がありえるのですが、自分の場合親DLGの変形時には

    (1)親DLGの変形と子コントロールの位置計算は、親DLGのOnSize()のタイミングで行う。
    (2)DLG::OnSize()では、
     (2.1) まず、親のクライアントRECTを取得し、子の移動処理
        この移動にはMoveWindow()を使う。この時
        再描画不良が起きないように、再描画オプション=TRUEで実行する
        これは、標準コントロールの背景ブラシが無効に設定されているため。
        移動でデータ更新が起こるものは事前にSetRedraw(FALSE)してから移動し復帰させる。
        ボーダーを持つものは一旦ボーダーを無効にしてから移動し、
        移動が完了してからポーターを復帰させて全体を再描画する(非クライアントの領域再描画を指定する)
        これはボーダーの描画が非同期に発生(ボーダーのちらつき)するのを防ぐため。
     (2.2) 親DLGはなるべくクライアント領域の再描画が起こらないような処理で再描画する。
        場合によっては派生元の処理を無視して自前で再描画する。

    コントロール毎の特性やオプションの違いによって移動前処理と移動後処理が異なるので、各コントロール毎に実装してます。
    最近のPCは早いので相当凝ったことをしても大丈夫です。
    で、このくらいやると、Apple的ないわゆる「ぬるぬる」感がでてきます(笑)。

    2017年12月22日 1:56
  • ちらつきを防止する 1 つの方法にウィンドウの拡張スタイルに WS_EX_COMPOSITED を付けるというものがあります。

    ダイアログの場合でしたら、リソースファイル *.rc を開いて該当のダイアログテンプレートに下記のように WS_EX_COMPOSITED を付けます。

    IDD_MFCAPPLICATION1_DIALOG DIALOGEX  0, 0, 320, 200
    STYLE DS_SHELLFONT | WS_POPUP | WS_VISIBLE | WS_CAPTION
     | WS_THICKFRAME
     | WS_SYSMENU
    EXSTYLE WS_EX_APPWINDOW | WS_EX_COMPOSITED // ここに追加
    CAPTION "MFCApplication1"
    FONT 9, "MS UI Gothic"
    BEGIN
        DEFPUSHBUTTON   "OK",IDOK,209,179,50,14
        PUSHBUTTON      "キャンセル",IDCANCEL,263,179,50,14
        CTEXT           "TODO: ダイアログのコントロールをここに配置",IDC_STATIC,10,96,300,8
    END
    2017年12月22日 3:45
  • kenjinote 様。ありがとうございます。

    VisualStudio 2015 で Windows 10 x64 で試験しています。リストコントロールに
    連動してツリーコントロール、エディットコントロール、ボタン、プログレスバー
    スタティックテキスト等10個ぐらいを同時に変形、移動させています。

    ちらつくのはリストコントロールというより、他の色々なコントロールがばたばた
    とちらついていました。

    しかし、ご指摘の WS_CLIPCHILDREN の設定でほとんどのちらつきはなくなりました。
    1点エディットコントロールの変形時に、コントロール内部にエッジの残像が残る
    現象がありました。この残像も変形が終われば消えるので、そのままでもまあなんと
    かいけるかなと思っています。これも改善できればもちろんいいのですが。

    2017年12月22日 8:24
  • 仲澤@失業者様。ありがとうございます。

    親ダイアログのリサイズ時に、以前は MoveWindows() で子コントロールを移動させてい
    たのですが、周りのコントロールの残像のようなゴミが残るので、移動の順番を変えたり
    いろいろやってみましたが、結局断念しました。

    ご指摘の再描画オプションや SetRedraw() 等をうまく使えば出来るのですね。勉強にな
    りました。

    今は、BeginDeferWindowPos() DeferWindowPos() EndDeferWindowPos() を使えばゴミ等
    が出ないので、これで移動させています。

    今回それでもエディットコントロールで変な残像が出るので、MoveWindow() をうまく使
    えば出来るのかもしれません。ちょっと検討してみます。

    ありがとうございました。

    2017年12月22日 8:37
  • kenjinote 様。ありがとうございます。

    ご指摘の方法を試してみました。私が使っている VisualStudio 2015 では、この修正をして
    VisualStudio を起動し、リソースビューでリソースを確認しようとすると、以下のようなエ
    ラーが出ます。

    error RC2104: undefined keyword or key name: WS_EX_COMPOSITED

    ただ、ビルドは出来たので実行してみたところ、エディットコントロールのちらつきはなく
    なりました。しかし、リストコントロールがプログラム起動直後からドラッグに関係なく常
    時ちらついています。

    VisualStudio が古いのでしょうか。

    2017年12月22日 8:58
  • Edit内の残像は、ボーダー(非クライアント領域)なので、再描画のタイミングが異なります。
    DeferWindowPos() は内部でSetWindowPos()を使用していると予測できます。
    DeferWindowPos()のフラグにSWP_DRAWFRAMEを指定してみてください。

    2017年12月22日 9:02
  • 仲澤@失業者様。ありがとうございます。

    ご指摘の SWP_DRAWFRAME を追加してみましたが、残念ながらエディットコント
    ロール内の残像は変わりませんでした。

    ご指摘ありがとうございました。

    2017年12月22日 9:37