Make shapes follow the mouse in WPF

2 minute read

What to make

maketarget.gif

How to make

Make a suitable figure

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

Set TranslateTransform object to RenderTransform

Since it is not possible to specify the coordinates of the control in WPF, store the TranslateTransform object that represents the deformation information of the shape in the RenderTransform property in the UIElement class.

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

Create and bind a ViewModel

Put the movement amount in XY of TranslateTransform class.
I want to bind it, so I will create a very simple ViewModel.

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

Bind

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

Play with X, Y of ViewModel

After that, if you play with X and Y of ViewModel, the figure will move.

When polluting the code behind

It’s easy if you can use code-behind.
All you have to do is get the coordinates in the MouseMove event of the Window and assign them directly to the VM.

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;
	}
}
<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>

However, in reality, it will be a strange move if it is left as it is.

makeerror.gif

This is because TranslateTransform is an object that specifies the amount of movement from the initial position.
To avoid this, set the initial position of the figure to the upper left.

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

Now it follows the mouse properly.

maketarget.gif

If you don’t pollute the code behind

It’s a little hard to do without polluting the code-behind.
If you don’t have System.Windows.Interactivity, please install it from nuget because it uses behaviors.
image.png

Create a behavior that looks like this:


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;
    }
}

This code was borrowed from the following site.
https://stackoverflow.com/questions/30047415/how-do-i-get-mouse-positions-in-my-view-model

This behavior subscribes to the MouseMove event for the assigned control and stores its XY coordinates in the MouseX, MouseY dependency properties.

Then assign this behavior to the Window and bind it to the ViewModel’s X, Y and the behavior’s MouseX, MouseY.


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

at the end

that’s all.

Since the RenderTransform property is in the UIElement class, you can make not only shapes but also buttons, for example, follow the mouse.
The usage is a mystery, but …

trackbutton.gif