C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[21]: パネル内の座標値、そして座標値の範囲を設定す [1]


(過去ログ 31 を表示中)

[トピック内 24 記事 (21 - 24 表示)]  << 0 | 1 >>

■14692 / inTopicNo.21)  Re[18]: パネル内の座標値、そして座標値の範囲を設定す
  
□投稿者/ Tetsu (16回)-(2008/02/25(Mon) 14:55:48)
Tetsu さんの Web サイト
ご無沙汰しています。先日魔界の仮面弁士さんが送ってくださったコードを少し変え、
チェックボックスではなく「ボタンをクリックしたら描画される」ようにしました。以下のようにしました。

        private void button1_Click(object sender, EventArgs e)
        {
            panel2.Paint += panel2_Paint;
            panel2.Invalidate();
        }

そうしたところ、ボタンをクリックをした後、ちゃんと描画されましたし、再描画も問題ありませんでした。

これを元に、自分自身が作りたいコードにも同じようにpanel2.Invalidate();を使って再描画を試みましたが、
どうしても、「一瞬だけ描画され、すぐに画面から全てが消えてしまう」ようになってしまいます。
上記のボタンクリックの場合と同じように、以下のようにpanel2.Invalidate();を使いました。

        private void button1_Click(object sender, EventArgs e)
        {
           //座標値を得て、描画をさせるメソッドを呼び出す
            this.myDraw.DrawMovingObjects();
            panel2.Invalidate();
        }

Invalidate();の一行なしでコードを走らせると、描画はされますが、画面をスクロールすると、
それまで描画されていなかった部分は表示されません。
画面をスクロールしてから再びボタンを押すと、パネルに写っている部分については再描画がされます。

また、Invalidate()を使う位置をいろいろと変えてみましたが、やはり、描画され、すぐに消えてしまいます。

Invalidate()の使い方が根本的に間違っているのでしょうか。
私のコードの場合、デリゲートを使っていますが、それが関係しているのでしょうか。
以下に再びコードをお知らせします。
お手数ですが、アドバイスをいただけると嬉しいです。よろしくお願いします。


なお、描画に使う座標値は、新たにMovingPointというクラスを作りました。
MovingPointにはID、x座標、y座標をフィールドとして作成し、
MovingPoint型のmPointというインスタンスを作成し、そこに座標値を格納しました。
さらに、そのmPointを格納するArrayList mPointsをつくり、
mPoints.Add(mPoint);というふうに、全てのmPointをArrayListの中に格納しました。
このArrayListであるmPointsをイベントハンドラの中で使用し、座標を取り出し、折れ線を描いています。


【DrawingMODクラスのコード】
    public delegate void DrawOnPanel(object sender, PaintEventArgs e, ArrayList mPoints);//デリゲート

    class DrawingMOD
    {

        public event DrawOnPanel DrawPolyline; //イベントフィールド
        
        private Control m_control;

        //コンストラクタ
        public DrawingMOD(Control c)
        {
            this.m_control = c;
        }

        //パネルに折れ線を描画するメソッド
        public void DrawMovingObjects()
        {
            //変数を定義
            int tID = 0;
            float xCo = 200.01f;
            float yCo = 200.01f;

            //ID, x座標,y座標を格納するMovingPointクラスのオブジェクトを格納するArrayListを作成
            ArrayList mPoints = new ArrayList();

            for (int i = 0; i < 3; i++)
            {
                //ID, x座標,y座標を格納するMovingPointクラスのオブジェクト
                MovingPoint mPoint = new MovingPoint();

                //ID, x座標,y座標をmPointに入れる
                mPoint.TrackID = tID;
                mPoint.XCoordinate = xCo;
                mPoint.YCoordinate = yCo;

                //mPointをArrayListであるmPointsに格納する
                mPoints.Add(mPoint);

                //ID, x座標,y座標の値を変える
                tID++;
                xCo += 70;
                yCo += 50;
            }

            PaintEventArgs e = new PaintEventArgs(m_control.CreateGraphics(), m_control.ClientRectangle);
            //イベントを発生させる
            this.DrawPolyline(this.m_control, e, mPoints); 

            //再描画を試みる
            //this.m_control.Invalidate();
        }
    }



