Search code examples
wpfcanvasrendertransformationscrollviewer

How to correctly synchronize WPF Scrollviewer and Canvas on Zooming in or out on Mouse Position


I'm using the following controls in WPF (XAML) code to create a canvas:

<ScrollViewer x:Name="CanvasScrollViewer"  HorizontalScrollBarVisibility="Visible" VerticalScrollBarVisibility="Visible" Background="Beige"  >
<Canvas x:Name="canvas"  Height="10000" Width="10000"   MouseWheel="CanvasMouseWheel" >
    <Canvas.RenderTransform>
        <MatrixTransform/>
    </Canvas.RenderTransform>
</Canvas>

In essence, this includes a canvas (which is larger than my screen resolution) and a scrollviewer, to make sure that I can navigate around the whole canvas.

Now, I want to zoom in / out at the current mouse cursor position of the canvas if I keep CTRL pressed and use the mousewheel. It works quite well using an approach similar to the one listed here.

My C# code looks like this:

private void CanvasMouseWheel(object sender, MouseWheelEventArgs e)
{
    if (Keyboard.Modifiers != ModifierKeys.Control)
        return;

    var sentCanvas = sender as UIElement;
    var position = e.GetPosition(sentCanvas);

    var transform = canvas.RenderTransform as MatrixTransform;
    var matrix = transform.Matrix;
    var scale = e.Delta >= 0 ? 1.2 : (1.0 / 1.2); 

    matrix.ScaleAtPrepend(scale, scale, position.X, position.Y);
    transform.Matrix = matrix;
    e.handled = true;
}

This works quite nicely with one exception: If I scroll in / out, the position of the scroll bars of the scroll viewers and the actual position of the canvas are no longer synchronized. Meaning: The canvas zooms in or out, but the scroll viewer does not adjusts its scroll bar positions which ultimately means that you cannot properly scroll the canvas anymore. Naturally (as I use the canvas to draw large diagrams which require a lot of navigation), I want to be able zoom out quite a lot from time to time, so that the whole canvas fills only part of the field of view of the scroll viewer. The attached screen shot shows that situation for clarification where I zoomed out a lot.

enter image description here

I tried several other approaches like first moving the scroll viewer to the mouse position and then applying the "scale(...)" operation (without "AtPrepend"), but to no avail. Performing the scale operation directly on the scrollviewer obviously doesn't work, either. Probably, the solution is trivial, but I can't see the forest for the trees...

Any help would be deeply appreciated!


Solution

  • Thanks to BionicCode for the hint. The following entry answers my question and works perfectly:

    WPF Canvas zoom and children position