WPF 简单的缩放和平移支持在 ScrollViewer 中






4.98/5 (59投票s)
一个示例,描述了一种使用鼠标滚轮或滑块进行缩放,以及拖动由 ScrollViewer 托管的有限内容的方法。
引言
我一直在寻找开源解决方案,展示一种在 ScrollViewer 中托管和管理的可任意内容进行简单缩放和平移的方法。
由于我没有找到免费的解决方案,我决定编写自己的解决方案并与大家分享。它支持通过滑块以及鼠标滚轮进行缩放。
使用代码
如果您对使用方法有任何疑问,请随时提问,我会尽快回复您。
主视图由下面的 XAML 定义。要缩放和平移的内容是“grid”控件的一部分。
<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>
缩放和平移由代码隐藏文件管理
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) //make sure we still can use the scrollbars
{
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);
}
}
}
}