6

I am trying to implement a zoom-functionality for a canvas using the mouse wheel. Currently I am just Zooming to the center position of the canvas using CenterX="0.5" and CenterY="0.5". I would like to change the behavior so that the zooming happens at the mouse position and I would like to know if this is possible with a ScaleTransform.

Currently I use the following code:

<Canvas Width="500" Height="500">
    <Canvas.LayoutTransform>
        <ScaleTransform CenterX="0.5" CenterY="0.5"
                                ScaleX="{Binding Zoom}"
                                ScaleY="{Binding Zoom}" />
    </Canvas.LayoutTransform>
</Canvas>
Eggi
  • 1,684
  • 4
  • 20
  • 31
  • [here](http://stackoverflow.com/questions/10372560/zooming-to-mouse-point-with-scrollview-and-viewbox-in-wpf) is an example, he has used a viewbox inside a ScrollViewer. Then he has captured mouse initial postion and implemented zooming Hope this helps.... – Abhinav Sharma Mar 12 '14 at 11:59
  • There are quite a couple of ways to do that, but not with a LayoutTransform. Because LayoutTransform ignores any translations, the transformed Canvas will never be moved appropriately. Would a RenderTransform also be ok? – Clemens Mar 12 '14 at 12:03
  • Could you give me an example how I could do that with RenderTransform? – Eggi Mar 12 '14 at 12:27

2 Answers2

16

A very basic approach to zoom a Canvas (or any other UIElement) at a specific position would be to use a MatrixTransform for the RenderTransform property

<Canvas Width="500" Height="500" MouseWheel="Canvas_MouseWheel">
    <Canvas.RenderTransform>
        <MatrixTransform/>
    </Canvas.RenderTransform>
</Canvas>

and update the Matrix property of the transform like in this MouseWheel handler:

private void Canvas_MouseWheel(object sender, MouseWheelEventArgs e)
{
    var element = (UIElement)sender;
    var position = e.GetPosition(element);
    var transform = (MatrixTransform)element.RenderTransform;
    var matrix = transform.Matrix;
    var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1); // choose appropriate scaling factor

    matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
    transform.Matrix = matrix;
}
Clemens
  • 123,504
  • 12
  • 155
  • 268
3

I spent the past two days agonizing over this issue and I figured it out. This will get you smooth zooming in toward the mouse and smooth zooming out. I'm posting my solution here for anyone who might search and stumble back here.

// Class constructor
public YourClass(Canvas theCanvas) //You may not need the Canvas as an argument depending on your scope
    {
        panTransform = new TranslateTransform();
        zoomTransform = new ScaleTransform();
        bothTransforms = new TransformGroup();

        bothTransforms.Children.Add(panTransform);
        bothTransforms.Children.Add(zoomTransform);

        theCanvas.RenderTransform = bothTransforms;

        //Handler
        theCanvas.MouseWheel += wheelEvent;
        //You also need your own handlers for panning, which I'm not showing here.
    }

private void returnCalculatedScale()
    {
        double d;
        //Do some math to get a new scale. I keep track of an integer, and run it through the formula y^(x/3) where X is the integer.

        return d;
    }


// Mouse wheel handler, where the magic happens
private void wheelEvent(object sender, MouseWheelEventArgs e)
    {
        Point position = e.GetPosition(mainCanvas);           

        zoomTransform.CenterX = position.X;
        zoomTransform.CenterY = position.Y;

        zoomTransform.ScaleX = returnCalculatedScale();
        zoomTransform.ScaleY = returnCalculatedScale();

        Point cursorpos = Mouse.GetPosition(mainCanvas); //This was the secret, as the mouse position gets out of whack when the transform occurs, but Mouse.GetPosition lets us get the point accurate to the transformed canvas.

        double discrepancyX = cursorpos.X - position.X;
        double discrepancyY = cursorpos.Y - position.Y;

        //If your canvas is already panned an arbitrary amount, this aggregates the discrepancy to the TranslateTransform.
        panTransform.X += discrepancyX;
        panTransform.Y += discrepancyY;
NWoodsman
  • 423
  • 4
  • 10