tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトルダブルバッファリング
記事No6412
投稿日: 2007/10/08(Mon) 00:29
投稿者たーくん
初心者たーくんです
いつもお世話になります

ダブルバッファリングの試験的なプログラムを作っていますが
うまくいきません。
過去レス等見て下記まで自分で作りましたが行き詰まりました。
よろしくお願いします

(目的)フォームにピクチャーボックス1(300×300)が有ります。
「10ヶづつ緑の四角をバッファに描画した後、画像に転送させる」
等速で行いたい。出来れば自作のサブプロシージャから好きなタイミングで呼び出したい。

(現状)
OnPaintメソッドが自分の好きなタイミングで呼べない。呼び方がわからない。
Onpaintメソッドは勝手に呼ばれているうえに不均等な呼び出し速度。


m(--)m   2005EE です

'--------------------------------------------------------------------
Public Class Form1

    Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load

        Me.SetStyle(ControlStyles.DoubleBuffer, True)
        Me.SetStyle(ControlStyles.UserPaint, True)
        Me.SetStyle(ControlStyles.AllPaintingInWmPaint, True)

    End Sub


    Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
        MyBase.OnPaint(e)

        Dim g As Graphics = PictureBox1.CreateGraphics
        Dim brush As SolidBrush = New SolidBrush(Color.Green)
        Dim x As Integer, y As Integer
        Dim ran As Random = New System.Random
        For i As Integer = 0 To 10
            x = ran.Next(300)
            y = ran.Next(300)

            Dim rec As Rectangle = New Rectangle(x, y, 10, 10)

            g.FillRectangle(brush, rec)
        Next

    End Sub
End Class

[ツリー表示へ]
タイトルOnPaint の呼び出されるタイミング
記事No6413
投稿日: 2007/10/08(Mon) 10:14
投稿者魔界の仮面弁士
> OnPaintメソッドが自分の好きなタイミングで呼べない。呼び方がわからない。
3 種類のメソッドがあります。

通常使われるのは、『Invalidate メソッド』です。これが呼ばれると、OS に
「再描画が必要になった」と伝えることができます。OS 側はこの後、再描画を
必要としたタイミングで(メソッド処理後や DoEvents 操作のタイミングなど)、
OnPaint を自動的に呼び出してくれます。このタイミング調整は、OS 側の都合に合わせて
行われるため、再描画回数が不用意に多くならずに済み、実行コストが抑えられます。

また、再描画のタイミングを OS に任せず、自分で強制的に再描画したい場合には、
Invalidate と共に『Update メソッド』を呼び出せば OK です。ただし連続した呼び出しは、
実行コストが高くつきますので、使いどころに気をつけてください。

また、Invalidate + Update の両方の働きを持つ『Refresh メソッド』もあります。


> Onpaintメソッドは勝手に呼ばれているうえに不均等な呼び出し速度。
「画面の内容を更新するタイミング」と「実際に描画を行うタイミング」が、
必ずしも一対一に対応するわけではない、という点に注意してください。

たとえばドラッグ作業などでは、1ドットずれるごとに再描画されるわけではありません。
マウスの移動に応じて「再描画が必要である」という指示が飛んだとしても、実際の再描画は
一度にまとめて処理されます。こうすることによって、素早くドラッグした場合などには、
途中の描画を省略することによって、描画処理が本来の処理を妨げないようにしています。

プログラムからループ処理中に描画する際なども同様です。ループのたびに再描画していては
CPU / GPU 負荷が高いので、ループが終わったタイミングや、プロシージャを抜けたタイミングで
再描画が行われるように調整されています。


> Protected Overrides Sub OnPaint(ByVal e As System.Windows.Forms.PaintEventArgs)
Form の OnPaint で、PictureBox に描画しているのは何故でしょうか?
ダブルバッファリングを使うなら、引数から得た e.Graphics を使う必要があるかと。

Form の OnPaint で処理するなら、そのまま Form に対して描画すればよいでしょうし、
PictureBox に描画したいのであれば、Form 上に実装するのではなく、PictureBox 自身の
OnPaint メソッドないしは Paint イベント を使うべきかと思います。

なお、標準のダブル バッファリングではどうしても機能不足だという場合には、
BufferedGraphicsContext クラスを用いて、独自に実装するという選択肢もあります。


> Dim g As Graphics = PictureBox1.CreateGraphics
これはマズイです。作成した Graphics リソースを解放していませんので。

自分で CreateGraphics した Graphics は、毎回解放しなければなりません。
こういう時は、
 Using g As Graphics = 〜〜
   :
 End Using
の構文を使うようにしてください。


もっとも、ControlStyles.DoubleBuffer を指定したなら、自分で生成するのではなく、
「イベント/メソッド引数で受け取った e.Graphics」を使う事になると思いますが、
この場合は解放不要となります。
(自分で生成したリソースは自分で破棄、OSが生成した分は基本的にOS任せが基本)


> Dim brush As SolidBrush = New SolidBrush(Color.Green)
こちらも同様。自分で New したブラシは、Using 等できちんと解放しましょう。
逆に、(SystemBrushes などのように)あらかじめ用意されたブラシは、解放不要です。

[ツリー表示へ]
タイトルダブルバッファを使わないサンプル
記事No6414
投稿日: 2007/10/08(Mon) 14:58
投稿者魔界の仮面弁士
> (目的)フォームにピクチャーボックス1(300×300)が有ります。
> 「10ヶづつ緑の四角をバッファに描画した後、画像に転送させる」
> 等速で行いたい。出来れば自作のサブプロシージャから好きなタイミングで呼び出したい。

『等速で』という部分が気になりますが、そういった要件であれば、
「ダブルバッファ」も「OnPaint」も関係なく、こんな感じで良いのではないかと。


Public Class Form1
 Private rect(10) As Rectangle

 Private Sub DrawNext()
  Dim ran As New Random()
  For i As Integer = 0 To 10
   rect(i) = New Rectangle(ran.Next(300), ran.Next(300), 10, 10)
  Next
  PictureBox1.Invalidate()
 End Sub

 Private Sub Form1_Load(ByVal sender As Object, ByVal e As EventArgs) Handles MyBase.Load
  PictureBox1.Size = New Size(300, 300)
  DrawNext()
 End Sub

 Private Sub PictureBox1_Paint(ByVal sender As Object, ByVal e As PaintEventArgs) Handles PictureBox1.Paint
  Dim b As Brush = Brushes.Green
  Dim g As Graphics = e.Graphics
  For Each r As Rectangle In rect
   g.FillRectangle(b, r)
  Next
 End Sub

 Private Sub Button1_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button1.Click
  DrawNext()
 End Sub

 Private Sub Button2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button2.Click
  For n As Integer = 1 To 1000
   DrawNext()
   PictureBox1.Update()
  Next
 End Sub
End Class

[ツリー表示へ]
タイトルRe: ダブルバッファを使わないサンプル
記事No6415
投稿日: 2007/10/08(Mon) 22:57
投稿者たーくん
魔界の仮面弁士さま

いつもご丁寧にありがとうございます。

超初心者故、かなり知識に穴があるようです。

ご指摘の部分、一つづつ理解に努めたいと思います
やればやるほど別の疑問が出てきて
自分の知識の穴に気づき腹が立ちます。 

また サンプルまで記述していただきすごく助かります。
再度質問することもありますがどうかよろしくお願いします
m(--)m

[ツリー表示へ]