投稿者 魔界の仮面弁士  (社会人) 投稿日時 2020/10/13 13:56:46
> ⑥ 『 Invalidate / Update / Refresh メソッド 』については、全くわかりません。
>   https://dobon.net/vb/dotnet/control/refreshupdateinvalidate.html
>   様の記述を読み、テスト用の実行ファイルを使っても理解できません。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    For n = 1 To 1000
        Label1.Text = n.ToString()
    Next
End Sub


たとえば、上記のコードを実行した場合、Label1 に表示されるのは
最後の 1000 という文字列だけであり、途中経過は表示されませんよね。

ループ回数を増やしたとしても、処理中はアプリケーションが固まるだけであり、
結局実際に描画されるのは、最後の結果のみとなります。


これは、イベント処理中というのはいわゆる「ビジー状態」に陥っており、
その作業中は、画面の再描画は何も行われないためです。

処理結果が実際に画面に反映されるのは、イベント処理が終わった後の、
何も処理されていない「アイドル状態の時」に行われる仕様です。



さてここで、Refresh メソッドを呼んでみましょう。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    For n = 1 To 1000
        Label1.Text = n.ToString()
        Label1.Refresh()  '❄強制再描画 
    Next
End Sub


Refresh メソッドを呼び出すと、ビジー状態であっても割り込みで描画処理が行われます。
上記のコードだと、1000 に至るまでの途中経過もしっかり描画されることを確認出来るでしょう。

しかしその分、ループを抜けるまでのトータル時間が遅くなっていることも体感できるかと思います。


つまり描画処理とは、相対的には「遅い」処理であると言えます。
そのため、ゲームやアニメのように、高頻度に画面を書き換える必要がある場合は別として、
ほとんどのアプリケーションでは、頻繁に画面を書き換えるべきではありません。


更新頻度が高すぎれば、どうせ人間の目では視認しきれないわけなので、
適当に間引いて更新頻度を抑えれば、速度の低下を抑えたまま途中経過の表示を実現できます。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    For n = 1 To 1000
        Label1.Text = n.ToString()
        If n Mod 25 = 0 Then
            Label1.Refresh()
        End If
    Next
End Sub




さて今度は、何も変更していないのに、Refresh を呼んでみましょう。
今回は所要時間の測定も行ってみます。

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    Dim sw = Stopwatch.StartNew()
    For n = 1 To 1000
        Label1.Refresh()
    Next
    sw.Stop()
    MsgBox(sw.ElapsedMilliseconds)
End Sub



この処理は、私の環境ではおよそ 400ミリ秒かかっていました。
しかし、Label1.Refresh() を Label1.Update() に変更した場合は 0 ミリ秒です。
Invalidate メソッドの場合は、数ミリ秒程度でした。



Update というのは、「再描画が必要なら描き換えるが、不要なら何もしない」メソッドなので、
Label の内容に差異が無ければ、実行時間はほぼゼロとなります。

Invalidate は、そのコントロールを「再描画が必要な状態」としてマークするためのものです。
再描画が終わった後は、そのコントロールが「再描画が不要な状態」に戻ります。

Refresh メソッドは、「Invalidate と Update を連続で呼び出すだけ」の強制再描画メソッドです。


⚡【Paint イベント】
 画面サイズの変更や、上に重なっていた別のウィンドウが取り除かれた場合など、
 「再描画」が必要な時に呼び出されます。
 独自の描画処理を行いたい場合は、ここの e.Graphics を利用して実装します。
 e.Graphics への描画処理結果は、このイベントの処理中は画面に反映されませんが、
 このイベント終了が終わった後のアイドル時に画面に反映されます。


🔸【CreateGraphics メソッド】
 CreateGraphics で生成した Graphics への描画結果は、直ちに画面上に反映されます。
 線を 1 本描くだけでも直ちに反映されてしまうため、描画途中の結果も
 随時表示されてしまい、ちらつきが強くなりがちです。
 また、CreateGraphics で描画した結果は、その上に他のウィンドウが重なるなどすると
 容易に消えてしまいます。
 通常は CreateGraphics ではなく、Paint イベントの e.Graphics を使うようにしましょう。


🔹【Invalidate メソッド】
 コントロールに対して、描画処理が必要であるという無効化マークを付与するための物です。
 言い換えると、再描画を「依頼」するためのメソッドであると言えるでしょう。
 これを呼んだとしても、直ちに何かが起こるわけではありませんが、
 これを呼んでおくことで、アイドル状態になったときに Paint イベントが誘発されます。
 なお、Invalidate を複数回連続で呼んだとしても、Paint の発生はアイドル時の一回のみです。


🔹【Update メソッド】
 再描画が必要とされていた場合に、Paint イベントを直ちに呼び出します。
 再描画の必要が無い場合には、呼び出しても何も起きません。


🔹【Refresh メソッド】
 いわゆる「強制再描画」メソッドです。
 このメソッドの呼び出しは、Invalidate メソッドと Update メソッドを連続で呼び出すことに等しいです。
 再描画が必要であるとマークされていようといまいと、強制的に再描画されることになるので、
 不用意に呼びすぎると、処理の低速化を招きます。