Files
XplorePlane/XP.ImageProcessing.RoiControl/Controls/ImageROICanvas.cs
T
2026-04-13 14:36:18 +08:00

270 lines
8.5 KiB
C#

using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using XP.ImageProcessing.RoiControl.Models;
namespace XP.ImageProcessing.RoiControl.Controls
{
/// <summary>
/// 图像ROI画布控件,支持图像显示、ROI编辑、缩放和平移
/// </summary>
public class ImageROICanvas : Control
{
private Canvas? roiCanvas;
private bool isDragging = false;
private Point mouseDownPoint = new Point();
private const double ZoomStep = 1.2;
private const double MinZoom = 0.1;
private const double MaxZoom = 10.0;
static ImageROICanvas()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(ImageROICanvas),
new FrameworkPropertyMetadata(typeof(ImageROICanvas)));
}
public ImageROICanvas()
{
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
roiCanvas = GetTemplateChild("PART_Canvas") as Canvas;
}
#region Dependency Properties
public static readonly DependencyProperty ImageSourceProperty =
DependencyProperty.Register(nameof(ImageSource), typeof(ImageSource), typeof(ImageROICanvas),
new PropertyMetadata(null, OnImageSourceChanged));
public ImageSource? ImageSource
{
get => (ImageSource?)GetValue(ImageSourceProperty);
set => SetValue(ImageSourceProperty, value);
}
private static void OnImageSourceChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = (ImageROICanvas)d;
if (e.NewValue is BitmapSource bitmap && control.roiCanvas != null)
{
control.ImageWidth = bitmap.PixelWidth;
control.ImageHeight = bitmap.PixelHeight;
}
}
public static readonly DependencyProperty ROIItemsProperty =
DependencyProperty.Register(nameof(ROIItems), typeof(ObservableCollection<ROIShape>), typeof(ImageROICanvas),
new PropertyMetadata(null));
public ObservableCollection<ROIShape>? ROIItems
{
get => (ObservableCollection<ROIShape>?)GetValue(ROIItemsProperty);
set => SetValue(ROIItemsProperty, value);
}
public static readonly DependencyProperty ZoomScaleProperty =
DependencyProperty.Register(nameof(ZoomScale), typeof(double), typeof(ImageROICanvas),
new PropertyMetadata(1.0));
public double ZoomScale
{
get => (double)GetValue(ZoomScaleProperty);
set => SetValue(ZoomScaleProperty, value);
}
public static readonly DependencyProperty ZoomCenterProperty =
DependencyProperty.Register(nameof(ZoomCenter), typeof(Point), typeof(ImageROICanvas),
new PropertyMetadata(new Point()));
public Point ZoomCenter
{
get => (Point)GetValue(ZoomCenterProperty);
set => SetValue(ZoomCenterProperty, value);
}
public static readonly DependencyProperty PanningOffsetXProperty =
DependencyProperty.Register(nameof(PanningOffsetX), typeof(double), typeof(ImageROICanvas),
new PropertyMetadata(0.0));
public double PanningOffsetX
{
get => (double)GetValue(PanningOffsetXProperty);
set => SetValue(PanningOffsetXProperty, value);
}
public static readonly DependencyProperty PanningOffsetYProperty =
DependencyProperty.Register(nameof(PanningOffsetY), typeof(double), typeof(ImageROICanvas),
new PropertyMetadata(0.0));
public double PanningOffsetY
{
get => (double)GetValue(PanningOffsetYProperty);
set => SetValue(PanningOffsetYProperty, value);
}
public static readonly DependencyProperty ImageWidthProperty =
DependencyProperty.Register(nameof(ImageWidth), typeof(double), typeof(ImageROICanvas),
new PropertyMetadata(800.0));
public double ImageWidth
{
get => (double)GetValue(ImageWidthProperty);
set => SetValue(ImageWidthProperty, value);
}
public static readonly DependencyProperty ImageHeightProperty =
DependencyProperty.Register(nameof(ImageHeight), typeof(double), typeof(ImageROICanvas),
new PropertyMetadata(600.0));
public double ImageHeight
{
get => (double)GetValue(ImageHeightProperty);
set => SetValue(ImageHeightProperty, value);
}
public static readonly DependencyProperty SelectedROIProperty =
DependencyProperty.Register(nameof(SelectedROI), typeof(ROIShape), typeof(ImageROICanvas),
new PropertyMetadata(null));
public ROIShape? SelectedROI
{
get => (ROIShape?)GetValue(SelectedROIProperty);
set => SetValue(SelectedROIProperty, value);
}
#endregion Dependency Properties
#region Mouse Events
protected override void OnMouseWheel(MouseWheelEventArgs e)
{
base.OnMouseWheel(e);
Point mousePos = e.GetPosition(this);
if (e.Delta > 0)
{
ZoomIn(mousePos);
}
else
{
ZoomOut(mousePos);
}
e.Handled = true;
}
protected override void OnMouseLeftButtonDown(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonDown(e);
mouseDownPoint = e.GetPosition(this);
isDragging = false;
}
protected override void OnMouseMove(MouseEventArgs e)
{
base.OnMouseMove(e);
if (e.LeftButton == MouseButtonState.Pressed)
{
Point mousePoint = e.GetPosition(this);
double mouseMoveLength = (mousePoint - mouseDownPoint).Length;
if (mouseMoveLength > 10 / ZoomScale)
{
isDragging = true;
PanningOffsetX += mousePoint.X - mouseDownPoint.X;
PanningOffsetY += mousePoint.Y - mouseDownPoint.Y;
mouseDownPoint = mousePoint;
}
}
}
protected override void OnMouseLeftButtonUp(MouseButtonEventArgs e)
{
base.OnMouseLeftButtonUp(e);
if (!isDragging)
{
// 处理点击事件(如添加多边形顶点)
Point clickPoint = e.GetPosition(roiCanvas);
OnCanvasClicked(clickPoint);
}
isDragging = false;
}
#endregion Mouse Events
#region Zoom Methods
public void ZoomIn(Point center)
{
double newZoom = ZoomScale * ZoomStep;
if (newZoom <= MaxZoom)
{
ZoomCenter = center;
ZoomScale = newZoom;
}
}
public void ZoomOut(Point center)
{
double newZoom = ZoomScale / ZoomStep;
if (newZoom >= MinZoom)
{
ZoomCenter = center;
ZoomScale = newZoom;
}
}
public void ResetZoom()
{
ZoomScale = 1.0;
PanningOffsetX = 0;
PanningOffsetY = 0;
ZoomCenter = new Point();
}
#endregion Zoom Methods
#region Events
public static readonly RoutedEvent CanvasClickedEvent =
EventManager.RegisterRoutedEvent(nameof(CanvasClicked), RoutingStrategy.Bubble,
typeof(RoutedEventHandler), typeof(ImageROICanvas));
public event RoutedEventHandler CanvasClicked
{
add { AddHandler(CanvasClickedEvent, value); }
remove { RemoveHandler(CanvasClickedEvent, value); }
}
protected virtual void OnCanvasClicked(Point position)
{
var args = new CanvasClickedEventArgs(CanvasClickedEvent, position);
RaiseEvent(args);
}
#endregion Events
}
/// <summary>
/// 画布点击事件参数
/// </summary>
public class CanvasClickedEventArgs : RoutedEventArgs
{
public Point Position { get; }
public CanvasClickedEventArgs(RoutedEvent routedEvent, Point position) : base(routedEvent)
{
Position = position;
}
}
}