tagCANDY CGI VBレスキュー(花ちゃん) の Visual Basic 2010 用 掲示板(VB.NET 掲示板) [ツリー表示へ]   [Home]
一括表示(VB.NET VB2005)
タイトル追加Formの移動で描いた図が消える
記事No9273
投稿日: 2009/08/24(Mon) 14:02
投稿者chime
VS2008での問題です。

Private Sub cmdOverview_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmdOverview.Click
        Form3.Show()
        Dim g11 As Graphics = Form3.PictureBox1.CreateGraphics
        Dim g12 As Graphics = Form3.PictureBox2.CreateGraphics
        .....
        .....
        Dim g22 As Graphics = Form3.PictureBox12.CreateGraphics

        Form3.PictureBox1.Refresh()
        .....
        .....
        Form3.PictureBox12.Refresh()
以下に描画サブルーチンがありますが、量が多くて省略します。

表題のごとくForm3の移動で描いた図をブルー枠で上下、左右と動かし画面外に
まで動かすと隠れた部分が消えます。

VB6で経験した描いたFormで、元にあった別のFormが重なった部分が消えるのに
似ていますが、このときはAutoRedrawを効かしてあれば問題が解決しました。

今回は描くformでの不具合で再度、描けば勿論描けますが、まだ解決できていません。
適当なコメントあれば幸いです。

[ツリー表示へ]
タイトルRe: 追加Formの移動で描いた図が消える
記事No9274
投稿日: 2009/08/24(Mon) 15:19
投稿者るしぇ
PictureBox の再描画処理が走っているのだから、デザイン画面で設定してある
通り、初期の描画のみ実行され、消えるのが正常な処理です。

ディスプレイ上のある点では1色しか表示できないのですから、隠れた部分の
色情報など覚えられません。常に描画し続ける事になります。

ボタンクリック時の描画処理は、ボタンを押した時のみ実行され、システム
からの命令で再描画された時に実行されませんから、当然描画されません。

> このときはAutoRedrawを効かしてあれば問題が解決しました。
英単語通り、自動でもう一度描いていたに過ぎません。

有名な話なので色々なサイトで Graphics 関連の話題として解説されています。
このサイトでは VB.NET Tips から描画・画像のサンプルを見て下さい。

[ツリー表示へ]
タイトルRe^2: 追加Formの移動で描いた図が消える
記事No9275
投稿日: 2009/08/24(Mon) 17:46
投稿者chime
るしぇさん
コメントありがとうございます。原理が了解できました。

設計ではフルスクリーンに12のグラフを描かせているので

Form3のLoadに
Private Sub Form3_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
        Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None
        Me.WindowState = FormWindowState.Maximized
End Sub
としてFormの枠を触らさないように(最小化も含めて)させてこの問題を
逃げました。

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

chime

[ツリー表示へ]
タイトルRe^3: 追加Formの移動で描いた図が消える
記事No9276
投稿日: 2009/08/24(Mon) 18:31
投稿者魔界の仮面弁士
> としてFormの枠を触らさないように(最小化も含めて)させてこの問題を
> 逃げました。

逃げちゃ駄目です。それだと、他のウィンドウが重なり合ったときに消えてしまいますよ。


そもそも、CreateGraphics メソッドを使った描画手法はあまり一般的ではありません。
PictureBox に描画する場合には、原則として

 (1) 描画処理は、各 PictureBox の Paint イベントの中に記述する。
  描画に必要な Graphics は、イベント引数 e より取得する事。

 (2) Dim bmp As New Bitmap(x, y) などで空のビットマップを用意し、
  Dim g As Graphics = Graphics.FromImage(bmp) で得た Graphics に描画し、
  描画結果の bmp を、PictureBox の Image または BackgroundImage に割り当てる。

のいずれかを使うようにしてみてください。


一度描画した結果をそのまま使い続けるような場合には、案2 を使うと良いでしょう。


