Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF Scrollviewer scroll to mouse position

I am working on an image viewer control that consists of an image inside of a scrollviewer. To zoom, I use a scaletransform within the image's layouttransform. Zoomin is happening on mouse click. If the user clicks on the image, I would like the pixel at the mouse position to be located at the center of the visual area of the scrollviewer. To achieve this I want to modify the scrollviewer offsets, but I am not sure about how to calculate the correct offset that places the pixel unter the mouse pointer at the center.

Here is a description of how to keep the scrollbars at their relative position. That works fine, but is not what I am looking for.

How do you calculate the offsets correctly?

Here is some of my code:

The xaml:

<ScrollViewer Margin="5" Name="scrvImagePanel" VerticalAlignment="Stretch" 
                  CanContentScroll="True"
                  DockPanel.Dock="Left" 
                  HorizontalAlignment="Stretch" 
                  HorizontalContentAlignment="Stretch"
                  HorizontalScrollBarVisibility="Auto" 
                  VerticalContentAlignment="Stretch" 
                  VerticalScrollBarVisibility="Auto"
                  Background="{StaticResource {x:Static SystemColors.ControlDarkBrushKey}}"
                  ScrollChanged="OnScrollChanged">
        <Image Name="imgPicture"
               MouseUp="imgMouseUp" 
               Stretch="None"
               HorizontalAlignment="Left" 
               VerticalAlignment="Top" 
               >
            <Image.LayoutTransform>
                <TransformGroup>
                <ScaleTransform x:Name="imgZoomTransform" ScaleX="1" ScaleY="1"></ScaleTransform>
                </TransformGroup>
            </Image.LayoutTransform>                
        </Image>
    </ScrollViewer>

and the code:

private void imgMouseUp(object sender, MouseButtonEventArgs e)
{
   Point absScreenPos = e.GetPosition(imgPicture);

    //calculate the new zoom factor
    this.ZoomFactor = imgZoomTransform.ScaleX;
    if (e.ChangedButton == MouseButton.Right)
    {
      this.ZoomFactor /= 2.0;
    }
    else if (e.ChangedButton == MouseButton.Left)
    {
      this.ZoomFactor *= 2.0;
    }

    //perform the zooming
    this.DoZoom(this.ZoomFactor, absScreenPos.X, absScreenPos.Y);

}

private void DoZoom(double zoom, double posX, double posY)
{
  // update the scale transform
  imgZoomTransform.ScaleX = zoom;
  imgZoomTransform.ScaleY = zoom;

  this.AdjustScroll(new Point(posX, posY));
}


private void AdjustScroll(Point? centerPoint)
{
  if(centerPoint != null)
  {
    var sv = this.scrvImagePanel;

    double offsetX = (centerPoint.Value.X / this.imgPicture.ActualWidth) * sv.ActualWidth;
    double offsetY = (centerPoint.Value.Y / this.imgPicture.ActualHeight) * sv.ActualHeight;

    sv.ScrollToHorizontalOffset(offsetX);
    sv.ScrollToVerticalOffset(offsetY);
  }
}
like image 437
tabina Avatar asked Dec 20 '12 09:12

tabina


1 Answers

The idea is simple, when mouse is down, you should calculate its distance from the center and add or subtract it to or from the current offsets.
Consider this code:

bool left = false;
    double scale = 2;
    double ZoomFactor;
    private void imgMouseUp(object sender, MouseButtonEventArgs e)
    {
        Point absScreenPos = e.GetPosition(imgPicture);

        //calculate the new zoom factor
        this.ZoomFactor = imgZoomTransform.ScaleX;
        if (e.ChangedButton == MouseButton.Right)
        {
            left = false;
            this.ZoomFactor /= scale;
        }
        else if (e.ChangedButton == MouseButton.Left)
        {
            left = true;
            this.ZoomFactor *= scale;
        }
        //perform the zooming
        this.DoZoom(this.ZoomFactor, absScreenPos.X, absScreenPos.Y);
    }
    private void AdjustScroll(Point? centerPoint)
    {
        if (centerPoint != null)
        {
            Point poi = Mouse.GetPosition(scrvImagePanel);

            double Xmove = ( scrvImagePanel.ActualHeight) / 2 - poi.X;
            double Ymove = (scrvImagePanel.ActualWidth) / 2 - poi.Y;

            if (left)
            {
                scrvImagePanel.ScrollToHorizontalOffset((scrvImagePanel.HorizontalOffset - Xmove) * scale);
                scrvImagePanel.ScrollToVerticalOffset((scrvImagePanel.VerticalOffset - Ymove) * scale);
            }
            else
            {
                scrvImagePanel.ScrollToHorizontalOffset((scrvImagePanel.HorizontalOffset - Xmove) / scale);
                scrvImagePanel.ScrollToVerticalOffset((scrvImagePanel.VerticalOffset - Ymove) / scale);
            }
        }
    }

I know that this code does not work perfectly, but I think this must give you the path

like image 168
Ramin Avatar answered Oct 05 '22 21:10

Ramin