C# フォームのOnPaintイベントについて

タグの編集
投稿者 tecc  (社会人) 投稿日時 2010/8/6 10:18:47

また雑談カテです。すいません。

protected override void OnPaint(PaintEventArgs e)
{
     //base.OnPaint(e);

     Graphics g = e.Graphics;
     LinearGradientBrush gb = new LinearGradientBrush
         (new Point(0, 0), new Point(this.ClientSize.Width, this.ClientSize.Height),
     Color.WhiteSmoke, Color.SkyBlue);

     gb.GammaCorrection = true;
     g.FillRectangle(gb, this.ClientRectangle);

     gb.Dispose();
     g.Dispose();
}

private void mainmenu_Load(object sender, EventArgs e)
{
System.Reflection.Assembly asm =
System.Reflection.Assembly.GetExecutingAssembly();
System.Version ver = asm.GetName().Version;
this.toolStripStatusLabel1.Text = string.Format("システムバージョン:{0}",ver.ToString());
                
this.SetStyle(ControlStyles.ResizeRedraw, true);
this.SetStyle(ControlStyles.UserPaint, true);
this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);
}


こんな感じでフォームのクライアント領域の色を変更してるんですが
その際、フォームがアクティブ?になる度にOnPaintイベントが発生します。
正しい、動きなのでしょうがアクティブになる度にグラフィック描写してはコスパ的に気になってきました。

実際、このOnPaintイベントの発生を1回目以降イベントを起こさないようにしてしまった場合
何らかの不具合がでますでしょうか?
投稿者 るきお  (社会人) 投稿日時 2010/8/6 12:43:15
OnPaintでの再描画のパフォーマンスが気になるのであれば、
毎回描画ロジックを実行するのではなく、一度描画した結果をとっておいて、
状況がかわるまではその結果を描画するようにすると良いと思います。

おっしゃるようにウィンドウがアクティブ化されたときにOnPaintが呼び出されるのは特におかしいことではありませんが、タイミングや再描画範囲はOSによって指定されますのでWindowsのバージョンによって異なる部分はあります。

>実際、このOnPaintイベントの発生を1回目以降イベントを起こさないようにしてしまった場合
>何らかの不具合がでますでしょうか?
OnPaintで描画しているものが描画されなくなる以外は特に不具合はないと思います。
なお、OnPaintでe.Graphicsで描画したものは永続化されていないので、要求があるたびに描画を実行しないと、ユーザーからは消えてしまったように見えるのでご注意ください。
つまり、1回目以降OnPaintイベントを処理しないようにした場合、最初(OnPaintイベント内のロジックによって)表示されていたものが、次の瞬間から表示されないことになります。

投稿者 魔界の仮面弁士  (社会人) 投稿日時 2010/8/6 13:05:38
> C# フォームのOnPaintイベントについて
OnPaint はイベントでは無く、メソッドですね。
(Paint イベントと混同されていませんか?)

> //base.OnPaint(e);
base.OnPaint の呼び出しを省略すると、Paint イベントが発生しなくなりますのでご注意を。

> g.Dispose();
外部から引数で渡されるオブジェクトを、勝手に Dispose しては駄目ですよ。
(今回の場合は Dispose しても問題は出ないとは思いますが…)

本来、自らが Dispose しなければいけないのは、その Graphics が
自分で作成した物(Graphics.FromImage や CreateGraphics などで)の場合です。

> その際、フォームがアクティブ?になる度にOnPaintイベントが発生します
OS 側で再描画が必要と判断された場合には、OnPaint/Paint が呼び出されますので、
結果的にそう見える事もあるかも知れません。ただし、常に発生するわけではありません。

確認のために、描画処理の先頭に
 Trace.WriteLine(DateTime.Now.ToString("HH:mm:ss.ffff"));
などと書いておき、フォームを切り替えるテストを行ってみてください。OS にもよりますが、
切り替えるタイミングで、呼ばれる場合と呼ばれない場合があるかと思います。

> コスパ的に気になってきました。
ごめんなさい、それはどういう意味でしょうか?

> 実際、このOnPaintイベントの発生を1回目以降イベントを起こさないようにしてしまった場合
ここでいう「イベントを起こさない」というのは、どのような処理の事を指しておられますか?

イベントという物は、通常、自動的に呼びだされるものを利用するだけであって、
自分で発生させる類の物では無いと思います。
(コントロールやコンポーネントを開発する場合は、自分で発生させることもありますが)


> 何らかの不具合がでますでしょうか?
イベントを起こさない、という言葉の意味が分からないので回答に迷いますが、
「再描画が不要な状態にする」という意味であれば、問題無いと思います。
どのようにしてそれを実現するのか、という点が不明瞭なので確約はできませんが。

「base.OnPaint を呼びだすかどうかを変更する」という意味だとすれば、
OnPaint を呼ばない場合は自己描画、OnPaint を呼ぶ場合は標準描画となるでしょう。

「再描画要求時に何も行わないようにする(base.OnPaint すら呼ばない)」という意味なら、
恐らくは画面が更新されないという不具合になるかと思います。
投稿者 るしぇ  (社会人) 投稿日時 2010/8/6 13:38:11
今、見ているこの画面でも、再描画は頻繁に起こってます。
だから重い処理を行なうと、見た目に描画処理が遅れる事が
よく分かります。

アクティブというのがウィンドウ単位の話であれば、画面上で
アクティブなウィンドウを切り替えた場合、多くの場面で重なり
があるでしょうから、その部分が再描画されず、先にアクティブ
だったウィンドウの描画が残ったままになることが予想されます。
最小化して元にもどしてもデスクトップの描画が残ったり。

実際にテストプログラムを作るのは難しくないのでは?
確認してみればいいと思います。
投稿者 tecc  (社会人) 投稿日時 2010/8/7 09:39:41
るしぇさん
>テストプログラムを作るのは難しくないのでは?
些細な事だったかもしれません。今後はテストプログラムを作成してみます。

魔界の仮面弁士さん
>Paint イベントと混同されていませんか?
混同していません!!もうずっとイベントだと思ってました。
はずかしぃ。

>イベントを起こさない
これに関しては、アイドル時間をとろうか?などと考え中でした。

るきおさん
>1回目以降OnPaint~の瞬間から表示されないことになります。
なるほどです。というよりかそういうコードですもんね。
ありがとうございました!