[WPF/xaml] 画面の要素を回転/拡大縮小/移動する（RenderTransformに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)

# やること
xamlで画面に配置したものを、移動したり、拡大縮小、回転させたい。
[以前の記事](https://qiita.com/tera1707/items/b6a1803a64f34827f741)で、同じことを`RenderTransformに、Transformクラスを継承したRotateTransformやScaleTransform、TranslateTransformをセットする`というやり方で実現したが、今回は`MatrixTransform`をセットするやり方で実現してみたい。

# 経緯
Controlを拡大縮小や移動するだけなら前のやり方で全く問題なかったのだが、画像を出すアプリで、タッチパネルをフリックして画像を移動、またピンチイン/アウトして拡大縮小、をしたいときに、どうも`MatrixTransform`を使うのが常套らしい。

以前`MatrixTransform`を使うやり方がめんどくさそうで逃げてたので、この際一度`MatrixTransform`を触ってみることにした。

# やり方

1. xamlで、回転や拡大縮小したいControlにNameで名前を付ける
- 回転や拡大縮小したいところで、そのControlのRenderTransformをとしてとってくる
- とったMatrixTransformの拡大縮小用のメソッドを呼ぶ
- とったMatrixTransformを、ControlのRenderTransformにセットする

# サンプル

### xaml

``` 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>

            <!-- 動かしてみるためのボタン -->
            <Button Content="平行移動" Grid.Row="0" Grid.Column="0" Click="Button_Click"/>
            <Button Content="拡大縮小" Grid.Row="0" Grid.Column="1" Click="Button_Click_1"/>
            <Button Content="回転" Grid.Row="0" Grid.Column="2" Click="Button_Click_2"/>
            <Button Content="全部" Grid.Row="1" Grid.Column="2" Click="Button_Click_3"/>

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

```

### cs(コードビハインド)

``` MainWindow.cs
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;

namespace WpfApp39
{
    /// <summary>
    /// MainWindow.xaml の相互作用ロジック
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        // 平行移動
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            Matrix matrix = (MyGrid.RenderTransform as MatrixTransform).Matrix;
            matrix.Translate(5, 5);
            MyGrid.RenderTransform = new MatrixTransform(matrix);
        }

        // 拡大縮小
        private void Button_Click_1(object sender, RoutedEventArgs e)
        {
            Matrix matrix = (MyGrid.RenderTransform as MatrixTransform).Matrix;
            matrix.Scale(1.1, 1.1);
            MyGrid.RenderTransform = new MatrixTransform(matrix);
        }

        // 回転
        private void Button_Click_2(object sender, RoutedEventArgs e)
        {
            Matrix matrix = (MyGrid.RenderTransform as MatrixTransform).Matrix;
            matrix.Rotate(20);
            MyGrid.RenderTransform = new MatrixTransform(matrix);
        }

        // 全部
        private void Button_Click_3(object sender, RoutedEventArgs e)
        {
            Matrix matrix = (MyGrid.RenderTransform as MatrixTransform).Matrix;
            matrix.Translate(5, 5);
            matrix.Scale(1.1, 1.1);
            matrix.Rotate(20);
            MyGrid.RenderTransform = new MatrixTransform(matrix);
        }
    }
}
```

before
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/49d8d113-06a9-84d0-e287-ef6c4bcaba3f.png)

after
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/352601/1ba5e86d-ca23-d3ed-7ec2-fd01c71ce0d2.png)

# メモ

- 上記のようにすると、移動/拡大縮小/回転が実現できる。
- xamlの方で、`RenderTransformOrigin`に0.5,0.5をセットしているが、これをセットすると、Gridのど真ん中を中心(基準)に、回転や拡大が行われる。セットしてないと、Gridの左上基準になる。
- 例では`matrix.Scale()`などを使っているが、本来やりたかった`フリック・ピンチインアウトで移動・拡大縮小`をするときは、動きの中心点を指定できる`ScaleAt()`や`RotateAt()`を使うらしい。(フリック・ピンチインアウトのイベントで、指の移動量と、2本指でピンチしたときの中心を採れるから、それを`ScaleAt()`や`RotateAt()`に渡してやると、ええ感じにできるらしい。)
- MatrixTransformは、本当は「 アフィン変換行列をどうこう」みたいな難しいことをするためのものっぽいが、とりあえず簡単な使い方だけ今回は知っておく。

**次は、フリック・ピンチインアウトで画像の移動・拡大縮小する方法を調べる。**

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

# 参照

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