近期项目需要用WPF绘制,要求能实现缩放、平移以及图元的单独移动,图元还要求支持选取、双击等操作,参考GitHub - SEilers/WpfPanAndZoom: A panning and zooming canvas for WPF.以及相关文章,记录下实现过程。

缩放操作:使用滚轮实现缩放

平移操作:按住鼠标右键,实现画布的平移;鼠标左键按住画布上的图元,实现图元的平移

复位操作:重置画布比例和位置

1、xaml文件

x:Class="WpfPanAndZoom.CustomControls.PanAndZoomCanvas"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:local="clr-namespace:WpfPanAndZoom.CustomControls"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

d:DesignHeight="450"

d:DesignWidth="800"

Background="#333333"

mc:Ignorable="d">

2、cs文件,

using Newtonsoft.Json.Linq;

using System;

using System.Collections.Generic;

using System.Linq;

using System.Reflection;

using System.Text;

using System.Threading.Tasks;

using System.Windows;

using System.Windows.Controls;

using System.Windows.Data;

using System.Windows.Documents;

using System.Windows.Input;

using System.Windows.Media;

using System.Windows.Media.Imaging;

using System.Windows.Media.Media3D;

using System.Windows.Navigation;

using System.Windows.Shapes;

using TrainControl.Sharp;

namespace WpfPanAndZoom.CustomControls

{

///

/// Interaktionslogik für PanAndZoomCanvas.xaml

/// https://stackoverflow.com/questions/35165349/how-to-drag-rendertransform-with-mouse-in-wpf

///

public partial class PanAndZoomCanvas : Canvas

{

#region Variables

private readonly MatrixTransform _transform = new MatrixTransform();

private Point _initialMousePosition;

private bool _dragging;

private UIElement _selectedElement;

private Vector _draggingDelta;

private Color _backgroundColor = Colors.Black;

private List _gridLines = new List();

#endregion

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

public PanAndZoomCanvas()

{

InitializeComponent();

MouseDown += PanAndZoomCanvas_MouseDown;

MouseUp += PanAndZoomCanvas_MouseUp;

MouseMove += PanAndZoomCanvas_MouseMove;

MouseWheel += PanAndZoomCanvas_MouseWheel;

BackgroundColor = _backgroundColor;

}

public Color BackgroundColor

{

get { return _backgroundColor; }

set

{

_backgroundColor = value;

Background = new SolidColorBrush(_backgroundColor);

}

}

#region 平移、缩放和复位操作

///

/// 新增复位操作

///

public void Reset()

{

Zoomfactor = 1.1f

foreach (UIElement child in this.Children)

{

child.RenderTransform = Transform.Identity;

}

}

private void PanAndZoomCanvas_MouseDown(object sender, MouseButtonEventArgs e)

{

if (e.ChangedButton == MouseButton.Right)

{

_initialMousePosition = _transform.Inverse.Transform(e.GetPosition(this));

}

if (e.ChangedButton == MouseButton.Left)

{

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;

}

}

private void PanAndZoomCanvas_MouseUp(object sender, MouseButtonEventArgs e)

{

_dragging = false;

_selectedElement = null;

}

private void PanAndZoomCanvas_MouseMove(object sender, MouseEventArgs e)

{

if (e.RightButton == 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;

}

}

if (_dragging && e.LeftButton == MouseButtonState.Pressed)

{

double x = Mouse.GetPosition(this).X;

double y = Mouse.GetPosition(this).Y;

if (_selectedElement != null)

{

Canvas.SetLeft(_selectedElement, x + _draggingDelta.X);

Canvas.SetTop(_selectedElement, y + _draggingDelta.Y);

}

}

}

private void PanAndZoomCanvas_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 = Canvas.GetLeft(child);

double y = Canvas.GetTop(child);

double sx = x * scaleFactor;

double sy = y * scaleFactor;

Canvas.SetLeft(child, sx);

Canvas.SetTop(child, sy);

child.RenderTransform = _transform;

}

}

#endregion

}

}

图元的移动,使用Canvas的SerLeft和SetTop方法实现

3、使用

x:Class="TrainControl.MainWindow"

xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

xmlns:CustomControls="clr-namespace:WpfPanAndZoom.CustomControls"

xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

xmlns:local="clr-namespace:TrainControl"

xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

Title="MainWindow"

Width="1024"

Height="768"

mc:Ignorable="d">

x:Name="canvas"

HorizontalAlignment="Stretch"

VerticalAlignment="Stretch">

Name="btnReset"

Margin="20,87,0,0"

HorizontalAlignment="Left"

VerticalAlignment="Top"

Click="btnReset_Click"

Content="复位" />

public partial class MainWindow : Window

{

public MainWindow()

{

InitializeComponent();

}

private void Window_Loaded(object sender, RoutedEventArgs e)

{

RegenGeometry();

}

///

/// Canvas复位

///

///

///

private void btnReset_Click(object sender, RoutedEventArgs e)

{

canvas.Reset();

}

///

/// 绘制图元

///

private void RegenGeometry()

{

canvas.Children.Clear();

// DrawVisual方式绘制文本的时候,需要用到

double dpi = VisualTreeHelper.GetDpi(this).PixelsPerDip;

TrainSingalService.Instance.Regen(canvas, dpi);

}

}

下一篇再描述TrainSingalService.Instance.Regen(canvas, dpi)函数的实现

相关文章

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。