Win2Dについて　その2　ドラッグで描画

# はじめに
前回のWin2Dを使用してプログラムから描画する方法を解説しました。
今回はペイントソフトで欠かせないマウス操作からの図形描画について解説したいと思います。
例としてマウスドラッグで四角形を描画する方法について解説していきます。

# UIコントロール
前回の追加したコントロールにマウス操作のイベントを追加していきます。
追加するのは以下のイベントです。
`PointerPressed`・・・マウスをコントロール内でクリックしたときに発火
`PointerMoved`・・・マウスをコントロール内で動かしたときに発火
`PointerReleased`・・・マウスクリックをコントロール内で離したときに発火
このイベントを追加することで、マウスをドラッグしたときの処理を加えることができます。
```diff_xml
<canvas:CanvasControl
    x:Name="canvas"
    CreateResources="Canvas_CreateResources"
    Draw="Canvas_Draw"
+   PointerMoved="Canvas_PointerMoved"
+   PointerPressed="Canvas_PointerPressed"
+   PointerReleased="Canvas_PointerReleased"
    />
```

# ドラッグ処理用クラス作成
ドラッグで描画していくための、最初の位置、現在の位置を保持するための`PointerDrag`クラスを作成します。
```cs
using Windows.Foundation;

public class PointerDrag
{
    //データを保持するフィールドを作る
    public Point StartLocation {  get; set; }
    public Point CurrentLocation { get; set; }

    //コンストラクタで現在のマウスの位置を与え初期化する
    public PointerDrag(Point location)
    {
        StartLocation = location;
        CurrentLocation = location;
    }
}
```
WinUI3ではマウスの位置が`Windows.Foundation.Point`構造体によって取得されるので、フィールドも`Point`型を指定してます。

# マウスクリックイベント
マウスをクリックしたときの処理を追加していきます。
`MainWindow.xaml.cs`に戻り、先ほど作成したマウスの位置を保持するクラスをフィールドとして追加します。
また、マウスをドラッグしているかを示す変数も追加します。
```cs
public sealed partial class MainWindow : Window
{
    PointerDrag pointerDrag;
    bool mouseDown = false;
}
```
マウスクリックのイベントを作成します。
```cs
private void Canvas_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
    mouseDown = true;
    pointerDrag = new PointerDrag(e.GetCurrentPoint(canvas).Position);
    canvas.Invalidate();
}
```
マウスが押されたことをmouseDownに保持します。
ドラッグにおいてのマウスクリックは最初に現在のマウスの場所を取得すればよいだけです。
`e.GetCurrentPoint(canvas).Position`でイベントが発火したときのコントロール上のマウスの位置を取得しています。
`canvas.Invalidate()`はコントロールの描画処理を更新するための関数です。
<font color="Red">※`canvas`はコントロールにつけた`x:Name`によって異なる</font>

# マウスムーブイベント
コントロール上でマウスが動いているときに発火します。
```cs
private void Canvas_PointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
    // マウスがクリックされていないときはドラッグではないので処理を無視する
    if (mouseDown == false)
        return; 
    pointerDrag.CurrentLocation = e.GetCurrentPoint(canvas).Position;
    canvas.Invalidate();
}
```
`CurrentLocation`に現在の位置を入れます

# マウスリリースイベント
マウスを離したときに発火します。
```cs
private void Canvas_PointerReleased(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
{
    pointerDrag.CurrentLocation = e.GetCurrentPoint(canvas).Position;
    mouseDown = false;
    canvas.Invalidate();
}
```
マウスムーブと同様に離した位置を現在の位置として保持します。
マウスが離されたので`mouseDown`を`false`にしています。

# 描画
その1と同様にDrawイベントで描画処理をします。
四角形のはじめと終わりの点については`pointerDrag`が保持しているのでそれを使い、Rectとして代入しています。
```cs
private void Canvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
{
    var ds = args.DrawingSession;
    ds.DrawRectangle(new Rect(pointerDrag.StartLocation, pointerDrag.CurrentLocation), Color.FromArgb(255, 0, 0, 0));
}
```

これでコントロール上でドラッグすると四角が描けると思います。
`MainWindow.xaml.cs`の全体は以下
```cs
public sealed partial class MainWindow : Window
{
    PointerDrag pointerDrag;
    bool mouseDown = false;

    public MainWindow()
    {
        this.InitializeComponent();
    }

    private void Canvas_PointerPressed(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
    {
        mouseDown = true;
        pointerDrag = new PointerDrag(e.GetCurrentPoint(canvas).Position);
        canvas.Invalidate();
    }

    private void Canvas_PointerMoved(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
    {
        // マウスがクリックされていないときはドラッグではないので処理を無視する
        if (mouseDown == false)
            return; 
        pointerDrag.CurrentLocation = e.GetCurrentPoint(canvas).Position;
        canvas.Invalidate();
    }
    
    private void Canvas_PointerReleased(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
    {
        pointerDrag.CurrentLocation = e.GetCurrentPoint(canvas).Position;
        mouseDown = false;
        canvas.Invalidate();
    }

    private void Canvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
    {
        var ds = args.DrawingSession;
        ds.DrawRectangle(new Rect(pointerDrag.StartLocation, pointerDrag.CurrentLocation), Color.FromArgb(255, 0, 0, 0));
    }
}
```

# 複数の描画についての対応
先ほどのコードは1つの描画には対応していますが、2個目を描画するとき1つ目に書いたものが消えてしまいます。
その時はListで`pointerDrag`を保持し、マウスリリースで`Add`、`foreach`で複数描画という形で書けばよいです。
```diff_c_sharp
+   List<PointerDrag> pointerDrags = new List<PointerDrag>();
    PointerDrag pointerDrag;
    bool mouseDown = false;

    private void Canvas_PointerReleased(object sender, Microsoft.UI.Xaml.Input.PointerRoutedEventArgs e)
    {
        pointerDrag.CurrentLocation = e.GetCurrentPoint(canvas).Position;
        mouseDown = false;
+       pointerDrags.Add(pointerDrag);
        canvas.Invalidate();
    }

    private void Canvas_Draw(CanvasControl sender, CanvasDrawEventArgs args)
    {
        var ds = args.DrawingSession;
+       foreach(var point in pointerDrags)
        {
            ds.DrawRectangle(new Rect(point.StartLocation, point.CurrentLocation), Color.FromArgb(255, 0, 0, 0));
        }
    }
```