none
ダブルバッファリングができないです RRS feed

  • 質問

  • こんにちは。

    VC#で画像を移動させるプログラムを組もうと思ったのですが、

    普通にpictureBoxに描画すると画面がちらついてしまいます。

    そこで、いろいろ検索した結果、ダブルバッファリングをすればいいと分かったのですが、

    その方法が分かりません。

    DoubleBufferdプロパティをtrueにしても改善されませんでした。

    タイマーで画像の位置を更新したいのですが、どうすればちらつきが無くなるのでしょうか?

     

    とりあえず今のタイマーイベントのコードです。

    pieceposは画像の位置で、動かす画像はpiece[15]です。


            private void timer1_Tick(object sender, EventArgs e)
            {
                piecepos[15].X = piecepos[15].X + 2;

                for (int i = 1; i <= 15; i++)
                {
                    g.DrawImage(piece[i], piecepos[i].X, piecepos[i].Y);
                }
            }

    どなたか教えていただけますでしょうか?

    よろしくお願いします。

    2011年1月29日 4:03

回答

  • こうするとちらつきが無くなりましたが、これだと画像が移動してくれません。

    今は単に Invalidate() と書かれていますよね。これだとそのコードを書いたクラス、今回の場合は Form に対して Invalidate を実行しています。
    今は何の描画イベントを起こさないといけなかったかを確認してください。

    # 元々、何に対して Refresh していたか。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク mountT 2011年1月29日 13:47
    • 回答としてマークされていない mountT 2011年1月29日 13:47
    • 回答としてマーク mountT 2011年1月29日 13:48
    2011年1月29日 13:31
    モデレータ

