
Introduction
I've been looking the whole day for Open Source solutions that show a simple way of zooming and dragging arbitrary content which is hosted and managed by a ScrollViewer.
As I did not find a free one, I decided to write my own one. It supports zooming by a slider as well as by the mouse wheel.
Using the code
I will only post the whole code. I might post further comments during the next weeks.
If you have any questions regarding the usage, feel free to post. The code snippet is small enough so I hope no further explanations are necessary.
The main view is defined by the XAML below. The content that is to be zoomed and dragged is part of the "grid" control.
<Window x:Class="ZoomExample.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="500" Width="500">
<Window.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Resources.xaml"/>
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Window.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<Slider Grid.Column="0" Orientation="Vertical"
HorizontalAlignment="Left" Minimum="1" x:Name="slider"/>
<ScrollViewer Name="scrollViewer" Grid.Column="1"
VerticalScrollBarVisibility="Visible"
HorizontalScrollBarVisibility="Visible">
<Grid Name="grid" Width="400"
Height="400" RenderTransformOrigin="0.5,0.5">
<Grid.LayoutTransform>
<TransformGroup>
<ScaleTransform x:Name="scaleTransform"/>
</TransformGroup>
</Grid.LayoutTransform>
<Viewbox Grid.Column="0" Grid.Row="0">
<ContentPresenter Content="{StaticResource Kompass}"/>
</Viewbox>
</Grid>
</ScrollViewer>
</Grid>
</Window>
Zooming and dragging is managed by the code-behind:
public partial class MainWindow : Window
{
Point? lastCenterPositionOnTarget;
Point? lastMousePositionOnTarget;
Point? lastDragPoint;
public MainWindow()
{
InitializeComponent();
scrollViewer.ScrollChanged += OnScrollViewerScrollChanged;
scrollViewer.MouseLeftButtonUp += OnMouseLeftButtonUp;
scrollViewer.PreviewMouseLeftButtonUp += OnMouseLeftButtonUp;
scrollViewer.PreviewMouseWheel += OnPreviewMouseWheel;
scrollViewer.PreviewMouseLeftButtonDown += OnMouseLeftButtonDown;
scrollViewer.MouseMove += OnMouseMove;
slider.ValueChanged += OnSliderValueChanged;
}
void OnMouseMove(object sender, MouseEventArgs e)
{
if (lastDragPoint.HasValue)
{
Point posNow = e.GetPosition(scrollViewer);
double dX = posNow.X - lastDragPoint.Value.X;
double dY = posNow.Y - lastDragPoint.Value.Y;
lastDragPoint = posNow;
scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset - dX);
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - dY);
}
}
void OnMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var mousePos = e.GetPosition(scrollViewer);
if (mousePos.X <= scrollViewer.ViewportWidth && mousePos.Y <
scrollViewer.ViewportHeight) {
scrollViewer.Cursor = Cursors.SizeAll;
lastDragPoint = mousePos;
Mouse.Capture(scrollViewer);
}
}
void OnPreviewMouseWheel(object sender, MouseWheelEventArgs e)
{
lastMousePositionOnTarget = Mouse.GetPosition(grid);
if (e.Delta > 0)
{
slider.Value += 1;
}
if (e.Delta < 0)
{
slider.Value -= 1;
}
e.Handled = true;
}
void OnMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
scrollViewer.Cursor = Cursors.Arrow;
scrollViewer.ReleaseMouseCapture();
lastDragPoint = null;
}
void OnSliderValueChanged(object sender,
RoutedPropertyChangedEventArgs<double> e)
{
scaleTransform.ScaleX = e.NewValue;
scaleTransform.ScaleY = e.NewValue;
var centerOfViewport = new Point(scrollViewer.ViewportWidth/2,
scrollViewer.ViewportHeight/2);
lastCenterPositionOnTarget = scrollViewer.TranslatePoint(centerOfViewport, grid);
}
void OnScrollViewerScrollChanged(object sender, ScrollChangedEventArgs e)
{
if (e.ExtentHeightChange != 0 || e.ExtentWidthChange != 0)
{
Point? targetBefore = null;
Point? targetNow = null;
if (!lastMousePositionOnTarget.HasValue)
{
if (lastCenterPositionOnTarget.HasValue)
{
var centerOfViewport = new Point(scrollViewer.ViewportWidth/2,
scrollViewer.ViewportHeight/2);
Point centerOfTargetNow =
scrollViewer.TranslatePoint(centerOfViewport, grid);
targetBefore = lastCenterPositionOnTarget;
targetNow = centerOfTargetNow;
}
}
else
{
targetBefore = lastMousePositionOnTarget;
targetNow = Mouse.GetPosition(grid);
lastMousePositionOnTarget = null;
}
if (targetBefore.HasValue)
{
double dXInTargetPixels = targetNow.Value.X - targetBefore.Value.X;
double dYInTargetPixels = targetNow.Value.Y - targetBefore.Value.Y;
double multiplicatorX = e.ExtentWidth/grid.Width;
double multiplicatorY = e.ExtentHeight/grid.Height;
double newOffsetX = scrollViewer.HorizontalOffset -
dXInTargetPixels*multiplicatorX;
double newOffsetY = scrollViewer.VerticalOffset -
dYInTargetPixels*multiplicatorY;
if (double.IsNaN(newOffsetX) || double.IsNaN(newOffsetY))
{
return;
}
scrollViewer.ScrollToHorizontalOffset(newOffsetX);
scrollViewer.ScrollToVerticalOffset(newOffsetY);
}
}
}
}