I've used your behaviour and changed few things to make it more MVVM'y:
<Window x:Class="WpfApp1.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:WpfApp1"
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
mc:Ignorable="d"
Title="MainWindow" Height="350" Width="525" DataContext="{Binding RelativeSource={RelativeSource Self}, Path=ViewModel}">
<ItemsControl ItemsSource="{Binding Path=Shapes}">
<ItemsControl.Resources>
<DataTemplate DataType="{x:Type local:Rectangle}">
<Rectangle Canvas.Top="{Binding Top, Mode=TwoWay}" Canvas.Left="{Binding Left, Mode=TwoWay}" Width="{Binding Width}" Height="{Binding Height}" Fill="Red">
<i:Interaction.Behaviors>
<local:DragBehavior/>
</i:Interaction.Behaviors>
</Rectangle>
</DataTemplate>
<DataTemplate DataType="{x:Type local:Circle}">
<Ellipse Width="{Binding Radius}" Height="{Binding Radius}" Fill="Blue" Canvas.Top="{Binding Top, Mode=TwoWay}" Canvas.Left="{Binding Left, Mode=TwoWay}">
<i:Interaction.Behaviors>
<local:DragBehavior/>
</i:Interaction.Behaviors>
</Ellipse>
</DataTemplate>
</ItemsControl.Resources>
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Top" Value="{Binding Path=Top, Mode=TwoWay}" />
<Setter Property="Canvas.Left" Value="{Binding Path=Left, Mode=TwoWay}" />
</Style>
</ItemsControl.ItemContainerStyle>
</ItemsControl>
</Window>
this is why I used a style for binding to Canvas.Top and Left.
This is my ViewModel. I used ReactiveUI for IPropertyChanged, but this doesn't matter really.
public class MainViewModel : ReactiveObject
{
private ReactiveList<IShape> _shapes;
public MainViewModel()
{
Shapes = new ReactiveList<IShape>();
Shapes.Add(new Rectangle { Top = 50, Left = 50, Height = 50, Width = 50 });
Shapes.Add(new Circle{Top = 100, Left = 100, Radius = 50});
}
public ReactiveList<IShape> Shapes
{
get { return _shapes; }
set { this.RaiseAndSetIfChanged(ref _shapes, value); }
}
}
public interface IShape
{
int Top { get; set; }
int Left { get; set; }
}
public abstract class Shape : ReactiveObject, IShape
{
private int _top;
private int _left;
public int Top
{
get { return _top; }
set { this.RaiseAndSetIfChanged(ref _top, value); }
}
public int Left
{
get { return _left; }
set { this.RaiseAndSetIfChanged(ref _left, value); }
}
}
public class Circle : Shape
{
private int _radius;
public int Radius
{
get { return _radius; }
set { this.RaiseAndSetIfChanged(ref _radius, value); }
}
}
public class Rectangle : Shape
{
private int _width;
private int _height;
public int Width
{
get { return _width; }
set { this.RaiseAndSetIfChanged(ref _width, value); }
}
public int Height
{
get { return _height; }
set { this.RaiseAndSetIfChanged(ref _height, value); }
}
}
I created classes for reactangles and circles, because the whole point of MVVM is to make distinction between layers. Holding UI controls in ViewModel is deffinetely against the idea.
Lastly, I had to change your MouseLeftButtonUp a little:
AssociatedObject.MouseLeftButtonUp += (sender, e) =>
{
AssociatedObject.ReleaseMouseCapture();
var diff = e.GetPosition(parent) - mouseStartPosition;
Canvas.SetTop(AssociatedObject, ElementStartPosition.Y + diff.Y);
Canvas.SetLeft(AssociatedObject, ElementStartPosition.X + diff.X);
transform.Y = 0;
transform.X = 0;
};
This takes changes from RenderTransform and writes them into object. Then, two way binding takes it down into our Rectangle class.
This is needed only,if you want to know where objects are, for example to check if they intersect in VM.
It works quite well, and is as MVVM as you can get. Maybe with exception of line var parent = Application.Current.MainWindow;
- this should replaced I think with binding to public dependency property of your behaviour.