一方、描画内容の更新が頻繁にある場合には、案1の Paint イベント法を使います。
この場合、再描画が必要になるたびに各コントールの Paint イベントが呼ばれますので、
そこで描画処理を行うことで、結果が消えてしまわないようにするという物です。
(この方法を使う場合には、Refresh/Update/Invalidate メソッドの違いについて
調べておいてください。不用意な Refresh の繰り返しはパフォーマンスを低下させます)

[ツリー表示へ]
タイトルRe^4: 追加Formの移動で描いた図が消える
記事No9277
投稿日: 2009/08/24(Mon) 20:47
投稿者chime
魔界の仮面弁士さん
 コメントありがとうございます。

> > としてFormの枠を触らさないように(最小化も含めて)させてこの問題を
> > 逃げました。
>
> 逃げちゃ駄目です。それだと、他のウィンドウが重なり合ったときに消えてしまいますよ。

Me.FormBorderStyle = Windows.Forms.FormBorderStyle.None
        Me.WindowState = FormWindowState.Maximized

をloadイベントに追加することでwindowsの青い枠のないfullScreen画面となります。
これでは上下、左右、最小化などできません。作者の用意したボタンでForm1
に戻しているのです。従って、ご指摘の他のwindowsと重なることはできません。
これの過程を"逃げ"と称したのです。

誤解のないように。

ご指摘の方法の検討もします。時間対効果がどうか気になりますが。

chime

[ツリー表示へ]
タイトルRe^5: 追加Formの移動で描いた図が消える
記事No9278
投稿日: 2009/08/24(Mon) 22:03
投稿者魔界の仮面弁士
> これでは上下、左右、最小化などできません。
ですね。それは分かりますが、先の回答はそのことを理解した上での発言なのです。


> 従って、ご指摘の他のwindowsと重なることはできません。
実は、話はそう単純でもないのです。

たとえ全画面表示かつ最前面表示のアプリであったとしても、
他のウィンドウが重なる可能性が多々あります。


実際当方では、下記のパターンなどで描画内容が消えてしまうことを実際に確認しています。

---
たとえば他のアプリケーションが起動していれば、[Alt] + [Tab] で切り替えることで、
他のウィンドウに切り替わります。Vista なら、[Windows] + [Tab] もあるでしょう。
これによって、他のウィンドウが重なってしまう事がありえます。

もしも他のアプリが起動していなかったとしても、[Ctrl] + [Shift] + [Esc] で
タスクマネージャが起動しますから、やはりこの問題は避けられません。

もう少し簡単なキー操作でも起こりえます。[Windows] キーを押せば、手前に
スタートメニューが表示されるかと思いますが、もしもその下に描画内容があれば、
描画した結果は消える事になります。

あるいは、[Windows] + [D] キーを 2 回行った場合にも、CreateGraphics の結果は
消えてしまうことでしょう。

たとえキー操作もマウス操作もペン操作も無かったとしても、スクリーンセーバーが
起動したとすれば、やはり描画結果は消されてしまうかと思います。
---

他にもいろいろなパターンが考えられます。

それらに対して個別に対策を採っていくよりは、正しい手法で実装しておいたほうが、
結果的には対費用効果が高くなるかと思いますよ。

[ツリー表示へ]
タイトルRe^6: 追加Formの移動で描いた図が消える
記事No9279
投稿日: 2009/08/25(Tue) 12:51
投稿者chime
魔界の仮面弁士さん

キー操作、スクリーンセーバなどで描画が消えるとのご指摘、理解しました。

前回、ご指摘の方法は自動再描画ですね。等高線とワイヤフレームをそれぞれ
12個の描画のため、直ぐに、当方のプログラムには対応できません。
メモリーも心配です。

手動での再描画にしました。描画群のあるForm3に[ReDraw]のボタンを作成
し、Form1に元からある描画ボタンのコードをここにも移しました。

ご指摘のトラブル例
(1)[Alt] + [Tab] で切り替え
(2)[Ctrl] + [Shift] + [Esc]
(3)[Windows] キーを押す
(4)[Windows] + [D] キーを 2 回行った場合
ではCreateGraphics は消えてしまいますが、[ReDraw]のボタンは消えませんので
ボタンダウンで再描画できます。

