近期项目需要用WPF绘制,要求能实现缩放、平移以及图元的单独移动,图元还要求支持选取、双击等操作,参考GitHub - SEilers/WpfPanAndZoom: A panning and zooming canvas for WPF.以及相关文章,记录下实现过程。
缩放操作:使用滚轮实现缩放
平移操作:按住鼠标右键,实现画布的平移;鼠标左键按住画布上的图元,实现图元的平移
复位操作:重置画布比例和位置
1、xaml文件
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
#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">
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)函数的实现
相关文章
发表评论