午後わてんのブログ

ベランダ菜園とWindows用アプリ作成(WPFとC#)

マウスクリックでCanvasに直線を描画、Line、Path、WPFとC#

今回のアプリのダウンロード先
System.Windows.Shapes.Line
マウスクリックで直線(Line)を描画
3年前にWindowsFormとVBだったのをWPFC#で試してみた
 
イメージ 1
クリックしたところを始点(X1,Y1)にして、カーソル位置を終点(X2,Y2)にしている
 
 
デザイン画面

f:id:gogowaten:20191212165334p:plain

C#のコード
using System.Windows;
using System.Windows.Input;

namespace _20180605_クリックで直線ShapeLine
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
     
            MyCanvas.MouseLeftButtonDown += MyCanvas_MouseLeftButtonDown;
            MyCanvas.MouseMove += MyCanvas_MouseMove;
        }

        private void MyCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            Point p = e.GetPosition(MyCanvas);
            MyLine.X2 = p.X;
            MyLine.Y2 = p.Y;     
        }

        private void MyCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point p = e.GetPosition(MyCanvas);          
            MyLine.X1 = p.X;
            MyLine.Y1 = p.Y;            
        }
        
    }
}

 
 
 
 
イメージ 3
Lineが残るようにしてみただけ
 
デザイン画面

f:id:gogowaten:20191212165354p:plain

 
C#のコード
using System.Windows;
using System.Windows.Input;

namespace _20180605_クリックで直線ShapeLine2
{
    public partial class MainWindow : Window
    {
        bool IsDraw;

        public MainWindow()
        {
            InitializeComponent();
            MyCanvas.MouseLeftButtonDown += MyCanvas_MouseLeftButtonDown;
            MyCanvas.MouseMove += MyCanvas_MouseMove;
        }

        private void MyCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (IsDraw == true)
            {
                Point p = e.GetPosition(MyCanvas);
                MyLine.X2 = p.X;
                MyLine.Y2 = p.Y;
            }
        }

        private void MyCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point p = e.GetPosition(MyCanvas);

            if (IsDraw == true)
            {
                IsDraw = false;
                MyLine.X2 = p.X;
                MyLine.Y2 = p.Y;
            }
            else
            {
                IsDraw = true;
                MyLine.X1 = p.X;
                MyLine.Y1 = p.Y;
            }
        }
    }
}
 
 
System.Windows.Shapes.Path
Pathを使ってクリックした場所を直線で繋いで描画
イメージ 5
Lineは1本の直線の図形なので複数の直線は繋げられないようだったので
Pathを使ってみた
 
 
 

f:id:gogowaten:20191212165415p:plain

 
C#コード、書き直す前
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace _20180605_クリックで直線PathGeometry
{

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
                        
            MyCanvas.MouseMove += MyCanvas_MouseMove;
            MyCanvas.MouseLeftButtonDown += MyCanvas_MouseLeftButtonDown;
            MyCanvas.MouseRightButtonDown += MyCanvas_MouseRightButtonDown;
        }

        private void MyCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            iPath.Data = null;//PathDataの初期化
        }

        private void MyCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point p = e.GetPosition(MyCanvas);
            if (iPath.Data == null)
            {
                MyPathStartPoint(p);//最初のクリック時
            }
            else
            {
                //クリックした座標をLineSegmentとして追加する
                var pg = (PathGeometry)iPath.Data;
                PathFigure pf = pg.Figures[0];
                pf.Segments.Add(new LineSegment(p, true));
            }
        }

        //マウス移動時は最後にクリックした点から伸びる線を描画する
        private void MyCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (iPath.Data != null)
            {
                Point p = e.GetPosition(MyCanvas);//マウスカーソル位置

                var pg = (PathGeometry)iPath.Data;              
                PathSegmentCollection psc = pg.Figures[0].Segments;
                PathSegment pathSegment = psc[psc.Count - 1];//終端
                //SetValueで終端座標を指定(マウスカーソルの位置)
                pathSegment.SetValue(LineSegment.PointProperty, p);
            }
        }
            
        //最初のクリック時にPathのDataになるPathGeometryを作成
        //開始点と次点を指定する
        private void MyPathStartPoint(Point p)
        {
            PathFigure pathFigure = new PathFigure();
            pathFigure.StartPoint = p;//開始点
            //次点をLineSegmentで作成してSegmentsに追加
            pathFigure.Segments.Add(new LineSegment(p, true));
                        
            PathFigureCollection pathFigureCollection = new PathFigureCollection();
            pathFigureCollection.Add(pathFigure);
                        
            PathGeometry pathGeometry = new PathGeometry();
            pathGeometry.Figures = pathFigureCollection;
            //
            iPath.Data = pathGeometry;
        }
                
    }
}
 
