You can use for example the MouseDown, MouseMove and MouseUp events and then the MouseEventArgs's GetPosition(element) that returns you the coordinates relative to element (there are more events that expose this method).
Also, take advantage of the RoutedEvent's OriginalSource to check which element inside the canvas was clicked (in this case it's only the rectangle).
Here's an example:
<Grid>
<Canvas Name="MyCanvas" PreviewMouseLeftButtonDown="OnMouseDown" MouseMove="OnMouseMove">
<Rectangle Width="20" Height="20" Canvas.Left="10" Canvas.Top="10" Fill="Blue"/>
<Rectangle Width="20" Height="20" Canvas.Left="50" Canvas.Top="10" Fill="Red"/>
</Canvas>
</Grid>
Code-behind:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
AddHandler(Mouse.MouseUpEvent, new MouseButtonEventHandler(OnMouseUp), true);
}
private bool _isBeingDragged;
public void OnMouseDown(object sender, MouseEventArgs args)
{
if (!(args.OriginalSource is Canvas))
{
_isBeingDragged = true;
}
}
public void OnMouseMove(object sender, MouseEventArgs args)
{
if (_isBeingDragged)
{
var elementBeingDragged = (FrameworkElement) args.OriginalSource;
var position = args.GetPosition(MyCanvas);
Canvas.SetLeft(elementBeingDragged, position.X - elementBeingDragged.ActualWidth / 2);
Canvas.SetTop(elementBeingDragged, position.Y - elementBeingDragged.ActualHeight / 2);
}
}
public void OnMouseUp(object sender, MouseEventArgs args)
{
_isBeingDragged = false;
}
}
I had to use the UIElement's AddHandler method to register MouseUp because somehow it has being marked as Handled (the AddHandler method allows you to register an event handler for events that have been handled previously, i.e. e.Handled = true)