Search code examples
c#wpfcanvasgraphics2d

Pan canvas such that Point (x,y) is at the center


I have a Canvas is WPF which includes a lot of shapes and lines. I have included Zooming and Panning feature in the canvas. I have a search like feature which takes X and Y as input and when I request for search the canvas should zoom into that point and translate the canvas in such a way that (X,Y) position is at the center. The zooming works fine but I'm not able to implement the Pan.
I used the following code for PanBorder.cs with the help of this post. Here, I want use PanToPosition(double x , double y) to translate such that the center is at (X,Y).

class PanBorder : Border
{
    private UIElement child = null;
    private Point origin;
    private Point start;private TranslateTransform GetTranslateTransform(UIElement element)
    {
        return (TranslateTransform)((TransformGroup)element.RenderTransform)
          .Children.First(tr => tr is TranslateTransform);
    }

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

    public override UIElement Child
    {
        get { return base.Child; }
        set
        {
            if (value != null && value != this.Child)
                this.Initialize(value);
            base.Child = value;
        }
    }

    public void Initialize(UIElement element)
    {
        this.child = element;
        if (child != null)
        {
            TransformGroup group = new TransformGroup();
            ScaleTransform st = new ScaleTransform();
            group.Children.Add(st);
            TranslateTransform tt = new TranslateTransform();
            group.Children.Add(tt);
            child.RenderTransform = group;
            child.RenderTransformOrigin = new Point(0.0, 0.0);

            this.MouseLeftButtonDown += child_MouseLeftButtonDown;
            this.MouseLeftButtonUp += child_MouseLeftButtonUp;
            this.MouseMove += child_MouseMove;
        }
    }

    public void Reset()
    {
        if (child != null)
        {
            // reset pan
            var tt = GetTranslateTransform(child);
            tt.X = 0.0;
            tt.Y = 0.0;
        }
    }

    public void PanToPosition(double x, double y) {
        if (child != null)
        {
            var tt = GetTranslateTransform(child);

            //Pan such that center is at (X,Y)

        }
    }
    #region Child Events

    private void child_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        if (child != null)
        {
            var tt = GetTranslateTransform(child);
            start = e.GetPosition(this);
            origin = new Point(tt.X, tt.Y);
            this.Cursor = Cursors.Hand;
            child.CaptureMouse();
        }
    }

    private void child_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (child != null)
        {
            child.ReleaseMouseCapture();
            this.Cursor = Cursors.Arrow;
        }
    }

    private void child_MouseMove(object sender, MouseEventArgs e)
    {
        if (child != null)
        {
            if (child.IsMouseCaptured)
            {
                var tt = GetTranslateTransform(child);
                Vector v = start - e.GetPosition(this);
                tt.X = origin.X - v.X;
                tt.Y = origin.Y - v.Y;
            }
        }
    }

    #endregion
}`

I have the following code to zoom into the canvas:

//for zooming control on the window
    void window_MouseWheel(object sender, MouseWheelEventArgs e)
    {
        Point p = e.MouseDevice.GetPosition(canvasWaSNA); 

        Matrix m = canvasWaSNA.RenderTransform.Value;
        if (e.Delta > 0) 
            m.ScaleAtPrepend(1.1, 1.1, p.X, p.Y);
        else
            m.ScaleAtPrepend(1 / 1.1, 1 / 1.1, p.X, p.Y);

        canvasWaSNA.RenderTransform = new MatrixTransform(m);
    }

And when I input the Point (X,Y) I call the following function:

public void moveToPosition(double x, double y) {
        resize();
        border.PanToPosition(x, y);
        Point p = new Point(x, y);
        Matrix m = canvasWaSNA.RenderTransform.Value;
        m.ScaleAtPrepend(6, 6, p.X, p.Y);
        canvasWaSNA.RenderTransform = new MatrixTransform(m);
    }

To resize the canvas I have following code:

private void resize()
    {
        Matrix m = canvasWaSNA.RenderTransform.Value;
        m.SetIdentity();
        canvasWaSNA.RenderTransform = new MatrixTransform(m);
        border.Reset();
    }

I need help. Thanx in advance.


Solution

  • Well, I finally did it. I applied the simple concept of translating a point, as my problem was to translate Point(X,Y) to the center of the screen and apply the zoom. Here is the updated PanToPosition function:

    public void PanToPosition(double x, double y, Point center) {
            if (child != null)
            {
                var tt = GetTranslateTransform(child);
                start = new Point(x,y);
                Point p = new Point(center.X, center.Y);
                origin = new Point(tt.X, tt.Y);
                Vector v = start - p;
    
                tt.X = origin.X - v.X;
                tt.Y = origin.Y - v.Y;
            }
        }
    

    And I called this function as:

    public void moveToPosition(double x, double y) {
            resize();
            Point center = new Point(this.Width / 2, this.Height / 2);
            border.PanToPosition(x, y, center);
            Point p = new Point(x, y);
            Matrix m = canvasWaSNA.RenderTransform.Value;
            m.ScaleAtPrepend(6, 6, p.X, p.Y);
            canvasWaSNA.RenderTransform = new MatrixTransform(m);
        }
    

    Here, I passed the center point of the screen and the point to be translated to the center. The start point is the point to be translated and p is the destination. Thus, I calculated the translation scale factor.