0

The following class (PanImage) is an adaption of the ZoomBorder class from this thread here Pan & Zoom Image. As I need to custom-paint over the image, I cannot use a border. When the PanImage is moved with the left mouse-button and the button is still pressed, the image jumps back and forth between two positions. Why is it doing this and how can I fix this behavior?

using System.ComponentModel;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;

class PanImage : Image
{

    public PanImage()
    {
        // add event-handler for changes of the SourceProperty of the Image class
        DependencyPropertyDescriptor dpd = DependencyPropertyDescriptor.FromProperty(Image.SourceProperty, typeof(Image));
        dpd?.AddValueChanged(this, delegate { Initialize(); });
    }


    private Point _origin;
    private Point _start;

    private TranslateTransform GetTranslateTransform()
    {
        return (TranslateTransform)((TransformGroup)RenderTransform)
          .Children.First(tr => tr is TranslateTransform);
    }

    private ScaleTransform GetScaleTransform()
    {
        return (ScaleTransform)((TransformGroup)RenderTransform)
          .Children.First(tr => tr is ScaleTransform);
    }

    public void Initialize()
    {
        RenderOptions.SetBitmapScalingMode(this, BitmapScalingMode.NearestNeighbor);
        TransformGroup group = new TransformGroup();
        ScaleTransform st = new ScaleTransform();
        TranslateTransform tt = new TranslateTransform();
        group.Children.Add(st);
        group.Children.Add(tt);

        RenderTransform = group;
        RenderTransformOrigin = new Point(0.0, 0.0);

        MouseLeftButtonDown += handle_MouseLeftButtonDown;
        MouseLeftButtonUp += handle_MouseLeftButtonUp;
        MouseMove += handle_MouseMove;
        PreviewMouseRightButtonDown += handle_PreviewMouseRightButtonDown;
    }

    public void Reset()
    {
        // reset zoom
        var st = GetScaleTransform();
        st.ScaleX = 1.0;
        st.ScaleY = 1.0;

        // reset pan
        var tt = GetTranslateTransform();
        tt.X = 0.0;
        tt.Y = 0.0;
    }


    private void handle_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var tt = GetTranslateTransform();
        _start = e.GetPosition(this);
        _origin = new Point(tt.X, tt.Y);
        Cursor = Cursors.Hand;
        CaptureMouse();
    }

    private void handle_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        ReleaseMouseCapture();
        Cursor = Cursors.Arrow;
    }

    void handle_PreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
    {
        Reset();
    }

    private void handle_MouseMove(object sender, MouseEventArgs e)
    {
        if (IsMouseCaptured)
        {
            Vector v = _start - e.GetPosition(this);
            var tt = GetTranslateTransform();
            tt.X = _origin.X - v.X;
            tt.Y = _origin.Y - v.Y;
        }
    }
}
ptair
  • 177
  • 1
  • 11
  • This often happens when you get the mouse position relative to the moved element. Try to get it from an element that won't move, e.g. the Image's parent element. – Clemens May 22 '19 at 17:15
  • Thank you for the hint, taking the position from the moved element was indeed the problem. If you add this comment as an answer, I can accept it. Here is how to get the parent element: UIElement parentElem = VisualTreeHelper.GetParent(control) as UIElement; – ptair May 23 '19 at 08:51
  • It's ok if you write a self-answer. – Clemens May 23 '19 at 08:55

1 Answers1

0

The problem was taking the mouse coordinates relative to the element which is moved. In order to prevent this behavior, take the position from a parent-element:

private void handle_MouseMove(object sender, MouseEventArgs e) {
    ...
    UIElement parentElem = VisualTreeHelper.GetParent(this) as UIElement;
    Vector v = _start - e.GetPosition(parentElem);
    ...
}

ptair
  • 177
  • 1
  • 11