[C#/xaml] System.Windows.Pointのリストを使って線を引く

もくじ
⇒https://qiita.com/tera1707/items/4fda73d86eded283ec4f

# やりたいこと

`System.Windows.Point`クラスと、`System.Windows.Media.PointCollection`クラスを使って、xamlで作った画面に線を引きたい。
※`System.Drawing.Point`クラスではなく`System.Windows.Point`クラス。

# 流れ

- 準備
  - `System.Windows.Point`を持たせるクラスに(以下、Pointクラスと言う)  
    `InotifyPropertyChanged`を実装する
  - `Point`を`PointCollection`に変換してくれるコンバータをつくる
- 線を書く
  - `System.Windows.Point`のObservableCollectionをつくる
  - 好きなタイミングで、線を引きたい座標を入れた`Point`をObservableCollectionにAddする
  - OnPropertyChanged(PropertyChanged)を実行する
  - →PointのObservableCollectionにあった線が描画される

# 前提

.NET core 3.1で実験。

# サンプル

### 画面

```MainWindow.xaml
<Window x:Class="WpfApp57.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WpfApp57"
        xmlns:conv="clr-namespace:Converter"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        Name="root">
    <Window.Resources>
        <conv:ListPointToPointCollectionConverter x:Key="ListPointToPointCollectionConverter" />
    </Window.Resources>
    <Grid>
        <Canvas>
            <!-- 線 -->
            <Polyline Name="MyLine" Stroke="Red" StrokeThickness="1" 
                      Points="{Binding Points, ElementName=root, Converter={StaticResource ListPointToPointCollectionConverter}}"/>
            <!-- ボタン -->
            <Button Content="Button" Height="100" Canvas.Left="10" Canvas.Top="324" Width="100" Click="Button_Click"/>
        </Canvas>
    </Grid>
</Window>
```

```cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Threading.Tasks;
using System.Windows;
using System.Collections.ObjectModel;

namespace WpfApp57
{
    /// <summary>
    /// ボタンをおしたら、サインカーブを書く
    /// </summary>
    public partial class MainWindow : Window, INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        protected void OnPropertyChanged(string name = null)
        {
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(name));
        }

        // ------------------------------------------------

        public ObservableCollection<Point> Points
        {
            get { return _points; }
            set { _points = value; OnPropertyChanged(nameof(Points)); }
        }
        private ObservableCollection<Point> _points = new ObservableCollection<Point>();

        public MainWindow()
        {
            InitializeComponent();

            // Point追加時に自動でOnPropertyChangedしてくれるようにする
            Points.CollectionChanged += ((sender, e) =>
            {
                var oc = sender as ObservableCollection<Point>;
                OnPropertyChanged(nameof(Points));
                Debug.WriteLine("count = " + oc.Count);
            });
        }

        private double y = 150.0;

        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var _ = Task.Run(() =>
            {
                this.Dispatcher.Invoke(new Action(async () =>
                {
                    for (int i = 0; i < 100; i++)
                    {
                        Points.Add(new Point((double)i, y + 150.0 * Math.Sin((double)i / (Math.PI))));
                        OnPropertyChanged(nameof(Points));
                        await Task.Delay(30);
                    }
                }));
            });
        }
    }
}
```

### コンバータ

```cs
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
using System.Windows.Media;
using System.Collections.ObjectModel;

namespace Converter
{
    public class ListPointToPointCollectionConverter : IValueConverter
    {
        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
        {
            var points = (ObservableCollection<Point>)value;
            return points != null ? new PointCollection(points) : null;
        }

        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
        {
            throw new NotImplementedException();
        }
    }
}
```

# 実行イメージ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/7d38081f-6486-9345-a311-30a9411db5df.png)


# 備考
試してないが、コンバータを使わなくても、コード内でPointをPointCollectionにしてもよいと思う。


# コード
https://github.com/tera1707/WPF-/tree/master/038_DrawLines