0

So I want to make a funtunality like in Windows when you display a Picture that when you zoom in you can just move the Picture with the mouse. I already got the zoom part in my project:

<ScrollViewer Name="scroll" Margin="40"
                  HorizontalScrollBarVisibility="Auto" 
                  VerticalScrollBarVisibility="Auto">
                <Canvas
                MouseWheel="Container_MouseWheel" x:Name="canvas">
                    <Viewbox>
                        <Image x:Name="img"  Source="{Binding imageSource}" Grid.ColumnSpan="2" />

                    </Viewbox>
                </Canvas>
            </ScrollViewer>

and the Code in the MouseWheel Event:

  var element = sender as UIElement;
        var position = e.GetPosition(element);
        var transform = element.RenderTransform as MatrixTransform;
        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);
        element.RenderTransform = new MatrixTransform(matrix);

but now Ive got the Problem that when I zoom I cant just click and hold the Image with the mouse and move to an another part of the picture, if you dont really know what I mean just open an Image on windows zoom in and then hold the mouse and move in the picture.

What I have tried was to add these 3 Events to my Canvas and this is the code behind for the 3 Events:

  private Image draggedImage;
    private Point mousePosition;
    private void Canvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var image = e.Source as Image;

        if (image != null && canvas.CaptureMouse())
        {
            mousePosition = e.GetPosition(canvas);
            draggedImage = image;
            Panel.SetZIndex(draggedImage, 1); // in case of multiple images
        }
    }

    private void Canvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        if (draggedImage != null)
        {
            canvas.ReleaseMouseCapture();
            Panel.SetZIndex(draggedImage, 0);
            draggedImage = null;
        }
    }

    private void Canvas_MouseMove(object sender, MouseEventArgs e)
    {
        if (draggedImage != null)
        {
            var position = e.GetPosition(canvas);
            var offset = position - mousePosition;
            mousePosition = position;
            Canvas.SetLeft(draggedImage, Canvas.GetLeft(draggedImage) + offset.X);
            Canvas.SetTop(draggedImage, Canvas.GetTop(draggedImage) + offset.Y);
        }
    }

but this didnt work saddly, does anyone has an Idea on how to make it work?

  • As a note, it makes no sense to put an Image in a Viewbox in a Canvas. The Viewbox has no effect, bcause a Canvas never applies any sizing on its child elements. Also setting Grid.ColumnSpan on the Image is totally pointless. – Clemens Nov 02 '22 at 09:36
  • Drop the ScrollViewer and the Viewbox and apply a translation to the Matrix. – Clemens Nov 02 '22 at 09:37
  • Also `element.RenderTransform = new MatrixTransform(matrix);` is not necessary. In the [original answer](https://stackoverflow.com/a/22353109/1136211) there is `transform.Matrix = matrix;` – Clemens Nov 02 '22 at 09:39
  • I removed the scrollViewer and Viewbox but what you mean by "Translation to the Matrix"? Because from now its not working either – NadiaBallshek Nov 02 '22 at 10:07

1 Answers1

0

You should not mix layout (i.e. set Canvas.Left/Top) and render transformations. If you zoom by means of a RenderTransform, you should also pan by the same RenderTransform.

Here is a simple example where the MouseMove handler also manipulates the RenderTransform of the Image element.

<Canvas x:Name="canvas"
        Background="Transparent"
        MouseLeftButtonDown="CanvasMouseLeftButtonDown"
        MouseLeftButtonUp="CanvasMouseLeftButtonUp"
        MouseMove="CanvasMouseMove"
        MouseWheel="CanvasMouseWheel">
    <Image x:Name="image" Source=...>
        <Image.RenderTransform>
            <MatrixTransform/>
        </Image.RenderTransform>
    </Image>
</Canvas>

Note that mouse positions are determined differently for pan and zoom, in order to get an "unscaled" offset for panning, but an origin point relative to the Image for zooming.

private Point? mousePosition;

private void CanvasMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    if (canvas.CaptureMouse())
    {
        mousePosition = e.GetPosition(canvas); // position in Canvas
    }
}

private void CanvasMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    canvas.ReleaseMouseCapture();
    mousePosition = null;
}

private void CanvasMouseMove(object sender, MouseEventArgs e)
{
    if (mousePosition.HasValue)
    {
        var position = e.GetPosition(canvas); // position in Canvas
        var translation = position - mousePosition.Value;
        mousePosition = position;

        var transform = (MatrixTransform)image.RenderTransform;
        var matrix = transform.Matrix;
        matrix.Translate(translation.X, translation.Y);
        transform.Matrix = matrix;
    }
}

private void CanvasMouseWheel(object sender, MouseWheelEventArgs e)
{
    var position = e.GetPosition(image); // position in Image
    var scale = e.Delta >= 0 ? 1.1 : (1.0 / 1.1);

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