65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.98/5 (59投票s)

2010 年 7 月 29 日

CPOL
viewsIcon

178489

downloadIcon

10482

一个示例,描述了一种使用鼠标滚轮或滑块进行缩放,以及拖动由 ScrollViewer 托管的有限内容的方法。

WpfZoomAndDragPanel/Overview_Resized.png

引言

我一直在寻找开源解决方案,展示一种在 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);
            }
        }
    }
}
© . All rights reserved.