

現在、Windowsフォームアプリケーションを開発しています。  
コード記述のプログラミング言語はC#です。  
今の時点で実現できていることは直線の始点を左クリックして、もう一度左クリックすることで終点を決め、その二点を結ぶ直線を描くことはできています。  
DrawLineメソッドです。  
pictureBox内に表示させています。

実装したいこと  
直線を一つ描画はできるのですが、線と線をつないで図形を描きたい場合など複数描ける必要があります。  
しかし、同記述していけばいいかわからなかったので、どなたか助けていただけないでしょうか？  
以下がソースコードです。

[code]

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Diagnostics;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Windows.Forms;
    using static System.Console;
    
    public partial class Form1 : Form
    {
    
        ///
        /// 各変数の定義など
        ///
    
        //描画する線の種類のボタンがクリックされた場合の機能を実装するためのスイッチON/OFFを観察するための変数
        private bool btnSolidWasClicked = false;
    
        //左クリックで始点(lsp)と終点(lep)を選択するために変数定義
        //MouseButtons.Left
        private Point lsp = new Point();
        private Point lep = new Point();
        //左クリックされたときにtrueかfalseかどうかで始点か終点か判断
        private Boolean lsp_flag;
    
        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (btnSolidWasClicked)
            {
                //描画先とするImageオブジェクトを作成
                Bitmap canvas = new Bitmap(pictureBox1.Width, pictureBox1.Height);
                //ImageオブジェクトのGraphicsオブジェクトを作成
                Graphics g = Graphics.FromImage(canvas);
                //線を滑らかにする
                g.SmoothingMode = SmoothingMode.AntiAlias;
                //Penオブジェクトの作成
                Pen GreenPen = new Pen(Color.White, 3);
    
                //直線の種類は実線
                GreenPen.DashStyle = DashStyle.Solid;
    
                //座標の表示
                label1.Text = string.Format("Screen Position : {0:d}, {1:d}",
                Cursor.Position.X, Cursor.Position.Y);
    
                if (lsp_flag) //終点の処理
                {
                    lep = e.Location;
                    g.DrawLine(GreenPen, lsp, lep);
                    //リソースを開放
                    GreenPen.Dispose();
                    g.Dispose();
                    lsp_flag = false;
                    //Picture1に表示
                    pictureBox1.Image = canvas;
                }
                else //始点の処理
                {
                    lsp = e.Location;
                    g.DrawRectangle(GreenPen, lsp.X, lsp.Y, 0.1f, 0.1f);
                    //リソースを開放
                    GreenPen.Dispose();
                    g.Dispose();
                    lsp_flag = true;
                    //Picture1に表示
                    pictureBox1.Image = canvas;
                }
            }
        }
    }
    
[/code]


CC BY-SA 4.0
2020-02-08T04:14:32.720
62865
2022-02-23T00:01:50.183
2020-02-08T08:47:09.527
26370
31173
question
c# visual-studio
C# Windowsフォームアプリケーション開発、描いた直線を残したまま、再度直線を描画したい。


マウスダウンイベントでpictureBoxに書いていると思いますが、マウスダウンイベントでは直接書かず、その座標を覚えておき、pictureBox.Refresh();を行います。  
そうしますとPaintイベントが発生しますので、この中で覚えておいた座標により描画をします。


CC BY-SA 4.0
2020-02-08T04:44:14.387
62866
2020-02-08T04:44:14.387
24490
62865
answer


### 単純な回答

質問のコードではクリックするたびに下記のコードで **真っ白な画像** を作って、その上に線を描画しているので複数の直線が書けません。  
`Bitmap canvas = new Bitmap(pictureBox1.Width, pictureBox1.Height);`

描画済みの線を消さずにpictureBox1を上書きすれば解決します。  
`pictureBox1_MouseDown`をサンプル1のように書き換えてください。  
ただし終点を書くごとに`lsp_flag = false;`で終点フラグをリセットすると、連続した線分になりません。  
サンプル1のtodoでは左クリックで線を連続させて右クリックで終点としています。

サンプル1

[code]

        private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
        {
            if (btnSolidWasClicked)
            {
                //描画先とするGraphicsオブジェクトを作成
                using (Graphics g = pictureBox1.CreateGraphics())
                //Penオブジェクトの作成
                using (Pen GreenPen = new Pen(Color.White, 3))
                {
                    //線を滑らかにする
                    g.SmoothingMode = SmoothingMode.AntiAlias;
    
                    //直線の種類は実線
                    GreenPen.DashStyle = DashStyle.Solid;
    
                    //座標の表示
                    label1.Text = string.Format("Screen Position : {0:d}, {1:d}",
                    Cursor.Position.X, Cursor.Position.Y);
    
                    if (lsp_flag) //終点の処理
                    {
                        lep = e.Location;
                        g.DrawLine(GreenPen, lsp, lep);
                        lsp = lep;
                        //todo 終点かどうかの判断は別途行ってください
                        //↓右クリックの時に終点と判断する例
                        if (e.Button == MouseButtons.Right)
                        {
                            lsp_flag = false;
                        }
                    }
                    else //始点の処理
                    {
                        lsp = e.Location;
                        g.DrawRectangle(GreenPen, lsp.X, lsp.Y, 0.1f, 0.1f);
                        lsp_flag = true;
                    }
                }
            }
        }
    
[/code]

### 応用可能な回答

