ピクチャボックスの描画が消えないようにする事について

タグの編集
投稿者 K5  (中学生) 投稿日時 2008/11/23 20:38:09
こんにちは、K5です。
このホームページのサンプル集にある
「グラフィックスが消えないようにする」というサンプルについて質問です。

「あらかじめ自動的に再描画されるようにする例」
のサンプルを改造してペイントソフトを作ろうと思ったのですが、最初に描画するグラフィクスは適用されるのですが、二回目から(後から)描画したグラフィクスが表示されません。

ウィンドウを最小化してからまた元の大きさに戻してやるとちゃんと表示されるのですが、最小化しないでもちゃんと表示されるようにするにはどうしたらいいのでしょうか。
教えていただけると助かります。
投稿者   (社会人) 投稿日時 2008/11/23 20:52:01
とりあえず、お前さんがそのサンプルをどう使って駄目だったのか、を教えてもらわん事には
答えようがないんじゃまいか。

投稿者 るきお  (社会人) 投稿日時 2008/11/23 21:43:59
そうですね。
まずはプログラムを見せていただくのが一番早いと思います。

それから、元にされたサンプルは
http://homepage1.nifty.com/rucio/main/dotnet/Samples/dnSampleKeepImage.htm
でよろしいですよね?
投稿者 K5  (中学生) 投稿日時 2008/11/24 01:56:56
返信遅れてすみません。
はい。そのサンプルを使用しました。
そしてこれがコードです。
Dim X1, Y1, X2, Y2 As Integer
    Private Sub PictureBox1_MouseDown(ByVal sender As ObjectByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseDown
        X1 = e.X
        Y1 = e.Y
    End Sub

    Private Sub PictureBox1_MouseUp(ByVal sender As ObjectByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp
        X2 = e.X
        Y2 = e.Y

        Dim g As Graphics = AutoGraphics(PictureBox1)
        g.DrawLine(dpen, X1, Y1, X2, Y2)

        End If
    End Sub

    Public Function AutoGraphics(ByVal picSource As PictureBox) As Graphics
        If picSource.Image Is Nothing Then

            picSource.Image = New Bitmap(picSource.ClientRectangle.Width, picSource.ClientRectangle.Height)

        End If

        Return Graphics.FromImage(picSource.Image)

    End Function

この場合、一度目に描いた線はちゃんと表示されるのですが、二度目から描く線は、一度最小化しないと表示されなくなるのです。
説明が下手ですみません。
投稿者 るきお  (社会人) 投稿日時 2008/11/24 02:06:52
これは「グラフィックが消えないようにする」のとはまた別の問題ですね。
DrawLineなどのグラフィックを描く命令はほとんどの場合、グラフィックを表示する命令を含んでいません。
つまり、描くことと表示することはコンピュータにとっては別の作業なのです。(人間にはわかりにくいですが・・・)

そこでPictureBox1に表示を促すInvalidateメソッドを使用すると解決すると思います。
関係ない部分でコードをそのまま実行できなかったので少し手を入れましたが、この問題だけに限定するとInvalidateメソッドでいけると思います。

   
Private Sub PictureBox1_MouseUp(ByVal sender As ObjectByVal e As System.Windows.Forms.MouseEventArgs) Handles PictureBox1.MouseUp

    Dim dpen As Pen = Pens.Red  '←るきお追加 

     X2 = e.X
    Y2 = e.Y

    Dim g As Graphics = AutoGraphics(PictureBox1)
    g.DrawLine(dpen, X1, Y1, X2, Y2)
    PictureBox1.Invalidate()    '←るきお追加 

    'End If '←るきおによるコメント化 

End Sub
投稿者 K5  (中学生) 投稿日時 2008/11/24 02:26:16
おお!!出来ました!
今までこれが出来なくてずっと悩んでたので、出来たときには感動しました!
Invalidateメソッドで表示する事が出来るのですね。勉強になりました。
それからコードがそのまま実行できなくてすみませんでした(^^;)

全く関係ない話ですが、Invalidateとは、日本語に訳すと"無効にする"という意味らしいです。
教えていただいてありがとうございました!
投稿者 るきお  (社会人) 投稿日時 2008/11/24 02:37:55
解決してよかったです。

Invalidateはご指摘のように本来は「無効にする」メソッドです。
Windowsは無効化された領域に対して再描画を行う習性があるので、
無効化することで再描画されるという仕組みです。
(WindowsXPでウィンドウを2つ重ねて、前にあるウィンドウをずらすと、
後ろにあるウィンドウの裏に隠れていた部分(を表す領域)がInvalidateされます。
そこで、すかさずWindowsはこの領域を再描画し、ユーザーからは
裏に隠れていた部分が表に出てきたように見えるわけです。)

ですので、再描画が必要ない領域まで無効化すると、
Windowsは必要ない部分までもう一度描画するのでその分パワーを使います。
もし、再描画が必要な範囲をもっとせまく限定できるのならば
Invalidateメソッドの引数にその限定された領域を指定することでパフォーマンス等向上します。

本当はInvlidateメソッドは単に無効化するだけで、再描画をするメソッドはUpdateです。
MSDNを見ると強制再描画を行うにはInvalidate後にUpdateせよという趣旨のことが書いてあるので、
Invalidateの下にUpdateも入れておいた方がいいのかもしれません。

ちょっと長く書きすぎました。このようにWindowsの描画に関する仕組みが.NETをやっていてもかかわっきます。このあたりはC++だろうが、機械語だろうがWindows上で動いていれば事情は同じですので理解しておけば今後も役に立つと思います。
投稿者 K5  (中学生) 投稿日時 2008/11/24 02:49:35
なるほど。いろいろ教えていただいてありがとうございます。
では、ペイントソフトをがんばって作りたいと思います。
(完成するかどうかは全く分かりませんが(^^;))
作っていて何か不調があればInvalidateの下にUpdateのメソッドを入れてみます。
何度も言いますが本当にありがとうございました。