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

2021-11-07  乐帮网

wpf

首先说明示例代码的环境:.Net Core WPF + Net 5(.Net Core 3 同样适用) 最后会提供源码下载。
实现的基本功能:使用鼠标左键按下时拖动,画布跟随拖动。 本篇和上一篇是同一系列,实现的原理是不同的,基本思想是画布本身不用缩放和平移,主要针对子元素进行变换,使用矩阵记忆和运算就挺方便的。代码主要来源:https://github.com/SEilers/WpfPanAndZoom

以下是控件的源码:

<Canvas x:Class="vkt.demo.canvas.mouse.CustomControls.ZoomCanvas"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" 
             xmlns:d="http://schemas.microsoft.com/expression/blend/2008" 
             xmlns:local="clr-namespace:vkt.demo.canvas.mouse.CustomControls"
             mc:Ignorable="d" 
             d:DesignHeight="450" d:DesignWidth="800" Background="LightGray">
</Canvas>

后台代码:

 /// <summary>
    /// ZoomCanvas.xaml 的交互逻辑
    /// https://stackoverflow.com/questions/35165349/how-to-drag-rendertransform-with-mouse-in-wpf
    /// </summary>
    public partial class ZoomCanvas : Canvas
    {

        #region Variables
        private readonly MatrixTransform _transform = new MatrixTransform();
        private Point _initialMousePosition;

        private bool _dragging;
        private UIElement _selectedElement;
        private Vector _draggingDelta;

        private Color _lineColor = Color.FromArgb(0xFF, 0xF0, 0xF0, 0xF0);
        private Color _backgroundColor = Color.FromArgb(0xFF, 0xE8, 0xE7, 0xE6);
        private List<Line> _gridLines = new List<Line>();


        #endregion


        public ZoomCanvas()
        {
            InitializeComponent();
            MouseDown += ZoomCanvas_MouseDown;
            MouseUp += ZoomCanvas_MouseUp;
            MouseMove += ZoomCanvas_MouseMove;
            MouseWheel += ZoomCanvas_MouseWheel;

            BackgroundColor = _backgroundColor;

            // draw lines
            //for (int x = -40000; x <= 40000; x += 10)
            //{
            //    Line verticalLine = new Line
            //    {
            //        Stroke = new SolidColorBrush(_lineColor),
            //        X1 = x,
            //        Y1 = -40000,
            //        X2 = x,
            //        Y2 = 40000
            //    };

            //    if (x % 1000 == 0)
            //    {
            //        verticalLine.StrokeThickness = 2;
            //    }
            //    else
            //    {
            //        verticalLine.StrokeThickness = 1;
            //    }

            //    Children.Add(verticalLine);
            //    _gridLines.Add(verticalLine);
            //}

            //for (int y = -40000; y <= 40000; y += 10)
            //{
            //    Line horizontalLine = new Line
            //    {
            //        Stroke = new SolidColorBrush(_lineColor),
            //        X1 = -40000,
            //        Y1 = y,
            //        X2 = 40000,
            //        Y2 = y
            //    };

            //    if (y % 1000 == 0)
            //    {
            //        horizontalLine.StrokeThickness = 2;
            //    }
            //    else
            //    {
            //        horizontalLine.StrokeThickness = 1;
            //    }

            //    Children.Add(horizontalLine);
            //    _gridLines.Add(horizontalLine);
            //}
        }


        public float Zoomfactor { get; set; } = 1.1f;

        public Color LineColor
        {
            get { return _lineColor; }

            set
            {
                _lineColor = value;

                foreach (Line line in _gridLines)
                {
                    line.Stroke = new SolidColorBrush(_lineColor);
                }
            }
        }
        public Color BackgroundColor
        {
            get { return _backgroundColor; }

            set
            {
                _backgroundColor = value;
                Background = new SolidColorBrush(_backgroundColor);
            }
        }

        public void SetGridVisibility(Visibility value)
        {
            foreach (Line line in _gridLines)
            {
                line.Visibility = value;
            }
        }



        private void ZoomCanvas_MouseDown(object sender, MouseButtonEventArgs e)
        {
            if (e.ChangedButton == MouseButton.Right)
            {


                if (this.Children.Contains((UIElement)e.Source))
                {
                    _selectedElement = (UIElement)e.Source;
                    Point mousePosition = Mouse.GetPosition(this);
                    double x = Canvas.GetLeft(_selectedElement);
                    double y = Canvas.GetTop(_selectedElement);
                    Point elementPosition = new Point(x, y);
                    _draggingDelta = elementPosition - mousePosition;
                }
                _dragging = true;
            }
            else if (e.ChangedButton == MouseButton.Left)
            {
                _initialMousePosition = _transform.Inverse.Transform(e.GetPosition(this));
            }
        }

        private void ZoomCanvas_MouseUp(object sender, MouseButtonEventArgs e)
        {
            _dragging = false;
            _selectedElement = null;
        }

        private void ZoomCanvas_MouseMove(object sender, MouseEventArgs e)
        {
            //if (_dragging && e.RightButton == MouseButtonState.Pressed)
            //{
            //    double x = Mouse.GetPosition(this).X;
            //    double y = Mouse.GetPosition(this).Y;

            //    if (_selectedElement != null)
            //    {
            //        SetLeft(_selectedElement, x + _draggingDelta.X);
            //        SetTop(_selectedElement, y + _draggingDelta.Y);
            //    }
            //}
             if (e.LeftButton == MouseButtonState.Pressed)
            {
                Point mousePosition = _transform.Inverse.Transform(e.GetPosition(this));
                Vector delta = Point.Subtract(mousePosition, _initialMousePosition);
                var translate = new TranslateTransform(delta.X, delta.Y);
                _transform.Matrix = translate.Value * _transform.Matrix;

                foreach (UIElement child in this.Children)
                {
                    child.RenderTransform = _transform;
                }
            }
        }

        private void ZoomCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
        {
            float scaleFactor = Zoomfactor;
            if (e.Delta < 0)
            {
                scaleFactor = 1f / scaleFactor;
            }

            Point mousePostion = e.GetPosition(this);

            Matrix scaleMatrix = _transform.Matrix;
            scaleMatrix.ScaleAt(scaleFactor, scaleFactor, mousePostion.X, mousePostion.Y);
            _transform.Matrix = scaleMatrix;

            foreach (UIElement child in this.Children)
            {
                double x = GetLeft(child);
                double y = GetTop(child);

                double sx = x * scaleFactor;
                double sy = y * scaleFactor;

                SetLeft(child, sx);
                SetTop(child, sy);

                child.RenderTransform = _transform;
            }
        }



    }

提示:画太多线会有性能问题,但正常使用场景不受影响。

使用过程:

 <Grid VerticalAlignment="Stretch" HorizontalAlignment="Stretch">
        <CustomControls:PanAndZoomCanvas x:Name="canvas"  HorizontalAlignment="Stretch"  VerticalAlignment="Stretch" />
    </Grid>

源码下载:链接:https://pan.baidu.com/s/1V6cL7boUMQ1G23VG3KtD-Q 

 

公众号二维码

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

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

欧阳修

付款二维码

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