これで、問題は解決したことになりました。 

何らかの操作で[ReDraw]のボタンが消えることがあるのかが知りたいところです。

chime

[ツリー表示へ]
タイトルRe^7: 追加Formの移動で描いた図が消える
記事No9280
投稿日: 2009/08/25(Tue) 16:21
投稿者ダンボ
> 手動での再描画にしました。描画群のあるForm3に[ReDraw]のボタンを作成
> し、Form1に元からある描画ボタンのコードをここにも移しました。

横から失礼します。
[ReDraw]ボタン押下時に再描画処理をまとめることができたのならば、
ボタンを設けずとも、Form3のPaintイベントに全く同じ再描画処理を
書いておけば自動再描画になると思うんですが。

> 何らかの操作で[ReDraw]のボタンが消えることがあるのかが知りたいところです。
無理やりプログラムで消さない限り消せないと思います。

[ツリー表示へ]
タイトルRe^8: 追加Formの移動で描いた図が消える
記事No9281
投稿日: 2009/08/25(Tue) 17:58
投稿者chime
ダンポさん
 コメントありがとうございます。

> [ReDraw]ボタン押下時に再描画処理をまとめることができたのならば、
> ボタンを設けずとも、Form3のPaintイベントに全く同じ再描画処理を
> 書いておけば自動再描画になると思うんですが。

これを試みました。
(3)[Windows] キーを押す

(A)再描画がスタート画面の下に描かれます。
(B)スタート画面を消す。再描画3回程度でとまる。

この後、マウスを画面上を無作為に広く動かすと、paint eventのため
再描画がされます。

実験結果はこの方法は可能ですが、再描画回数がボタン操作の1回に比較
して、数回多いときで5回、また画面が少しでも異常状態になれば
再描画が始まります。

手動のボタン再描画がこの場合は最適ですが。

> > 何らかの操作で[ReDraw]のボタンが消えることがあるのかが知りたいところです。
> 無理やりプログラムで消さない限り消せないと思います。

そうあればいいのですが。

chime

[ツリー表示へ]
タイトルRe^9: 追加Formの移動で描いた図が消える
記事No9282
投稿日: 2009/08/25(Tue) 18:32
投稿者魔界の仮面弁士
> 実験結果はこの方法は可能ですが、再描画回数がボタン操作の1回に比較
> して、数回多いときで5回、また画面が少しでも異常状態になれば
> 再描画が始まります。

再描画が繰り返されるという事は、どこかに不自然なコードが
紛れ込んでしまっているのだと思います。


たとえば描画処理の中に、PictureBox1.Refresh() などというコードが
紛れ込んでいないでしょうか。

Invalidate … 再描画を要求するメソッド。再描画のタイミングは OS 側が制御する。
Update     … 再描画を行うメソッド。ただし再描画が不要だった場合には何も起きない。
Refresh    … 強制的に再描画させるためのメソッド。Invalidate + Update の意味。


あるいは、CreateGraphics メソッドを呼び出していたりはしないでしょうか。