【Form1のコード】
    public partial class Form1 : Form
    {
        DrawingMOD myDraw; //Form1で共通に使用するオブジェクト変数の宣言

        Panel panel1 = new Panel();
        Panel panel2 = new Panel();

        public Form1()
        {
            InitializeComponent();

            //panel1、panel2のプロパティ
            panel1.AutoScroll = true;
            panel1.Dock = DockStyle.Fill;
            panel2.AutoSize = true;
            panel2.Size = new Size(1000, 1000);
            panel2.Location = Point.Empty;

            panel1.Controls.Add(panel2);
            this.Controls.Add(panel1);

            this.Size = new Size(400, 400);

            this.myDraw = new DrawingMOD(this.panel2);
            //イベントとイベントハンドラを関連づける
            myDraw.DrawPolyline += new DrawOnPanel(myDraw_DrawPanel);
            
        }

        private void button1_Click(object sender, EventArgs e)
        {
           //座標値を得て、描画をさせるメソッドを呼び出す
            this.myDraw.DrawMovingObjects();
            
            //再描画を試みる
            //panel2.Invalidate();
        }

        //myDraw_DrawPanelイベントハンドラ
        private void myDraw_DrawPanel(object sender, PaintEventArgs e, ArrayList mPoints)
        {

            //背景色を設定
            e.Graphics.Clear(Color.Black);

            //ペンの色を定義
            int cRed = 200;
            int cGreen = 100;
            int cBlue = 100;

            //描画をするためのPointFの配列を用意
            PointF[] myPoints = new PointF[mPoints.Count];

            foreach (MovingPoint m in mPoints)
            {
                //myPointsにmPointsの座標を入れる
                myPoints[m.TrackID] = new PointF(float.Parse(m.XCoordinate.ToString()), float.Parse(m.YCoordinate.ToString()));                
            }

            //ペンを作成
            using (Pen pen = new Pen(Color.FromArgb(cRed, cGreen, cBlue), 4))
            {
                //折れ線を矢印にする
                pen.EndCap = LineCap.ArrowAnchor;
                //折れ線を描く
                e.Graphics.DrawLines(pen, myPoints);
            }
        }

    }

引用返信 編集キー/
■14695 / inTopicNo.22)  Re[19]: パネル内の座標値、そして座標値の範囲を設定す
□投稿者/ 魔界の仮面弁士 (655回)-(2008/02/25(Mon) 15:40:03)
No14692 (Tetsu さん) に返信
> private void button1_Click(object sender, EventArgs e)
> {
>   panel2.Paint += panel2_Paint;
>   panel2.Invalidate();
> }
クリックするたびに、Paint イベントを割り当ててはマズイと思いますよ。
最初に一度だけ割り当てておけば十分です。

ここでは -= せずに、+= ばかり繰り返していますので、一度の
再描画要求に対し、同じ描画処理が複数回繰り返されてしまっています。


No14463のコードは、「Paint で処理する場合/何も処理しない場合」を
切り替えているからあのようなコードになっているのであって、
今回のように、常に自分で描画したい場合には当てはまりません。


> //座標値を得て、描画をさせるメソッドを呼び出す
> this.myDraw.DrawMovingObjects();
> panel2.Invalidate();
座標値を得るまでは良いですが、実際の描画処理は Paint に記述してください。
Invalidate メソッドを呼び出せば、Paint イベントも連動して発生します。

逆に、Paint イベントを使いたく無いのであれば、座標値を得た後、
画像を Bitmap クラスにして、それを BackgroundImage に割り当てましょう。


座標データが変更されたタイミングは、DrawingMOD クラスで
把握できるでしょうけれども、描画すべきタイミングを
把握できるのは、(DrawingMOD ではなく)コントロール自身です。



