none
親フォームのループ中のイベントで子フォームに描画。終わった後にリザイズで表示が消える。 RRS feed

  • 質問

  • 親フォームの長時間のループ中にイベントを発生させ、子フォームのイベントハンドラー
    (Paintイベントではない)でリアルタイムに子フォームにグラフィックを描いています。

    ループの最中はチャント描けていますが、終了した後に子フォームをリサイズ
    するとループ中に描かれた内容が消えます。

    Paintイベントでのコーディングでないのでそのようになってしまうようですが、
    どうしたら、ループが終わった後も、グラフィックが消えないようにできますか?

    (幾つか前の質問
     ”本体フォームのforループで子フォーム上にグラフィックを
     描くには(gの扱い方、イベントのやり方)”のプログラムです。)

    2010年10月4日 1:14

回答

  • 前のスレッドではお役に立てなかったようですが、もう一度チャレンジさせてもらいます。

    描画の処理の為に大きな配列が必要なんでしょうか。
    もしかして、ただ単に描画対象のデータが大きな配列に格納されているだけじゃないかなと想像しました。
    ただし書かれた通り前者なのでしたら、Paint のたびに描画処理を行うと重いアプリになりそうに思いました。
    そのため、やはり Bitmap を利用されてはと思います。

    > 他のフォームの下になってふたたび現れた場合は消えませんが、どうしてでしょう。

    Paint がどのようなタイミングで呼び出されるかは OS 次第です。
    Vista からは、そのように動作します。
    検索したところ、次の @IT の記事がわかりやすそうでした。

    改良されたVistaの描画アーキテクチャ
    http://www.atmarkit.co.jp/fwin2k/vista_feature/05hardware/05hardware_03.html

    ただし、どのような場合に Paint が呼び出されるか(再描画しなければ消えてしまうか)はあまり重要ではなく、そうなる場合が多々あるという認識が重要かと思います。

    前のスレッドの続きになりますが、Bitmap を利用する方法について、具体的に書かせてもらいます。

    PictureBox では、Bitmap の内容を PictureBox 自身が自動的に再描画してくれます。
    クサキさんの現状は、Paint イベント以外で PictureBox.CreateGraphics メソッドで得られた g に対して描画されていると思いますが、そうではなく Bitmap に関連付けられた g に対して描画されると、再描画は PictureBox に任せられますので、今回のように消えることはなくなりますし、データが変化したときだけ描画を処理するだけで済むようにできます。

    コードのサンプルですが、私の
    http://social.msdn.microsoft.com/Forums/ja-JP/csharpgeneralja/thread/505fda78-96dc-41c1-9ca5-d789c1a101a4/#ffc9f08a-4111-4d79-9ed6-2c90d33bfb3e
    の「子フォーム」側のコードを以下に差し替えると、ご希望の動作になりそうに思います。

    // 子フォーム
    public partial class Form子 : Form
    {
        ・・・
        private void form親_DrawDataChanged(object sender, DrawDataChangedEventArgs e)
        {
            // 親からもらったデータを使用して bitmap に描画
            // (描画データが大きな配列でも、e.Data で渡されるのは参照だけです)
            var bitmap = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
            using (var g = Graphics.FromImage(bitmap))
            {
                g.DrawLine(Pens.Red, e.Data.Point1, e.Data.Point2);
            }

            // bitmap を表示
            pictureBox1.Image = bitmap;

            // または、bitmap を再利用しても、場合によってはメリットがありそう。
            //if (pictureBox1.Image == null)
            //{
            //    pictureBox1.Image =
            //        new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
            //}
            //using (var g = Graphics.FromImage(pictureBox1.Image))
            //{
            //    //g.Clear(Color.Transparent);
            //    g.DrawLine(Pens.Red, e.Data.Point1, e.Data.Point2);
            //}
            //pictureBox1.Invalidate();
        }
    }

    • 回答としてマーク クサキ 2010年10月7日 0:22
    2010年10月4日 9:56
  • リサイズだけではなく、他のフォームの下になってふたたび現れた場合や、そのフォームが画面からはみ出した状態からふたたび全体を表示する場合などでも再描画が必要です。したがって、Paintイベントの度に再描画するようにロジックを組む必要があります。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク クサキ 2010年10月7日 0:23
    2010年10月4日 2:21
    モデレータ
  • > 色んなアドバイスを全て試すことが出来ず、申し訳なく思っています。

    返信の仕方に配慮不足なところがあるのは自覚してたりします。
    こちらこそごめんなさい。

    > pictureBox1.Image = bitmap; のあとにpictureBox1.Refresh();   
    > にを追加しました。

    必要でしたか。失礼しました。

    > などのエラーが発生します。何がまずいのでしょうか?

    PictureBox の Image に割り当てたままの bitmap を Dispose されているからかなと思います。
    Dispose する前に、先に Image に null を代入してみてください(そして再設定)。

    > そもそも、サイズを変えて表示するには、
    > やはり再描画する必要があるような気がしてきました。

    PictureBox の SizeMode プロパティを StretchImage にするのはいかがでしょうか?(あと Doc や Anchor)
    これで良ければ、リサイズのためのコードは不要です。

    • 回答としてマーク クサキ 2010年10月7日 0:23
    2010年10月5日 6:58

