Recently, I needed to support dragging shapes and some other elements on a Canvas in WPF. However, looking online I found several implementations that were more complex than needed and/or not well functioning and I just wanted something very simple and solid. For that reason, below you will find a simple, yet useful implementation which can be easily adapted according to you needs.
Step 1: Create a WPF C# Project and add a Canvas in the main window
<Window x:Class="Canvas_Test.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Canvas Margin="0" >
</Canvas>
</Window>
Step 2: Create your shapes inside the canvas
<Window x:Class="Canvas_Test.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Canvas Margin="0" >
<Rectangle Width="50" Height="50" Fill="Red" />
<Rectangle Width="50" Height="50" Fill="Green" />
<Rectangle Width="50" Height="50" Fill="Blue" />
</Canvas>
</Window>
Step 3: Write the proper handlers in the cs
public partial class MainWindow : Window
{
protected bool isDragging;
private Point clickPosition;
private TranslateTransform originTT;
public MainWindow()
{
InitializeComponent();
}
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var draggableControl = sender as Shape;
originTT = draggableControl.RenderTransform as TranslateTransform ?? new TranslateTransform();
isDragging = true;
clickPosition = e.GetPosition(this);
draggableControl.CaptureMouse();
}
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
isDragging = false;
var draggable = sender as Shape;
draggable.ReleaseMouseCapture();
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
var draggableControl = sender as Shape;
if (isDragging && draggableControl != null)
{
Point currentPosition = e.GetPosition(this);
var transform = draggableControl.RenderTransform as TranslateTransform ?? new TranslateTransform();
transform.X = originTT.X+ (currentPosition.X - clickPosition.X);
transform.Y =originTT.Y+ (currentPosition.Y - clickPosition.Y);
draggableControl.RenderTransform = new TranslateTransform(transform.X, transform.Y);
}
}
}
Step 4: Assign the proper handlers to the shapes
<Window x:Class="Canvas_Test.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"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
<Canvas Margin="0" >
<Rectangle Width="50" Height="50" Fill="Red" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseMove="Canvas_MouseMove" />
<Rectangle Width="50" Height="50" Fill="Green" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseMove="Canvas_MouseMove" />
<Rectangle Width="50" Height="50" Fill="Blue" MouseLeftButtonUp="Canvas_MouseLeftButtonUp" MouseLeftButtonDown="Canvas_MouseLeftButtonDown" MouseMove="Canvas_MouseMove"/>
</Canvas>
</Window>
Below is the final output – you will be able to drag-move the boxes around the window
You can also clone the project from github: https://github.com/devcoons/wpf-csharp-canvas-move-items
Excellent code. Simple and working way.
Thanks mate, exactly what I was looking for!
It’s nit working what I should do??
This project helped me a ton, so I figure I’ll pay it back by leaving this for future viewers.
If you want to clamp the elements inside the canvas, add this to the canvas element: `x:Name=”Canvas” `
Then change the code-behind to:
“`
protected bool isDragging;
private Point clickPosition;
private TranslateTransform originTT;
private Rect canvasBounds;
private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (sender is Grid draggableControl)
{
originTT = draggableControl.RenderTransform as TranslateTransform ?? new TranslateTransform();
isDragging = true;
clickPosition = e.GetPosition(this);
draggableControl.CaptureMouse();
canvasBounds = Canvas.RenderTransform.TransformBounds(
new Rect(0, 0, Canvas.ActualWidth, Canvas.ActualHeight));
}
}
private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
isDragging = false;
if(sender is Grid draggableControl) draggableControl.ReleaseMouseCapture();
}
private void Canvas_MouseMove(object sender, MouseEventArgs e)
{
if (isDragging && sender is Grid draggableControl)
{
Point currentPosition = e.GetPosition(this);
var transform = draggableControl.RenderTransform as TranslateTransform ?? new TranslateTransform();
transform.X = Math.Clamp(originTT.X + (currentPosition.X – clickPosition.X), canvasBounds.Left, canvasBounds.Right – draggableControl.ActualWidth);
transform.Y = Math.Clamp(originTT.Y + (currentPosition.Y – clickPosition.Y), canvasBounds.Top, canvasBounds.Bottom – draggableControl.ActualHeight);
draggableControl.RenderTransform = new TranslateTransform(transform.X, transform.Y);
}
}
“`