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 { /// /// 图像ROI画布控件,支持图像显示、ROI编辑、缩放和平移 /// 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), typeof(ImageROICanvas), new PropertyMetadata(null)); public ObservableCollection? ROIItems { get => (ObservableCollection?)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 } /// /// 画布点击事件参数 /// public class CanvasClickedEventArgs : RoutedEventArgs { public Point Position { get; } public CanvasClickedEventArgs(RoutedEvent routedEvent, Point position) : base(routedEvent) { Position = position; } } }