パネル内にDrawLineで描画した線の再描画の高速化

回答済み パネル内にDrawLineで描画した線の再描画の高速化

  • 2012年6月15日 9:21
     
     

    お世話になります。

    MDIのプログラムを作成しています。

    MDI子ウィンドウにパネルを配置し、パネルのpaint関数内で折れ線グラフを描画しています。

    MDI親ウィンドウのスクロールバーを上下することでMDI子ウィンドウのグラフが表示されたり隠れたり

    すると思うのですが、隠れている状態から表示される際、パネルのpaint関数が動作し折れ線グラフが再描画

    されるのですが、マウスのホイール操作による画面スクロールが遅くなります。

    (スクロールバーの矢印ボタンをマウスでクリックすることによる画面スクロールは遅くありません。)

    パネル内のグラフの再描画は、IsVisible関数を使うことで、新たに表示された個所のみ再描画するようには

    しているので、画面スクロールの速度はだいぶ改善できてきたかとおもっているのですが、さらに改善できないかと思い質問しました。

    以下のコードで改善できる点などありましたらご教示ください。

    [MDI子ウィンドウ内のパネルのPaint関数]

    //DrawLine関数により、折れ線グラフを描画

            private void pnlGraph_Paint(object sender, PaintEventArgs e)
            {

            ...

                    Pen penMagenta = new Pen(Color.Magenta, 1);

                    // lstGrhData:グラフデータの入ったList

                    for (int i = 0; i < lstGrhData.Count - 1; i++)
                    {

               //グラフの点が表示クリップ領域にいる場合

                        if (e.Graphics.IsVisible(x1, y1) == true || e.Graphics.IsVisible(x2, y2) == true)
                        {

                 //線を描画

                            e.Graphics.DrawLine(penMagenta, x1, y1, x2, y2);
                        }

                    }

            ...
     }

    [MDI親ウィンドウ内のマウスホイール関数]

            [DllImport("user32.dll", CharSet = CharSet.Auto)]
            private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

            [DllImport("user32.dll")]
            private static extern IntPtr GetWindowLong(IntPtr hWnd, Int32 nIndex);

            public const int GWL_STYLE = (-16);
            public const int WS_VSCROLL = 0x00200000;

            private const int WM_SCROLL = 276;
            private const int WM_VSCROLL = 277;
            private const int SB_LINEUP = 0;
            private const int SB_LINEDOWN = 1;


            private void MenuForm_MouseWheel(object sender, MouseEventArgs e)
            {

                MdiClient mc = GetMdiClient(this);

                IntPtr style = GetWindowLong(mc.Handle, GWL_STYLE);

                if (((int)style & WS_VSCROLL) == 0)
                {
                    return;
                }
               
                if (0 < e.Delta)
                {
                    SendMessage(mc.Handle, WM_VSCROLL, (IntPtr)SB_LINEUP, IntPtr.Zero);
                }
                else if (e.Delta < 0)
                {
                    SendMessage(mc.Handle, WM_VSCROLL, (IntPtr)SB_LINEDOWN, IntPtr.Zero);
                }


                mc.Invalidate(true);

            }



すべての返信

  • 2012年6月16日 11:06
     
     回答済み
    内部で Bitmap を作成し、Panel の BackgroundImage に指定するわけにはいかないのですか?そうすれば、再描画はグラフそのものが変更されるときのみになるはずですが。

    Jitta@わんくま同盟

    • 回答としてマーク エネコン 2012年7月2日 4:42
    •  
  • 2012年6月16日 14:17
     
     回答済み

    描画に関しては Jitta さんの意見と同様で、Paint イベントで毎回描画ルーチンを動かすのではなく内部 Bitmap に描画しておいて Bitmap を描画という形が自然だと思います。

    ただ・・・前回のスレで伝えたと思いますが Invalidate(true) でないと子ウィンドウの再描画処理が発生しないため、こちらではアクティブ化に問題が発生するので、そのコードを例示するのは問題あるんじゃないですかね?

    ---------------------------------------------------------------------------------------------------------

    Paint イベントルーチンが重たいので Invalidate(false) にしたのでしょうが、何故 true でないといけないのか、false との違いはなんなのかというのを理解せずに修正するのは危険ですよ。

    API を使ってのカスタマイズは色々と問題があるので、やはり返信するべきではなかったかな・・・・。

    今回のケースだけでもないですが Paint イベントで時間のかかる複雑な処理を行うべきではありませんよ。

    • 編集済み kyano30 2012年6月16日 15:08 追記
    • 回答としてマーク エネコン 2012年7月2日 4:43
    •  
  • 2012年6月18日 7:42
     
     

    >>ただ・・・前回のスレで伝えたと思いますが Invalidate(true) でないと子ウィンドウの再描画処理が発生しないため、こちらではアクティブ化に問題が発生するので、そのコードを例示するのは問題あるんじゃないですかね?

    大変失礼しました。

    質問のコードを Invalidate(true)に修正しました。

    Bitmapに描画後、Panel の BackgroundImage にBitmapを設定するように修正しました。

    スクロールの動作を改善できましたが、1点だけ気になる点があります。

    MDI親ウィンドウをスクロールしている最中、Bitmapの画像の残像が残ってしまうのですが

    これはどうしようもないでしょうか?

    ( 子ウィンドウが選択できなくならないよう、スクロール処理のなかで、Invalidate(true)により再描画しているためしょうがないかなとも思っていますが

     解決策があればご教示ください。)

    なお、質問のコードをDrawLineからDrawCurveに変更すると再描画が早くなるのですが、

    描画されるグラフが違ってしまうので解決になっていませんでした。

    Paint イベントで再描画を早くしようとするならDrawCurveでなくDrawLinesでした。




  • 2012年6月18日 9:43
     
     

    スクロール動作が改善したのはよかったですが、気になってる部分の理由は Invalidate での再描画ではなく MDIClient 側の WM_VSCROLL の処理のせいだと思いますよ。

    解決策はそのあたりを変更すれば改善するかもしれない、といったところですが確証はありませんね。


    ついでなので例示コードについて意見すると GetWindowLong の使い方が多少問題があるように見えます。

    標準スクロールバーの表示状態取得で使うと答えましたが、そう使うのなら GetScrollBarInfo を使った方がよいのじゃないかな?


    • 編集済み kyano30 2012年6月18日 9:43 入力間違い
    •  
  • 2012年7月2日 4:42
     
     

    返信が遅くなりました。

    回答ありがとうございます。

    GetScrollBarInfoを使ったスクロールについて、勉強してみます。

    このスレッドで質問した内容について、Panel の BackgroundImage にBitmapを設定することで、

    解決できましたので、ここでこのスレッドを閉じたいと思います。

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