> PaintEventArgs e = new PaintEventArgs(m_control.CreateGraphics(), m_control.ClientRectangle);
上記のコードでは、Graphics の破棄処理が抜け落ちています。
もしも CreateGraphics するのであれば、作成した Graphics は、
呼び出し元が責任を持って破棄しなくてはいけません。

また、そもそも Paint イベントとは、スクロールした場合などに応じて、
OS が「再描画が必要になった」事を知らせるために発生させるものです。
プログラムから発生させたいのであれば、Invalidate/Update/Refresh 等を
使うべきであって、外部から CreateGraphics して呼び出すような物ではありません。
http://dobon.net/vb/dotnet/control/refreshupdateinvalidate.html

引用返信 編集キー/
■14700 / inTopicNo.23)  Re[20]: パネル内の座標値、そして座標値の範囲を設定す
□投稿者/ 引っ込んだ(略) (8回)-(2008/02/25(Mon) 18:10:05)
お久しぶりです、引っ込んだ(略)と申します。

 私の環境で2通りの方法をテストしてみました。
  ・Paintイベントで描画する方法
  ・Bitmapに描画して、BackgroundImageに設定する方法
 前者のほうがスクロール時のちらつきが小さくなったので、前者の方法で書いてみました。
 (特徴)
  1.表示する点列をDrawingMODクラスのプロパティで公開
  2.Button押下時に、点列を取得し、DrawPolylineイベントを発行
  3.点列を描画するコードは、Form内に書きました。
  4.点列がない場合は、背景のみ描画します。
 以下、コードです。

