WPFで図形をマウスに追従させる

##作るもの
![maketarget.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697830/5e806f2f-4b47-17f0-050e-0a99e7b3c430.gif)



##作り方
###適当な図形を作る

```xml
<Rectangle Stroke="Green"
           StrokeThickness="5"
           Width="50"
           Height="50">
</Rectangle>
```

###RenderTransformにTranslateTransformオブジェクトをセット
WPFではコントロールの座標を指定することはできないので、UIElementクラスにあるRenderTransformプロパティに、図形の変形情報を表すTranslateTransformオブジェクトを格納します。

```xml
<Rectangle Stroke="Green"
           StrokeThickness="5"
           Width="50"
           Height="50">
    <Rectangle.RenderTransform>
        <TranslateTransform X="{Binding X.Value}"
                            Y="{Binding Y.Value}"/>
    </Rectangle.RenderTransform>
</Rectangle>
```

###ViewModelを作ってバインド
TranslateTransformクラスのXYに移動量を入れます。
バインドしたいのですごく簡単なViewModelを作ります。

```csharp
class MainWindowViewModel
{
    public ReactivePropertySlim<double> X { get; } = new ReactivePropertySlim<double>();
    public ReactivePropertySlim<double> Y { get; } = new ReactivePropertySlim<double>();
}
```

バインドします。

```xml
<Rectangle Stroke="Green"
           StrokeThickness="5"
           Width="50"
           Height="50">
    <Rectangle.RenderTransform>
        <TransformGroup>
            <TranslateTransform X="{Binding X.Value}"
                                Y="{Binding Y.Value}"/>
        </TransformGroup>
    </Rectangle.RenderTransform>
</Rectangle>
```

###ViewModelのX, Yをいじる
あとはViewModelのX,Yをいじれば図形が動きます。

####コードビハインドを汚す場合
コードビハインドを使ってもいいなら簡単です。
WindowのMouseMoveイベントで座標を取得してVMに直接代入すればいいです。

```csharp
public partial class MainWindow : Window
{
	public MainWindow() {
		InitializeComponent();
	}

	private MainWindowViewModel VM => (MainWindowViewModel)DataContext;

	private void Window_MouseMove(object sender, MouseEventArgs e) {
		var mousepos = e.GetPosition(this);
		VM.X.Value = mousepos.X;
		VM.Y.Value = mousepos.Y;
	}
}
```

```xml
<Window x:Class="MouseTrackShape.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:MouseTrackShape"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800"
        MouseMove="Window_MouseMove">
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Rectangle Stroke="Green"
                   StrokeThickness="5"
                   Width="50"
                   Height="50">
            <Rectangle.RenderTransform>
                <TransformGroup>
                    <TranslateTransform X="{Binding X.Value}"
                                        Y="{Binding Y.Value}"/>
                </TransformGroup>
            </Rectangle.RenderTransform>
        </Rectangle>
    </Grid>
</Window>

```

しかし、実はこのままだとおかじな動きになります。

![makeerror.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697830/845492d7-826e-868e-8aca-e7052c9ef75e.gif)


これは、TranslateTransformが初期位置からの移動量を指定するオブジェクトだからですね。
これを回避するために図形の初期位置を左上にしておきます。

```xml
<!--略-->
        <Rectangle Stroke="Green"
                   StrokeThickness="5"
                   Width="50"
                   Height="50"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Top">
<!--略-->
```

ちゃんとマウスに追従するようになりました。

![maketarget.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697830/5e806f2f-4b47-17f0-050e-0a99e7b3c430.gif)


####コードビハインドを汚さない場合
コードビハインドを汚さずにやるなら少し大変です。
ビヘイビアを使うので、System.Windows.Interactivityを持っていない人はnugetからインストールしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697830/37a69c93-278a-e1e2-aebd-5601dff54dca.png)

次のようなビヘイビアを作成します。

```csharp

public class MouseBehaviour : System.Windows.Interactivity.Behavior<FrameworkElement>
{
    public static readonly DependencyProperty MouseYProperty = DependencyProperty.Register(
        "MouseY", typeof(double), typeof(MouseBehaviour), new PropertyMetadata(default(double)));

    public double MouseY {
        get { return (double)GetValue(MouseYProperty); }
        set { SetValue(MouseYProperty, value); }
    }

    public static readonly DependencyProperty MouseXProperty = DependencyProperty.Register(
        "MouseX", typeof(double), typeof(MouseBehaviour), new PropertyMetadata(default(double)));

    public double MouseX {
        get { return (double)GetValue(MouseXProperty); }
        set { SetValue(MouseXProperty, value); }
    }

    protected override void OnAttached() {
        AssociatedObject.MouseMove += AssociatedObjectOnMouseMove;
    }

    private void AssociatedObjectOnMouseMove(object sender, MouseEventArgs mouseEventArgs) {
        var pos = mouseEventArgs.GetPosition(AssociatedObject);
        MouseX = pos.X;
        MouseY = pos.Y;
    }

    protected override void OnDetaching() {
        AssociatedObject.MouseMove -= AssociatedObjectOnMouseMove;
    }
}
```
このコードは以下サイトから拝借しました。
https://stackoverflow.com/questions/30047415/how-do-i-get-mouse-positions-in-my-view-model

このビヘイビアは割り当てられたコントロールのMouseMoveイベントを購読して、そのXY座標をMouseX, MouseY依存関係プロパティに格納するものです。

そしてWindowにこのビヘイビアを割り当てて、ViewModelのX, YとビヘイビアのMouseX, MouseYとバインドすればOKですね。

```xml

<Window x:Class="MouseTrackShape.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:MouseTrackShape"
        xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
        mc:Ignorable="d"
        Title="MainWindow" Height="450" Width="800">
    <Window.Resources>
        <local:MouseEventArgsToPositionConverter x:Key="MouseEventArgsToPositionConverter"/>
    </Window.Resources>
    <Window.DataContext>
        <local:MainWindowViewModel/>
    </Window.DataContext>
    <Grid>
        <Rectangle Stroke="Green"
                   StrokeThickness="5"
                   Width="50"
                   Height="50"
                   HorizontalAlignment="Left"
                   VerticalAlignment="Top">
            <Rectangle.RenderTransform>
                <TranslateTransform X="{Binding X.Value}"
                                    Y="{Binding Y.Value}"/>
            </Rectangle.RenderTransform>
        </Rectangle>
    </Grid>
    <i:Interaction.Behaviors>
        <local:MouseBehaviour MouseX="{Binding X.Value, Mode=OneWayToSource}" MouseY="{Binding Y.Value, Mode=OneWayToSource}" />
    </i:Interaction.Behaviors>
</Window>
```

##さいごに
以上です。

RenderTransformプロパティはUIElementクラスにあるので、図形だけじゃなくて例えばボタンなんかをマウスに追従させることもできますよ。
使いみちは謎ですが…

![trackbutton.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/697830/188f3696-fd3f-4e50-59c7-f4b6b522a1f9.gif)