Search code examples
c#wpfpanning

Canvas "jiggles" when panning


INTRODUCTION

I am new to WPF and I am trying to implement Canvas panning on my own.

I have managed to figure out the logic and mechanics that must be applied, but when implementing them I get a strange behavior.

PROBLEM

My panning code "works" well, meaning that image is moving in the correct direction, but it "jiggles" ( or "dances" if you like ) while moving.

QUESTION

  • Why is this happening?
  • How to fix it?

RELEVANT INFORMATION:

Below is a small demo app that illustrates the problem.

XAML:

<Window x:Class="CanvasPanning.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid Background="Beige">
        <Canvas Name="canvas" 
                Width="200" 
                Height="200" 
                Background="Aqua"
                MouseLeftButtonDown="canvas_MouseLeftButtonDown" 
                MouseLeftButtonUp="canvas_MouseLeftButtonUp" 
                LostMouseCapture="canvas_LostMouseCapture" 
                MouseMove="canvas_MouseMove">
            <Ellipse Stroke="Black" Fill="Beige" Canvas.Left="50" Canvas.Top="50" Width="100" Height="50" />
            <Rectangle Stroke="Black" Fill="Red" Canvas.Left="50" Canvas.Top="120" Width="100" Height="50" />
        </Canvas>
    </Grid>
</Window>

Code behind:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

namespace CanvasPanning
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        private Matrix m;
        private bool isPanning;
        private Point ptOldPanningPosition;

        public MainWindow()
        {
            InitializeComponent();

            isPanning = false;
            m = canvas.RenderTransform.Value;
        }

        private void canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonDown(e);

            if (isPanning) return;

            isPanning = canvas.CaptureMouse();

            if(isPanning)
            {
                ptOldPanningPosition = e.GetPosition(canvas);
            }

        }

        private void canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            base.OnMouseLeftButtonUp(e);

            if (isPanning)
            {
                isPanning = false;
                canvas.ReleaseMouseCapture();
            }
        }

        private void canvas_LostMouseCapture(object sender, MouseEventArgs e)
        {
            isPanning = false;
            base.OnLostMouseCapture(e);
        }

        private void canvas_MouseMove(object sender, MouseEventArgs e)
        {
            base.OnMouseMove(e);

            if(isPanning)
            {
                Point ptNewPanningPosition = e.GetPosition(canvas);

                Vector offset = ptNewPanningPosition - ptOldPanningPosition;

                m.OffsetX += offset.X;
                m.OffsetY += offset.Y;

                ptOldPanningPosition = ptNewPanningPosition;

                canvas.RenderTransform = new MatrixTransform(m);

            }
        }
    }
}

Solution

  • You are taking the position relative to the canvas and you are moving the canvas, take the position relate to the window or the containing element the drag is happening

    In mouse left down

    if(isPanning)
    {
        ptOldPanningPosition = e.GetPosition(this);
    }
    

    And in mousemove

    if(isPanning)
    {
        Point ptNewPanningPosition = e.GetPosition(this);
    }