すべての返信

  • 下記の方法はどうでしょうか。

    1. グラフィックの描画に必要なデータを設定するためのプロパティを子フォームに用意
    2. グラフィックの描画は子フォーム(または子フォーム上のPictureBoxとか)のPaintイベントで行う
    3. 親フォームはループ内で、子フォームのプロパティにデータを設定し、子フォームのRefreshメソッドを呼び出して再描画する

    Paintイベント内で描画すれば、リサイズしてもグラフィックは消えません。

     


    なかむら(http://d.hatena.ne.jp/griefworker)
    2010年10月4日 2:15
  • >Paintイベントでのコーディングでないのでそのようになってしまうようですが、

    どうしたら、ループが終わった後も、グラフィックが消えないようにできますか?

     

    仰る通り、Paintイベントのe.Graphicsに対して処理をしていない為と思われます。

    ループ最後を描画した時と同じ処理を、Paintイベントで呼び出してはどうでしょうか。

    どのようにデータを持っているかが問題になるとは思いますが。

    2010年10月4日 2:18
  • リサイズだけではなく、他のフォームの下になってふたたび現れた場合や、そのフォームが画面からはみ出した状態からふたたび全体を表示する場合などでも再描画が必要です。したがって、Paintイベントの度に再描画するようにロジックを組む必要があります。


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    • 回答としてマーク クサキ 2010年10月7日 0:23
    2010年10月4日 2:21
    モデレータ
  • 時間と共に変化する現象を長い時間、リアルタイムでグラフィックで描画しています。
    再度、同じ処理をするには、幾つかの配列を大きく確保しなければならず、
    やりたくありませんでしたが、仕方がないんですね。

    前の質問でBitmapを使えば何か良いことがあるようでしたが、
    この話とは関係が無いんですね。

    > リサイズだけではなく、他のフォームの下になってふたたび現れた場合や、
    > そのフォームが画面からはみ出した状態からふたたび全体を表示する場合
    > などでも再描画が必要です。

    そのフォームが画面からはみ出した状態からふたたび全体を表示する
    場合は消えませんが、どうしてでしょう。

    2010年10月4日 6:56
  • 書き方が悪かったかもしれません。フォームをマウスでドラッグして半分ぐらい画面からはみ出るようにし、そこからそのままマウスでドラッグしてフォーム全体が表示されるように戻します。その際、はみ出した部分のみ消えたままになっていると思います。

    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2010年10月4日 8:08
    モデレータ
  • すいません。書き間違いました。正しくは以下のようです。

    他のフォームの下になってふたたび現れた場合は消えませんが、どうしてでしょう。

     

    2010年10月4日 9:27
  • 前のスレッドではお役に立てなかったようですが、もう一度チャレンジさせてもらいます。

    描画の処理の為に大きな配列が必要なんでしょうか。
    もしかして、ただ単に描画対象のデータが大きな配列に格納されているだけじゃないかなと想像しました。
    ただし書かれた通り前者なのでしたら、Paint のたびに描画処理を行うと重いアプリになりそうに思いました。
    そのため、やはり Bitmap を利用されてはと思います。

    > 他のフォームの下になってふたたび現れた場合は消えませんが、どうしてでしょう。

    Paint がどのようなタイミングで呼び出されるかは OS 次第です。
    Vista からは、そのように動作します。
    検索したところ、次の @IT の記事がわかりやすそうでした。

    改良されたVistaの描画アーキテクチャ
    http://www.atmarkit.co.jp/fwin2k/vista_feature/05hardware/05hardware_03.html

    ただし、どのような場合に Paint が呼び出されるか(再描画しなければ消えてしまうか)はあまり重要ではなく、そうなる場合が多々あるという認識が重要かと思います。

    前のスレッドの続きになりますが、Bitmap を利用する方法について、具体的に書かせてもらいます。

    PictureBox では、Bitmap の内容を PictureBox 自身が自動的に再描画してくれます。
    クサキさんの現状は、Paint イベント以外で PictureBox.CreateGraphics メソッドで得られた g に対して描画されていると思いますが、そうではなく Bitmap に関連付けられた g に対して描画されると、再描画は PictureBox に任せられますので、今回のように消えることはなくなりますし、データが変化したときだけ描画を処理するだけで済むようにできます。

    コードのサンプルですが、私の
    http://social.msdn.microsoft.com/Forums/ja-JP/csharpgeneralja/thread/505fda78-96dc-41c1-9ca5-d789c1a101a4/#ffc9f08a-4111-4d79-9ed6-2c90d33bfb3e
    の「子フォーム」側のコードを以下に差し替えると、ご希望の動作になりそうに思います。

    // 子フォーム
    public partial class Form子 : Form
    {
        ・・・
        private void form親_DrawDataChanged(object sender, DrawDataChangedEventArgs e)
        {
            // 親からもらったデータを使用して bitmap に描画
            // (描画データが大きな配列でも、e.Data で渡されるのは参照だけです)
            var bitmap = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
            using (var g = Graphics.FromImage(bitmap))
            {
                g.DrawLine(Pens.Red, e.Data.Point1, e.Data.Point2);
            }

            // bitmap を表示
            pictureBox1.Image = bitmap;

            // または、bitmap を再利用しても、場合によってはメリットがありそう。
            //if (pictureBox1.Image == null)
            //{
            //    pictureBox1.Image =
            //        new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
            //}
            //using (var g = Graphics.FromImage(pictureBox1.Image))
            //{
            //    //g.Clear(Color.Transparent);
            //    g.DrawLine(Pens.Red, e.Data.Point1, e.Data.Point2);
            //}
            //pictureBox1.Invalidate();
        }
    }

    • 回答としてマーク クサキ 2010年10月7日 0:22
    2010年10月4日 9:56
  • > 前のスレッドではお役に立てなかったようですが、もう一度チャレンジさせてもらいます。
    自分の知っている範囲を少しずつ広げていくしかできませんで、
    色んなアドバイスを全て試すことが出来ず、申し訳なく思っています。

    > 描画の処理の為に大きな配列が必要なんでしょうか。
    > もしかして、ただ単に描画対象のデータが大きな配列に格納されているだけじゃないかなと想像しました。
    今は、ループの最中は大きな配列は使っていません。
    ループが終わった後に、リサイズ等で描画が消えてしまうということで、
    すべてのデータを残しておいて再描画する必要があるのではないかということです。


    ご案内のようにして、基本的に当初の問題は解決しました。
    ただ、最後のループの描画だけしか表示されていませんでしたので、
    bitmapのnewは外に出しました。また、それでも途中経過の
    1ループ分の描画だけが表示されるだけでしたので、
    pictureBox1.Image = bitmap; のあとにpictureBox1.Refresh();   
    にを追加しました。

    新たな問題です。リサイズでフォームを変えた時、pictureBoxも同じ比率で、
    大きさを変えようと、pictureBoxのClientSizeChangedイベントで
    下記のように新しいpictureBoxの大きさで、新しいBitmapを作り直そうと、
    if (bitmap != null)
    {                   
       bitmap.Dispose();
       bitmap = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
    }                   
    としましたら、
    ここや、Application.Run(new StartForm()); のところで
    ”'System.ArgumentException' のハンドルされていない例外が System.Drawing.dll で発生しました。
    追加情報: 使用されたパラメータが有効ではありません。”
    などのエラーが発生します。何がまずいのでしょうか?

    そもそも、サイズを変えて表示するには、
    やはり再描画する必要があるような気がしてきました。
    如何でしょうか?

     

    2010年10月5日 6:32
  • > 色んなアドバイスを全て試すことが出来ず、申し訳なく思っています。

    返信の仕方に配慮不足なところがあるのは自覚してたりします。
    こちらこそごめんなさい。

    > pictureBox1.Image = bitmap; のあとにpictureBox1.Refresh();   
    > にを追加しました。

    必要でしたか。失礼しました。

    > などのエラーが発生します。何がまずいのでしょうか?

    PictureBox の Image に割り当てたままの bitmap を Dispose されているからかなと思います。
    Dispose する前に、先に Image に null を代入してみてください(そして再設定)。

    > そもそも、サイズを変えて表示するには、
    > やはり再描画する必要があるような気がしてきました。

    PictureBox の SizeMode プロパティを StretchImage にするのはいかがでしょうか?(あと Doc や Anchor)
    これで良ければ、リサイズのためのコードは不要です。

    • 回答としてマーク クサキ 2010年10月7日 0:23
    2010年10月5日 6:58
  • if (bitmap != null)
    {                   
       pictureBox1.Image = null;
       bitmap.Dispose();
       bitmap = new Bitmap(pictureBox1.ClientSize.Width, pictureBox1.ClientSize.Height);
    }                   
    上記のようにしてたら、エラーはなくなりました。
    しかし、グラフィックの表示もなくなりダメです。

    > PictureBox の SizeMode プロパティを StretchImage にするのはいかがでしょうか?(あと Doc や Anchor)

    PictureBoxのサイズの変更と同時に、中身のグラフィックも拡大、縮小したいと思っています。
    自然画像の場合はStretchImageで問題ありませんが、グラフのように幅1で描かれたグラフィックは
    拡大すると線が太く、縮小すると線が途切れたり無くなったりしグラフには適当でありません。

    2010年10月6日 0:27
  • > しかし、グラフィックの表示もなくなりダメです。

    new した bitmap に対して描画されていないか、
    その bitmap を Image に割り当てられていないかのどちらかかと思います。

    > 拡大すると線が太く、縮小すると線が途切れたり無くなったりしグラフには適当でありません。

    するとクサキさんの方針通り、ClientSizeChanged イベントで再描画するしかないですね。
    基準となるサイズと変更後のサイズの比率を使って座標を計算して再描画されるといいと思います。
    この描画処理はメソッドにしておき、データの変化時とサイズの変化時の両方で呼び出すようにします。
    (ちなみに、もし Paint イベントで描画する方法をとられる場合には、ClientSizeChanged では pictureBox1.Invalidate() の実行だけを行うようにします。これによって Paint での全体の描画につながります。)

    これまでの話の組み合わせで実現できそうに思いますので、もしまだ問題が残っていましたら、それを具体的に書いてみてください。

    2010年10月6日 5:13
  • Bitmapを使うと、最小化の時、描画が消えることが無く有効だと思います。

    ただ、線幅1のグラフィックを描く場合に、内容の拡大・縮小までやるには、
    やはりデータを残しておき、再描画する必要があるということだと思います。

    グラフィックのこと大分整理できました。
    何を重視するかで個別の仕様は考えて行こうと思います。
    ありがとうございました。

    2010年10月7日 0:21