title: Translate a WPF canvas at high scale factors isn't smooth away from origin
body: <p>Imagine you have a canvas that you want to scale to a very high value and then allow "panning."</p>
  
  <p>A good example is a geographic tool that needs to allow panning with "zoom" levels from the extent of the whole earth down to the extent of a few meters.</p>
  
  <p>I've found that if you are scaled in to more than, say 500,000, translating becomes very erratic, but ONLY if you are viewing far from the canvas's 0,0 origin!</p>
  
  <p>I've tried to translate using the RenderTransform of the canvas AND I've tried it by literally moving anothercanvas on top of the scaled canvas.  I've also seen the same issue in someone else's sample app online.</p>
  
  <p>The following example code provides for panning (click and drag) at two different zoom locations.  If you implement the code, you can hit one button to zoom in to 0,0 where you will find nice, smooth mouse panning.  Then use the other button to zoom in to 200, 200 and smooth panning is no more!</p>
  
  <p>Any idea why this is or how one could fix it?</p>
  
  <p>XAML for sample:</p>
  
  <pre><code>&lt;Window x:Class="TestPanZoom.Window1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      Title="Window1" Height="500" Width="500" Loaded="Window_Loaded"&gt;
      &lt;Grid PreviewMouseLeftButtonDown="Grid_PreviewMouseLeftButtonDown" MouseMove="Grid_MouseMove"&gt;
          &lt;Canvas Name="canvas1"&gt;&lt;/Canvas&gt;
          &lt;Button Height="31" 
                  Name="button1" 
                  Click="button1_Click" 
                  Margin="12,12,0,0" 
                  VerticalAlignment="Top" 
                  HorizontalAlignment="Left" Width="270"&gt;
              Zoom WAY in to 0,0 and get smooth panning
          &lt;/Button&gt;
          &lt;Button Height="31" 
                  Name="button2" 
                  Click="button2_Click" 
                  Margin="12,49,0,0" 
                  VerticalAlignment="Top" 
                  HorizontalAlignment="Left" 
                  Width="270"&gt;
              Zoom WAY in to 200, 200 -- NO smooth panning
          &lt;/Button&gt;
      &lt;/Grid&gt;
  &lt;/Window&gt;
  </code></pre>
  
  <p>Code for sample:</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Text;
  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 TestPanZoom
  {
      /// &lt;summary&gt;
      /// Interaction logic for Window1.xaml
      /// Demo of an issue with translate transform when scale is very high
      /// but ONLY when you are far from the canvas's 0,0 origin.
      /// Why?  Is their a fix?
      /// &lt;/summary&gt;
      public partial class Window1 : Window
      {
          Point m_clickPoint;
  
          public Window1()
          {
              InitializeComponent();
          }
  
          private void Window_Loaded(object sender, RoutedEventArgs e)
          {
              // Add a 2x2 black ellipse centered at 0,0
              Ellipse el = new Ellipse();
              el.Fill = Brushes.Black;
              el.Width = 2;
              el.Height = 2;
              el.HorizontalAlignment = HorizontalAlignment.Left;
              el.VerticalAlignment = VerticalAlignment.Top;
              el.Margin = new Thickness(0 - el.Width / 2, 0 - el.Height / 2, 0, 0);
              canvas1.Children.Add(el);
  
              // Add a 1x1 red rectangle with its top/left corner at 0,0
              Rectangle r = new Rectangle();
              r.Fill = Brushes.Red;
              r.Width = 1;
              r.Height = 1;
              r.HorizontalAlignment = HorizontalAlignment.Left;
              r.VerticalAlignment = VerticalAlignment.Top;
              r.Margin = new Thickness(0, 0, 0, 0);
              canvas1.Children.Add(r);
  
  
              // Add a 2x2 purple ellipse at a point 200,200
              Point otherPoint = new Point(200, 200);
              el = new Ellipse();
              el.Fill = Brushes.Purple;
              el.Width = 2;
              el.Height = 2;
              el.HorizontalAlignment = HorizontalAlignment.Left;
              el.VerticalAlignment = VerticalAlignment.Top;
              el.Margin = new Thickness(otherPoint.X - el.Width / 2, otherPoint.Y - el.Height / 2, 0, 0);
              canvas1.Children.Add(el);
  
              // Add a 1x1 blue rectangle with its top/left corner at 200,200
              r = new Rectangle();
              r.Fill = Brushes.Blue;
              r.Width = 1;
              r.Height = 1;
              r.HorizontalAlignment = HorizontalAlignment.Left;
              r.VerticalAlignment = VerticalAlignment.Top;
              r.Margin = new Thickness(otherPoint.X, otherPoint.Y, 0, 0);
              canvas1.Children.Add(r);
          }
  
  
          private void Grid_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
          {
              m_clickPoint = e.GetPosition(this);
          }
  
          // Pan with the mouse when left-mouse is down
          private void Grid_MouseMove(object sender, MouseEventArgs e)
          {
              if (e.LeftButton == MouseButtonState.Pressed)
              {
                  Point mousePosition = e.GetPosition(this);
                  double xDiff = mousePosition.X - m_clickPoint.X;
                  double yDiff = mousePosition.Y - m_clickPoint.Y;
  
                  TranslateTransform tt = new TranslateTransform(xDiff, yDiff);
                  TransformGroup tg = new TransformGroup();
                  tg.Children.Add(canvas1.RenderTransform);
                  tg.Children.Add(tt);
                  canvas1.RenderTransform = tg;
  
                  m_clickPoint = e.GetPosition(this);
              }
          }
  
          private void button1_Click(object sender, RoutedEventArgs e)
          {
              TransformGroup tg = new TransformGroup();
              double scale = 1000000;
              double xCenter = 0;
              double yCenter = 0;
              double xOffset = (canvas1.ActualHeight / 2.0 - xCenter);
              double yOffset = (canvas1.ActualWidth / 2.0 - yCenter);
              ScaleTransform st = new ScaleTransform(scale, scale);
              st.CenterX = xCenter;
              st.CenterY = yCenter;
              TranslateTransform tt = new TranslateTransform(xOffset, yOffset);
              tg.Children.Add(st);
              tg.Children.Add(tt);
              canvas1.RenderTransform = tg;
          }
  
          private void button2_Click(object sender, RoutedEventArgs e)
          {
              TransformGroup tg = new TransformGroup();
              double scale = 1000000;
              double xCenter = 200;
              double yCenter = 200;
              double xOffset = (canvas1.ActualHeight / 2.0 - xCenter);
              double yOffset = (canvas1.ActualWidth / 2.0 - yCenter);
              ScaleTransform st = new ScaleTransform(scale, scale);
              st.CenterX = xCenter;
              st.CenterY = yCenter;
              TranslateTransform tt = new TranslateTransform(xOffset, yOffset);
              tg.Children.Add(st);
              tg.Children.Add(tt);
              canvas1.RenderTransform = tg;
          }
  
      }
  }
  </code></pre>