すべての返信

  • こちらが参考になると思います。

    @IT:.NET TIPS ダブル・バッファリングにより描画を行うには? - C# VB.NET Windowsフォーム


    Blog:プログラマーな日々 http://d.hatena.ne.jp/JHashimoto/
    2011年1月29日 4:29
  • 一か所訂正があります。

            private void timer1_Tick(object sender, EventArgs e)
            {
                piecepos[15].X = piecepos[15].X + 2;

                pictureBox1.Refresh();

           for (int i = 1; i <= 15; i++)
                {
                    g.DrawImage(piece[i], piecepos[i].X, piecepos[i].Y);
                }
            }

     

    です。Refreshを書くのを忘れてました。

    これのせいでちらついているのに、みなさんに原因を伝え忘れるとは...

    2011年1月29日 4:39
  • ありがとうございます。

    張ってくださったリンクのページは見たことがあるのですが、

    OnPaintがどうすれば発生するのかがいまいち分かりません。

    OnPaintは勝手に発生するものなのでしょうか?

     

    2011年1月29日 4:43
  • Paintイベント(OnPaintメソッド)は描画が必要になった時点で毎回発生します。例えば、ウィンドウをリサイズしたり、上に重なっているウィンドウを移動したりした場合です。

    Control.Paint イベント (System.Windows.Forms)


    Blog:プログラマーな日々 http://d.hatena.ne.jp/JHashimoto/
    • 編集済み jhashimoto 2011年1月29日 5:24 追記
    2011年1月29日 5:23
  • 分かりやすい説明ありがとうございます。

     

    ですが、まだちらつきは解消されません。

    また、最初のリンクのコードとタイマーをどう組み合わせていいのかが分かりません。


            private void timer1_Tick(object sender, EventArgs e)
            {
                piecepos[15].X = piecepos[15].X + 10;

            }

     
            private void pictureBox1_Paint(object sender, PaintEventArgs e)
            {
                pictureBox1.Refresh();

                for (int i = 1; i <= 15; i++)
                {
                    g.DrawImage(piece[i], piecepos[i].X, piecepos[i].Y);
                }
            }

    なんとなくこうかな?と思い、やってみたのですがやはりだめでした。

                this.SetStyle(ControlStyles.DoubleBuffer, true);
                this.SetStyle(ControlStyles.UserPaint, true);
                this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);

    を追加しても、

    pictureBox1_Paint(object sender, PaintEventArgs e)
    のところを

             protected override void OnPaint(PaintEventArgs e)
    にしてもダメでした。

     Paintではリサイズも何もしていないのにちらつき、OnPaintでは描画されませんでした。

    困りました。原因もわからないです。

    2011年1月29日 8:44
  • いろいろと問題のあるコードになっています。
    自分が使っているコードがどういった意味を持つものなのか意識する、考えるといったことを心がけてください。

            private void pictureBox1_Paint(object sender, PaintEventArgs e)
            {
                pictureBox1.Refresh();

    こんなコードを書いてはいけません。
    Refresh は今すぐに絵の更新を求めるメソッドであり、その結果として Paint イベントが呼び出されます。
    このようなコードを書くと無限の再帰呼び出しに陥り、スタックオーバーフローで落ちます。

    逆に今のコードで落ちないのであれば、Paint イベントが結びつけられていません。
    それはそれで問題なので、デザイナーのプロパティから雷アイコンをクリックし、Paint の行にイベントを割り付けてください。

            private void timer1_Tick(object sender, EventArgs e)
            {
                piecepos[15].X = piecepos[15].X + 10;
            }

    絵の更新を要求したいのであれば、Tick イベントで Invalidate メソッドを呼んでください。
    Refresh メソッドと違い、そのうち再描画されます。
    (Refresh メソッドはその必要がなければ、Invalidate メソッドにすべきです。重い描画を無駄にやってしまうかもしれませんので)

                    g.DrawImage(piece[i], piecepos[i].X, piecepos[i].Y);

    そもそも、この g って何ですか?
    どこかで CreateGraphics でもしてるんでしょうか?

    Paint イベントで渡される e にある Graphics プロパティに対して描くようにし、余分な Graphics は作らないようにしてください。

     

    さて、ちらつきってどんなものでしょうか?
    ダブルバッファリングで効果があるのは、徐々に絵が描かれていく様子が見えなくなることです。

    一瞬背景の灰色が見えてから、すぐに完成した絵が見える現象に対して効果があるわけではありません。
    もし、こちらの現象が出ていて、対策が必要なのであれば、背景を描画しないといった対策も考えないといけないかもしれません。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 編集済み AzuleanMVP, Moderator 2011年1月29日 14:28 無限ループと書いていたのを再帰に変更。
    2011年1月29日 9:43
    モデレータ
  • いろいろご指摘いただきありがとうございます。

    とりあえず仰っているようにしてみました。

     private void timer1_Tick(object sender, EventArgs e)
     {
     piecepos[15].X = piecepos[15].X + 10;
     Invalidate(); 
     }
    
     private void pictureBox1_Paint(object sender, PaintEventArgs e)
     {
     
     for (int i = 1; i <= 15; i++)
     {
     e.Graphics.DrawImage(piece[i], piecepos[i].X, piecepos[i].Y);
     }
     }
    

    PaintイベントにRefreshを入れると落ちるので、結びつけられてはいると思います。

    こうするとちらつきが無くなりましたが、これだと画像が移動してくれません。

     

    >さて、ちらつきってどのようなものでしょうか?

    どういう状態かといいますと、ウインドウがとても激しくチカチカしている状態です。

    タイマーイベントが呼ばれるたびに画面が一瞬暗転するような感じです。

    • 編集済み mountT 2011年1月29日 10:11 誤字
    2011年1月29日 10:11
  • こうするとちらつきが無くなりましたが、これだと画像が移動してくれません。

    今は単に Invalidate() と書かれていますよね。これだとそのコードを書いたクラス、今回の場合は Form に対して Invalidate を実行しています。
    今は何の描画イベントを起こさないといけなかったかを確認してください。

    # 元々、何に対して Refresh していたか。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    • 回答としてマーク mountT 2011年1月29日 13:47
    • 回答としてマークされていない mountT 2011年1月29日 13:47
    • 回答としてマーク mountT 2011年1月29日 13:48
    2011年1月29日 13:31
    モデレータ
  • ありがとうございました!

     pictureBox1.Invalidate();

    だったのですね。おかげでちらつくことなく描画が出来るようになりました。

    間違っているところの解説をしてくださったり、ヒントをくださったりと

    本当にありがとうございました。m(_ _)m

    2011年1月29日 13:39