単純な回答では「線を1本消すUndo処理」や「後で線の色を変える」などの処理ができません。  
ペイントソフトとしての機能強化を見込んでいる場合は、クリックした座標ごとに描画する線の種類、太さ、色を保持するクラスを作ってリストで管理すると良いでしょう。  
サンプル2の`DrawInfo`クラスがその例になります。  
試す場合は`pictureBox1`の`Paint`イベントを`PictureBox1_Paint`に割り当ててください。

サンプル2

[code]

    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.Drawing.Drawing2D;
    using System.Windows.Forms;
    
    namespace WindowsFormsApp1
    {
        public partial class Form1 : Form
        {
            ///
            /// 各変数の定義など
            ///
    
            //描画位置を管理するリスト
            private List<DrawInfo> DrawList = new List<DrawInfo>();
            //描画する線の種類のボタンがクリックされた場合の機能を実装するためのスイッチON/OFFを観察するための変数
            private bool btnSolidWasClicked = false;
            //左クリックされたときにtrueかfalseかどうかで始点か終点か判断
            private Boolean lsp_flag;
    
            public Form1()
            {
                InitializeComponent();
            }
    
            private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
            {
                DrawList.Add(new DrawInfo(e.Location, btnSolidWasClicked, lsp_flag));
                pictureBox1.Refresh();
                lsp_flag = (e.Button == MouseButtons.Right);
            }
    
            private void PictureBox1_Paint(object sender, PaintEventArgs e)
            {
                if (DrawList.Count == 0) return;
                using (var canvas = new Bitmap(pictureBox1.Width, pictureBox1.Height))
                using (Graphics g = Graphics.FromImage(canvas))
                {
                    g.SmoothingMode = SmoothingMode.AntiAlias;
                    DrawInfo prev = null;
                    foreach (var info in DrawList)
                    {
                        info.Draw(g, prev);
                        prev = info;
                    }
                    //canvasをImageにセットするとDisposeした時点で例外が発生するのでコピーを作成
                    pictureBox1.Image = (Bitmap)canvas.Clone();
                }
            }
        }
    
        public class DrawInfo
        {
            public Point Location { get; set; }
            public Pen Pen { get { return new Pen(PenColor, PenWidth); } }
            public Color PenColor { get; set; }
            public float PenWidth { get; set; }
            public DashStyle Style { get; set; }
            /// <summary>
            /// 線分開始フラグ
            /// </summary>
            public bool IsStart { get; set; }
    
            protected DrawInfo()
            {
                PenColor = Color.White;
                PenWidth = 3f;
                Style = DashStyle.Dot;
            }
    
            public DrawInfo(Point location, bool isSolid, bool isStart)
                : this()
            {
                Location = location;
                Style = isSolid ? DashStyle.Solid : DashStyle.Dash;
                IsStart = isStart;
            }
    
            /// <summary>
            /// 線分を描画する。前の点がない場合は始点として点を描画する
            /// </summary>
            /// <param name="g"></param>
            /// <param name="prev"></param>
            public void Draw(Graphics g, DrawInfo prev)
            {
                if (prev == null)
                {
                    IsStart = true;
                }
                using (var pen = Pen)
                {
                    if (IsStart)
                    {
                        //始点の処理
                        var r = (int)(PenWidth / 2);
                        var rect = new Rectangle
                        {
                            X = Location.X - r,
                            Y = Location.Y - r,
                            Width = r,
                            Height = r,
                        };
                        g.DrawEllipse(pen, rect);
                    }
                    else
                    {
                        //線分の処理
                        g.DrawLine(pen, prev.Location, Location);
                    }
                }
            }
        }
    
    }
    
[/code]


CC BY-SA 4.0
2020-02-10T01:24:20.097
62912
2020-02-10T01:24:20.097
9820
62865
answer


Imageオブジェクトを使わずにGraphicsオブジェクトを採用しました。また、線の描画の後、始点の更新など追加したことで無事複数の線を描画することができました。  
下記が修正後のコードを一部抜粋したものです。  
ご回答してくださった方々ありがとうございました。

[code]

    private void pictureBox1_MouseDown(object sender, MouseEventArgs e)
    {
        if (btnSolidWasClicked)
        {
            //Graphicsオブジェクトを作成
            Graphics g = this.pictureBox1.CreateGraphics();
    
            g.SmoothingMode = SmoothingMode.AntiAlias;
            //Penオブジェクトの作成
            Pen GreenPen = new Pen(Color.White, 3);
            //実線
            GreenPen.DashStyle = DashStyle.Solid;
            //座標の表示
            label1.Text = string.Format("Screen Position : {0:d}, {1:d}",
                Cursor.Position.X, Cursor.Position.Y);
    
            if (lsp_flag)
            {
                lep = e.Location;
                g.DrawLine(GreenPen, lsp, lep);
                lsp = lep;
                g.Dispose();
                GreenPen.Dispose();
            } else
            {
                lsp = e.Location;
                g.DrawRectangle(GreenPen, lsp.X, lsp.Y, 0.1f, 0.1f);
                g.Dispose();
                GreenPen.Dispose();
                lsp_flag = true;
            }
    
            //右クリックで終点判断
            if (e.Button == MouseButtons.Right)
            {
                lsp_flag = false;
            }
        }
    }
    
[/code]


CC BY-SA 4.0
2020-02-11T10:16:31.177
62960
2020-02-11T14:06:22.883
2020-02-11T14:06:22.883
3060
31173
62865
answer