270 lines
8.5 KiB
C#
270 lines
8.5 KiB
C#
using XP.ImageProcessing.RoiControl.Models;
|
|
using System.Collections.ObjectModel;
|
|
using System.Windows;
|
|
using System.Windows.Controls;
|
|
using System.Windows.Input;
|
|
using System.Windows.Media;
|
|
using System.Windows.Media.Imaging;
|
|
|
|
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;
|
|
}
|
|
}
|
|
} |