[xaml/WPF] フリックやピンチインアウトでControlを移動・拡大縮小する(ManipulationDeltaイベントとMatrixTransform)

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

**回転/拡大縮小/移動の関連記事**

- [画面の要素を回転/拡大縮小/移動する（RenderTransform）](https://qiita.com/tera1707/items/b6a1803a64f34827f741)
- [画面の要素を回転/拡大縮小/移動する（RenderTransformにMatrixTransform）](https://qiita.com/tera1707/items/5d02ca57f692283a0f9a)
- [フリックやピンチインアウトでControlを移動・拡大縮小する(ManipulationDeltaイベントとMatrixTransform)](https://qiita.com/tera1707/items/dff76c2b870fe5c63e9d)
- [[xaml/C#] 枠の中で画像などを拡大縮小する（マウスと指で移動/拡大）](https://qiita.com/tera1707/items/37af056540f23e73213f)

# やりたいこと
タッチ対応のディスプレイを使用しているときに、画面上の画像をフリック・ピンチインアウトで移動・拡大縮小させたい。

# やり方
`ManipulationDelta`イベントを拾って、指の動いた位置と量をとり、それをMatrixクラスにセットして色々やることで実現する。

# 手順

- xamlで、移動や拡大をしたいコントロールのManipulationDeltaイベントハンドラをセットする
- 移動や拡大をしたいコントロールのIsManipulationEnabledプロパティをTrueにする
- コードビハインドのManipulationDeltaイベントハンドラで、引数から指の移動量・中心点情報をとって、Matrixクラスの移動・拡大のためのメソッドに渡す
- そのMatrixをMatrixTransformに渡して、さらにMatrixTransformを移動や拡大をしたいコントロールのRenderTransformにセットする。

具体例は、下のサンプルを参照。

# ManipulationDeltaについて

### とれるもの

イベントハンドラの引数として渡されてくる`ManipulationDeltaEventArgs e`に、下記のような情報が載ってくる。

|取れる情報|取る引数|
|:--|:--|
|指の移動量(左右)|e.DeltaManipulation.X|
|指の移動量(上下)|e.DeltaManipulation.Y|
|2本指の中心点(左右)|e.ManipulationOrigin.X|
|2本指の中心点(上下)|e.ManipulationOrigin.Y|
|拡大率(左右)|e.ManipulationOrigin.Scale.X|
|拡大率(上下)|e.ManipulationOrigin.Scale.Y|


上記のような値が取れる。で、

- 移動を行うためのMatrixクラスのメソッド`Translate()`は引数に移動する量をとる
  - ⇒移動する量に、`指の移動量`をセットする。
- 移動を行うためのMatrixクラスのメソッド`ScaleAt()`は引数に`上下方向の拡大率`、`左右方向の拡大率`と、`上下方向の拡大中心点`、`左右方向の拡大中心点`をとる
  - ⇒上下と左右の拡大中心点に、`2本指の中心点`をセットする
  - ⇒上下と左右の拡大率に、`拡大率`をセットする。

ということをすることで、指が移動した分だけ移動と拡大縮小ができる。

# サンプル

```MainWIndow.xaml
<Window x:Class="WpfApp39.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:WpfApp39"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <Grid ShowGridLines="True">
            <Grid.ColumnDefinitions>
                <ColumnDefinition/><ColumnDefinition/><ColumnDefinition/>
            </Grid.ColumnDefinitions>
            <Grid.RowDefinitions>
                <RowDefinition/><RowDefinition/><RowDefinition/>
            </Grid.RowDefinitions>

            <!-- このGridを回転させる -->
            <Grid x:Name="MyGrid" Grid.Row="1" Grid.Column="1" Background="Pink" RenderTransformOrigin="0.5,0.5"
                  IsManipulationEnabled="True"
                  ManipulationDelta="Grid_ManipulationDelta">
                
                <TextBlock Text="あ" HorizontalAlignment="Center" VerticalAlignment="Center" FontSize="50"/>
            </Grid>
        </Grid>
    </Grid>
</Window>

```


```MainWIndow.xaml.cs
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace WpfApp39
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void Grid_ManipulationDelta(object sender, ManipulationDeltaEventArgs e)
        {
            var delta = e.DeltaManipulation;
            Matrix matrix = (MyGrid.RenderTransform as MatrixTransform).Matrix;
            matrix.Translate(delta.Translation.X, delta.Translation.Y);// 指の移動量を指定して対象を移動

            var scaleDelta = delta.Scale.X;//上下と左右に同じ量拡大するときは、Xの拡大率だけとればOK
            var orgX = e.ManipulationOrigin.X;//指の中心点(X)
            var orgY = e.ManipulationOrigin.Y;//指の中心点(Y)
            matrix.ScaleAt(scaleDelta, scaleDelta, orgX, orgY);//中心を指定して対象を拡大
            MyGrid.RenderTransform = new MatrixTransform(matrix);
        }
    }
}

```

# 注意点

- `ManipulationDelta`イベントのハンドラをセットしたときは、`IsManipulationEnabled`を"True"にすることを忘れない。(Trueにしないと、イベントがこない)
- 移動や拡大をするコントロールに、`HorizontalAlignment="Center"`や`RenderTransformOrigin="0.5,0.5"`などをつけると、中心点がずれて、ピンチインアウトしたときの拡大縮小の中心がずれてしまうっぽい。(matrixが、左上を基準にしている？？？)
- WidthやHeightを指定しても、中心がずれるっぽい。(matrixが、素？の大きさを基準にしている？？？)

# コード
https://github.com/tera1707/WPF-/tree/master/032_ScaleTranslateByMatrix

# 参考
タッチイベントを処理しよう（かずきさんブログ）
https://blog.okazuki.jp/entry/20101212/1292167120

Matrix 構造体
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.media.matrix?view=netframework-4.7.2

ManipulationDelta クラス
https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.input.manipulationdelta?view=netframework-4.7.2