WPF 中实现Canvas的缩放和平移(一)

2021-11-06  乐帮网

wpf

首先说明示例代码的环境:.Net Core WPF + Net 5(.Net Core 3 同样适用) 最后会提供源码下载。
实现的基本功能:使用鼠标左键按下时拖动,画布跟随拖动。 实现缩放,但是是通过右侧的按钮来控制缩放的中心点和缩放的比例(1为原始大小)
为什么我没有实现通过鼠标滚轮来缩放呢?这个并不是我没有尝试,只是发现存在一定问题,主要是缩放中心点的问题在MouseOver过程中并不稳定,体验并不好。所以就放弃了,想要完美实现请查看我的下一篇文章。

实现的主要逻辑:通过定义两个Transform分别负责平移和缩放。这里没有从零开始写,直接使用了别人的代码见:https://github.com/mdrabick/StiZoomableCanvas
我猜你一定很想知道遇到什么问题,其实还是缩放过程中中心点计算并不稳定。
以下是我的源码:

<Grid x:Name="grid">
        <Grid.ColumnDefinitions>
            <ColumnDefinition Width="*"></ColumnDefinition>
            <ColumnDefinition Width="200"></ColumnDefinition>
        </Grid.ColumnDefinitions>
        <StackPanel Grid.Row="0" Grid.Column="1" Background="LightSlateGray">
            <StackPanel.Resources>
                <Style TargetType="{x:Type FrameworkElement}">
                    <Setter Property="Margin" Value="0,10,0,0"/>
                </Style>
                <Style TargetType="{x:Type Label}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />
                <Style TargetType="{x:Type TextBox}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />
                <Style TargetType="{x:Type Button}" BasedOn="{StaticResource {x:Type FrameworkElement}}" />
            </StackPanel.Resources>
            <Label>在这里设置中心点</Label>
            <TextBox x:Name="centerXInput" Text="400"/>
            <TextBox  x:Name="centerYInput" Text="400"/>
            <TextBox x:Name="scaleInput" Text="1.2"/>
            <Button Click="Button_Click" Content="点击缩放"></Button>
        </StackPanel>
        <ScrollViewer Name="scroll" Grid.Row="0" Grid.Column="0" HorizontalScrollBarVisibility="Hidden" VerticalScrollBarVisibility="Hidden" >
            <Controls:StiZoomableCanvas Margin="0" Loaded="CanvasLoaded"  x:Name="zoomCanvas" ApplyTransform="True" >
                <Rectangle Width="40000" Height="40000" Fill="LightGray" Canvas.Left="-2000" Canvas.Top="-2000" />
                <Ellipse Canvas.Left="-5" Canvas.Top="-5" Fill="Red" Width="10" Height="10"/>
                <Ellipse Canvas.Left="500" Canvas.Top="200" Fill="Purple" Width="100" Height="100"/>
                <Rectangle Width="300" Height="500" Fill="Gold" Canvas.Top="100" Canvas.Left="100"></Rectangle>
            </Controls:StiZoomableCanvas>

        </ScrollViewer>
    </Grid>

后台代码:

public partial class MainWindow : Window
    {
        private Point _mouseOverPoint;

        private Point _mouseDownPoint;

        private Point _canvasPoint;


        public MainWindow()
        {
            InitializeComponent();
            zoomCanvas.AddHandler(Button.MouseLeftButtonDownEvent, new MouseButtonEventHandler(CanvasMouseLeftButtonDown), true);
            zoomCanvas.AddHandler(Button.MouseMoveEvent, new MouseEventHandler(CanvasMouseMove), true);
            zoomCanvas.AddHandler(Button.MouseLeftButtonUpEvent, new MouseButtonEventHandler(CanvasMouseLeftButtonUp), true);
        }
        private void Button_Click(object sender, RoutedEventArgs e)
        {
            var group = zoomCanvas.TransformGroup;
            double scale = 1, centerX, centerY;
            double.TryParse(scaleInput.Text, out scale);
            double.TryParse(centerXInput.Text, out centerX);
            double.TryParse(centerYInput.Text, out centerY);

            ScaleTransform transform = group.Children[0] as ScaleTransform;
            transform.CenterX = centerX;
            transform.CenterY = centerY;

            zoomCanvas.Scale = scale;
            //zoomCanvas.Offset = new Point(zoomCanvas.Width / 2, zoomCanvas.Height / 2);
        }

        private void CanvasMouseMove(object sender, MouseEventArgs e)
        {
            StiZoomableCanvas canvas = sender as StiZoomableCanvas;
            //_mouseOverPoint =  e.GetPosition(canvas);
            if (e.LeftButton == MouseButtonState.Pressed)
            {
                Point point = e.GetPosition(grid);
                double dx = _mouseDownPoint.X + point.X - _canvasPoint.X;
                double dy = _mouseDownPoint.Y + point.Y - _canvasPoint.Y;
                //canvas.Margin = new Thickness(dx, dy, 0, 0);
                canvas.Offset = new Point(-dx, -dy);
            }
        }

        private void CanvasMouseWheel(object sender, MouseWheelEventArgs e)
        {
            StiZoomableCanvas canvas = sender as StiZoomableCanvas;
            double scale = canvas.Scale;
            if (e.Delta > 0)
                scale += Math.Abs(scale) * 0.03;
            else
                scale -= Math.Abs(scale) * 0.03;
            if (scale < 0.2)
                return;


            //var group = canvas.TransformGroup;
            //Point pointToContent = group.Inverse.Transform(_mouseOverPoint);
            //Debug.Assert(group != null, "hello current control");
            //ScaleTransform transform = group.Children[0] as ScaleTransform;
            //if (transform.ScaleX + scale < 0.2)
            //{
            //    return;
            //}
            //transform.ScaleX += scale;
            //transform.ScaleY += scale;
            //TranslateTransform transform1 = group.Children[1] as TranslateTransform;
            //transform1.X = -1 * ((pointToContent.X * transform.ScaleX) - _mouseOverPoint.X);
            //transform1.Y = -1 * ((pointToContent.Y * transform.ScaleY) - _mouseOverPoint.Y);

        }

        private void CanvasMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            StiZoomableCanvas canvas = sender as StiZoomableCanvas;
            if (canvas != null)
            {
                _mouseDownPoint = new Point()
                {
                    X = -canvas.Offset.X,
                    Y = -canvas.Offset.Y
                };
                _canvasPoint = e.GetPosition(grid);
                canvas.CaptureMouse();
                canvas.Cursor = Cursors.SizeAll;
            }

        }

        private void CanvasMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            StiZoomableCanvas canvas = sender as StiZoomableCanvas;
            canvas.ReleaseMouseCapture();
            canvas.Cursor = Cursors.Arrow;
        }

        private void CanvasLoaded(object sender, RoutedEventArgs e)
        {
           
        }

    }

源码地址:链接:https://pan.baidu.com/s/172TE8tn1bBFzUyIz8wt9bA 

 

公众号二维码

关注我的微信公众号
在公众号里留言交流
投稿邮箱:1052839972@qq.com

庭院深深深几许?杨柳堆烟,帘幕无重数。
玉勒雕鞍游冶处,楼高不见章台路。
雨横风狂三月暮。门掩黄昏,无计留春住。
泪眼问花花不语,乱红飞过秋千去。

欧阳修

付款二维码

如果感觉对您有帮助
欢迎向作者提供捐赠
这将是创作的最大动力