using System.Collections;
using System.Drawing.Drawing2D;
    
    public class MovingPoint
    {
        public int TrackID=0;
        public float XCoordinate=0.0f;
        public float YCoordinate=0.0f;
    }
    
    public class DrawingMOD
    {

        public event PaintEventHandler DrawPolyline; //イベントフィールド
        // ↓<イベントを発生させるだけのメソッド>
        // ↓DrawingMODの派生クラスをもし作成する場合に、次の2点で有利
        // ↓ 1.派生クラスでDrawPolylineイベントを発行可能になる。
        // ↓    (念のための補足:派生クラスで基本クラスのイベントを直接発行できないので、
        // ↓     OnDrawPolylineメソッドを呼び出す方法を使わざるを得ない)
        // ↓ 2.派生クラスでオーバーライドすると、
        // ↓    基本クラス内のコードがOnDrawPolylineメソッドを呼び出した場合の振る舞いを変更可能
        // ↓    (既定ではDrawPolylineイベントが発行されるが、これを発行されないように変更する等)。
        protected virtual void OnDrawPolyline(PaintEventArgs e)
        {
            this.DrawPolyline(this, e);
        }
                
        private Control m_control;

        private ArrayList m_movingpoints=new ArrayList();
        // ↓ 点列をプロパティで公開してみました
        public ArrayList MovingPoints
        { 
            get { return this.m_movingpoints; }
            set { this.m_movingpoints = value; } // ← とりあえずsetアクセサも書いておきました。
        }

        
        //コンストラクタ
        public DrawingMOD(Control c)
        {
            this.m_control = c;
            this.m_control.Paint += new PaintEventHandler(this.m_control_Paint);
        }

        //パネルに折れ線を描画するメソッド
        //    ↓
        //パネルに描画する折れ線情報を取得するメソッド、に意味合いを変更
        public void DrawMovingObjects()
        {
            //変数を定義
            int tID = 0;
            float xCo = 200.01f;
            float yCo = 200.01f;

            this.MovingPoints.Clear();

            for (int i = 0; i < 3; i++)
            {
                //ID, x座標,y座標を格納するMovingPointクラスのオブジェクト
                MovingPoint mPoint = new MovingPoint();

                //ID, x座標,y座標をmPointに入れる
                mPoint.TrackID = tID;
                mPoint.XCoordinate = xCo;
                mPoint.YCoordinate = yCo;

                //mPointをArrayListであるmPointsに格納する
                this.MovingPoints.Add(mPoint);

                //ID, x座標,y座標の値を変える
                tID++;
                xCo += 70;
                yCo += 50;
            }
            //再描画を試みる → m_controlのPaintイベントが発行される
            // → private void m_control_Paintが呼ばれる → DrawPolylineイベントを発行
            this.m_control.Invalidate();
        }
        private void m_control_Paint(object sender, PaintEventArgs e)
        {
            // ↓DrawPolylineイベントハンドラのsenderを、DrawingMODインスタンス自身に変更しました。
            this.OnDrawPolyline(e);
        }
    }
    
    //【Form1のコード】
    public partial class Form1 : Form
    {
        DrawingMOD myDraw; //Form1で共通に使用するオブジェクト変数の宣言

        Panel panel1 = new Panel();
        Panel panel2 = new Panel();
        Button Button1 = new Button();
        
        public Form1()
        {
            InitializeComponent();

            //Button1のプロパティ
            Button1.Text = "Click!!";
            Button1.Click += new EventHandler(this.button1_Click);
            //panel1、panel2のプロパティ
            panel1.AutoScroll = true;
            panel1.Dock = DockStyle.Fill;
            panel2.AutoSize = true;
            panel2.Size = new Size(1000, 1000);
            panel2.Location = Point.Empty;
                    
            panel1.Controls.Add(panel2);
            this.Controls.Add(panel1);
            this.Controls.Add(Button1);
            Button1.BringToFront();

            this.Size = new Size(400, 400);

            this.myDraw = new DrawingMOD(this.panel2);
            //イベントとイベントハンドラを関連づける
            myDraw.DrawPolyline+= new PaintEventHandler(myDraw_DrawPanel);
            
        }

        private void button1_Click(object sender, EventArgs e)
        {
           //座標値を得て、描画をさせるメソッドを呼び出す
            this.myDraw.DrawMovingObjects();
        }

        //myDraw_DrawPanelイベントハンドラ
        private void myDraw_DrawPanel(object sender, PaintEventArgs e)
        {
            //背景色を設定
            e.Graphics.Clear(Color.Black);

            DrawingMOD dm = sender as DrawingMOD;
            int cnt = dm.MovingPoints.Count;
            // 点データがない場合は何もしない。
            if (cnt <= 0) { return; }

            //ペンの色を定義
            int cRed = 200;
            int cGreen = 100;
            int cBlue = 100;

            //描画をするためのPointFの配列を用意
            PointF[] myPoints = new PointF[cnt];

            foreach (MovingPoint m in dm.MovingPoints)
            {
                //myPointsにmPointsの座標を入れる
                myPoints[m.TrackID] = new PointF(m.XCoordinate, m.YCoordinate);
            }

            //ペンを作成
            using (Pen pen = new Pen(Color.FromArgb(cRed, cGreen, cBlue), 4))
            {
                //折れ線を矢印にする
                pen.EndCap = LineCap.ArrowAnchor;
                //折れ線を描く
                e.Graphics.DrawLines(pen, myPoints);
            }
        }

    }

以上です。

引用返信 編集キー/
■14892 / inTopicNo.24)  Re[21]: パネル内の座標値、そして座標値の範囲を設定す
□投稿者/ Tetsu (17回)-(2008/02/28(Thu) 14:26:19)
Tetsu さんの Web サイト
魔界の仮面弁士さん、引っ込んだ(略)さん

回答ありがとうございました。
お二人の回答をもとに自分のコードにあてはめてみましたところ、
無事にパネル上で再描画ができるようになりました。

今回、長いやり取りになってしまいましたが、一つのことをやるためにいろいろな方法で
実現することが可能なのだということを勉強することができました。

まだC#を始めて間もなく、学校で授業を取ったわけでも、トレーニングを受けたわけでもなく、
一人で手探り状態なので、皆さんには大変お世話になってしまっています。

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


解決済み
引用返信 編集キー/

<前の20件
トピック内ページ移動 / << 0 | 1 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -