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);
}
}
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With