> 手動のボタン再描画がこの場合は最適ですが。
Paint イベント法で正しく実装されている場合、ボタンクリックで描画させるためには、
 Private Sub Button1_Click(・・・・・・
  PictureBox1.Refresh()
  PictureBox2.Refresh()
  PictureBox3.Refresh()
 End Sub
に相当するコードを記述することになります。



ただし今回の場合、画面の更新頻度はさほど高くないようですので、
Paint イベントを使って描画処理をおこなわせる(No.9276 の第1案)という手法では無く、
PictureBox の Image プロパティに描画した結果を割り当てる方法(No.9276 の第2案)を
使った方が、都合が良いかと思いますよ。こうすれば、画像が消える事は無いので、
VB6 で AutoRedraw を設定したときのような効果を得る事ができます。

[ツリー表示へ]
タイトルRe^10: 追加Formの移動で描いた図が消える
記事No9284
投稿日: 2009/08/25(Tue) 22:55
投稿者chime
魔界の仮面弁士さん

  いろいろコメントありがとうございます。

> たとえば描画処理の中に、PictureBox1.Refresh() などというコードが
> 紛れ込んでいないでしょうか。

 はい。12個いれています。

> > 手動のボタン再描画がこの場合は最適ですが。
> Paint イベント法で正しく実装されている場合、ボタンクリックで描画させるためには、
>  Private Sub Button1_Click(・・・・・・
>   PictureBox1.Refresh()
>   PictureBox2.Refresh()
>   PictureBox3.Refresh()
>  End Sub
> に相当するコードを記述することになります。

 そのようにして実行結果に満足しています。

> PictureBox の Image プロパティに描画した結果を割り当てる方法(No.9276 の第2案)を
> 使った方が、都合が良いかと思いますよ。こうすれば、画像が消える事は無いので、
> VB6 で AutoRedraw を設定したときのような効果を得る事ができます。

当方にとってはImageをとる手法を検討する必要があります。

chime

[ツリー表示へ]
タイトルRe^11: 追加Formの移動で描いた図が消える
記事No9285
投稿日: 2009/08/25(Tue) 23:51
投稿者魔界の仮面弁士
> > たとえば描画処理の中に、PictureBox1.Refresh() などというコードが
> > 紛れ込んでいないでしょうか。
>  はい。12個いれています。

Paint イベント内で Refresh しないようにしてください。
Refresh メソッドを呼び出すと、強制的に描画処理を行わせる事になりますので、
そこで Paint イベントが再度呼び出されてしまいます。

PictureBox1_Paint には、Picture1 の描画処理のみを記述し、
PictureBox2_Paint には、Picture2 の描画処理のみを記述するようにします。


>> PictureBox の Image プロパティに描画した結果を割り当てる方法(No.9276 の第2案)を
>> 使った方が、都合が良いかと思いますよ。こうすれば、画像が消える事は無いので、
>> VB6 で AutoRedraw を設定したときのような効果を得る事ができます。
> 当方にとってはImageをとる手法を検討する必要があります。

No.9276 に書いた内容の再掲となりますが、その方法を実装するためには、
今まで、PictureBox1.CreateGraphics() を使っていたコードを、
 Dim bmp As New Bitmap(300, 200)   '任意サイズ(ここでは300x200)の画像を用意。
 Using g As Graphics = Graphics.FromImage(bmp)    '描画に必要な Graphics を得る。
  g.Clear(SystemColors.Control)   '背景塗りつぶし。Color.Transparent や Color.White などでも可。
  g.DrawLine(……)   '各種描画処理。DrawImage とか DrawText とか。
 End Using
 PictureBox1.Image = bmp    '描画された Bitmap を PictureBox に割り当てる。
のように変更すれば OK です。この方法では、Paint イベントが不要となります。

Image プロパティや BackgroundImage プロパティに割り当てられた画像は、
他のウィンドウが重なっても、消えてしまう事はありません。

[ツリー表示へ]
タイトルRe^12: 「解決」追加Formの移動で描いた図が消える
記事No9287
投稿日: 2009/08/26(Wed) 11:35
投稿者chime
魔界の仮面弁士さん

 コメントありがとうございます。

Bitmap Imageによる方法を検討し、Windows重畳による画像消去対策に成功
しました。

(1) Dim g* As Graphics文をすべて書き換え
(2) PictureBox*.Refresh()をはずし
(3) AutoRedraw Functionを追加

が修正項目です。これで、問題は完全に処理されました。Redrawボタンを
消します。

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

chime

以下は修正の具体的コードです。


Private Sub Button1_Click_1(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
        Form3.Show()
        'Dim g11 As Graphics = Form3.PictureBox1.CreateGraphics
        ........................
        'Dim g22 As Graphics = Form3.PictureBox12.CreateGraphics
        ........................
        
        Dim g11 As Graphics = AutoRedraw (Form3.PictureBox1)
        ........................
        Dim g22 As Graphics = AutoRedraw(Form3.PictureBox12)
  
        'Form3.PictureBox1.Refresh()
        ........................
        'Form3.PictureBox12.Refresh()
        
        画像描画処理コード
        
End Sub
        
        
Public Function AutoRedraw(ByVal Orgpic As PictureBox) As Graphics
        If Orgpic.Image Is Nothing Then
            Orgpic.Image = New Bitmap(Orgpic.Width, Orgpic.Height)
        End If

        Return Graphics.FromImage(Orgpic.Image)

End Function

[ツリー表示へ]
タイトルRe^13: 「解決」追加Formの移動で描いた図が消える
記事No9288
投稿日: 2009/08/26(Wed) 14:29
投稿者魔界の仮面弁士
そのコードだと、まだ問題を抱えています。

> Dim g11 As Graphics = AutoRedraw (Form3.PictureBox1)
ではなく、
 Using g11 As Graphics = AutoRedraw (Form3.PictureBox1)
  g11.DrawLine( … )
 End Using
といったコードに変更する必要があります。

Graphics クラスというのは、使い終わったら「Dispose メソッド」で破棄しなければ
なりません。そして、確実に Dispose するための仕組みがこの Using ブロックです。

たとえば、『Graphics.FromImage で取得した Graphics』ですとか、
『CreateGraphics メソッドで取得した Graphics』のように、
自身で作成した Graphics に対しては、きちんと後片付けする必要があります。

# ただし、自分で作成したものでない場合(たとえば、Paint イベントの e.Graphics)や、
# まだ使用中のオブジェクトの場合は、Dispose してはいけません。

なお、Graphics クラスの他にも、Bitmap クラスや Pen クラスなどが
Dispose を必要としていますので、後始末を忘れないようにしてください。

> chime
chime さんではなく、
chime さんだったような…?

[ツリー表示へ]
タイトルRe^14: 「解決」追加Formの移動で描いた図が消える
記事No9290
投稿日: 2009/08/26(Wed) 18:17
投稿者chime
魔界の仮面弁士さん


(A) ご推奨のVB2005からの「Usingステートメント」を使って
Disposeメソッドが呼び出され、確実にファイルをクローズ
するように書き換えは
Dim g11 As Graphics = AutoRedraw(Form3.PictureBox1)
1個ならコメントどうりで可能です。
現在のコードでは12個を同時に
Dim g11 As Graphics = AutoRedraw(Form3.PictureBox1)
Dim g12 As Graphics = AutoRedraw(Form3.PictureBox2)
.................................
Dim g22 As Graphics = AutoRedraw(Form3.PictureBox12)
と宣言し、[For]-[Next]で処理しています。
12個のDimをUsingに書き換え、終了処理で
12個EndUsingを処理の最後に書きました。

沢山の入れ子スタイルであまり見栄えはよくありません。

(B) 当サイトの[VB.NET専用メニュー]-[VB.NET Tips一覧]-[描画・画像(37)]
[PictureBox上で消える画像&描画・消えない画像&描画 (5)]
にある g.Dispose()の手法を使えば

何も修正しないで、
g11.Dispose() : g12.Dispose() .........: g22.Dispose()
を処理の最後に置いてもよいと思いますが。

上記 (A)はVB2005以降です。

(A)と(B)の優劣は?

即の対応ありがとうございます。

chime

[ツリー表示へ]
タイトルRe^15: 「解決」追加Formの移動で描いた図が消える
記事No9294
投稿日: 2009/08/27(Thu) 00:24
投稿者魔界の仮面弁士
> 沢山の入れ子スタイルであまり見栄えはよくありません。
入れ子にせずとも、一つの Using で複数のオブジェクトを対象にする事もできますよ。


> 何も修正しないで、
> g11.Dispose() : g12.Dispose() .........: g22.Dispose()
> を処理の最後に置いてもよいと思いますが。
もちろん、それでも構いません。
その場合は、何らかのエラーが発生しても Dispose が呼び出されることを
保証させるために、Dispose 処理を Finally 〜 End Finally 内に記述するのが
好ましいとされています。

[ツリー表示へ]