PathのDataプロパティにPathGeometryを指定
クリックした座標を追加していくところはPathSegmentCollectionで
Pathからたどっていくと
 
Path.Data
	PathGeometry.Figures
		PathFigureCollection
			PathFigure.Segments
				PathSegmentCollection
					LineSegment
 
LineSegmentのPointプロパティにx,y座標を指定してPathSegmentCollectionに追加していく

f:id:gogowaten:20191212165434p:plain

遠いなあ
 
イメージ 8
最初のクリック時にPathのDataになるPathGeometryを作成
今見てて思ったのがPathSegmentCollectionは特に作成していないけど、73行目でAddでいいんだなあと、ってことは79行目もFiguresにPathFigureCollectionを指定しているけど、これも直接Addで追加できるのかなと思って
 
イメージ 9
こうしてみたら、これでも動いた
このほうがラクだわ
 
 
左クリック時はクリックした座標を追加
イメージ 10
PathGeometryの最初([0])のFiguresのSegmentにクリック座標のLineSegmentを追加
 
 
 
マウス移動時は最後にクリックした点から伸びる直線を描画
イメージ 11
59行目でLineSegmentが入っているPathSegmentCollectionを取得
これが

f:id:gogowaten:20191212165455p:plain

この赤字のところで
これの最後にあるLineSegmentの座標を今のマウスカーソルの座標にするので
最後のインデックスはCount-1
これでPathSegmentを取得して、60行目
62行目で座標を指定している、指定するのにSetValueとか分かりづらいのを使っている
今思ったのが、60行目でPathSegmentじゃなくてLineSegmentにキャストして取得すればいいのでは、ってことで書き直して
 
イメージ 13
これでOK、座標指定も普通にPointで指定できた!
 
 
右クリック時はPath.Dataの消去
イメージ 14
 
C#コード、書き直し後
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;


namespace _20180605_クリックで直線PathGeometry
{

    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
                        
            MyCanvas.MouseMove += MyCanvas_MouseMove;
            MyCanvas.MouseLeftButtonDown += MyCanvas_MouseLeftButtonDown;
            MyCanvas.MouseRightButtonDown += MyCanvas_MouseRightButtonDown;
        }

        private void MyCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
        {
            iPath.Data = null;//PathDataの初期化
        }

        private void MyCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            Point p = e.GetPosition(MyCanvas);
            if (iPath.Data == null)
            {
                MyPathStartPoint(p);//最初のクリック時
            }
            else
            {
                //クリックした座標をLineSegmentとして追加する
                var pg = (PathGeometry)iPath.Data;
                PathFigure pf = pg.Figures[0];
                pf.Segments.Add(new LineSegment(p, true));
            }
        }

        //マウス移動時は最後にクリックした点から伸びる線を描画する
        private void MyCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            if (iPath.Data != null)
            {
                Point p = e.GetPosition(MyCanvas);//マウスカーソル位置

                var pg = (PathGeometry)iPath.Data;              
                PathSegmentCollection psc = pg.Figures[0].Segments;
                LineSegment lineSegment = (LineSegment)psc[psc.Count - 1];//終端
                //終端座標を指定(マウスカーソルの位置)
                lineSegment.Point = p;
            }
        }
            
        //最初のクリック時にPathのDataになるPathGeometryを作成
        //開始点と次点を指定する
        private void MyPathStartPoint(Point p)
        {
            PathFigure pathFigure = new PathFigure();
            pathFigure.StartPoint = p;//開始点
            //次点をLineSegmentで作成してSegmentsに追加
            pathFigure.Segments.Add(new LineSegment(p, true));
                        
            PathGeometry pathGeometry = new PathGeometry();
            pathGeometry.Figures.Add(pathFigure);
            //
            iPath.Data = pathGeometry;
        }
                
    }
}
 

f:id:gogowaten:20191212165509p:plain

 
コード全部
 
 
WindowsFormと比べるとWPFは直線一つなら同じ感じだけど、複数の直線を繋げた描画は少しややこしいかなあ、もっとラクな方法ないかしら
 

2018/06/08追記
もっとラクな方法ありました
マウスクリックでCanvasに直線を描画その2、Polyline、WPFC# ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/15540488.html
2018/06/08追記ここまで

 
 
 
 
参照したところ
 
 
 
 
関連記事
2015/1/21は3年前
マウスクリックでPictureBoxに直線を描くテスト、VisualBasic2013 ( ソフトウェア ) - 午後わてんのブログ - Yahoo!ブログ
https://blogs.yahoo.co.jp/gogowaten/12634284.html