using System; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Shapes; using TeamAAS_VP.ViewModels; namespace TeamAAS_VP.Views { public partial class ImageView : UserControl { private Point _start; private Shape _previewShape; private bool _isDown; private UIElement _dragTarget; private Point _dragOffset; // 新增:记录当前选中的批注元素 private UIElement _selectedAnnotation; public ImageView() { InitializeComponent(); Loaded += ImageView_Loaded; } private void ImageView_Loaded(object sender, RoutedEventArgs e) { var vm = DataContext as ImageViewModel; vm?.AttachRenderTarget(RootPixelRender, AnnoCanvas); vm?.LoadLast(); // 让 KeyDown 可用 AnnoCanvas.Focus(); } private ImageViewModel Vm => DataContext as ImageViewModel; private Point GetPixelPoint(MouseEventArgs e) { var p = e.GetPosition(AnnoCanvas); var vm = Vm; if (vm == null || vm.PixelWidth <= 0 || vm.PixelHeight <= 0) { return p; } var actualW = AnnoCanvas.ActualWidth; var actualH = AnnoCanvas.ActualHeight; if (actualW <= 0 || actualH <= 0) return p; var scaleX = actualW / vm.PixelWidth; var scaleY = actualH / vm.PixelHeight; if (scaleX <= 0) scaleX = 1; if (scaleY <= 0) scaleY = 1; return new Point(p.X / scaleX, p.Y / scaleY); } private UIElement GetAnnotationElementFromOriginalSource(object originalSource) { // 右键/左键命中的可能是:Rectangle/TextBlock/Grid/Line/Polygon 等子元素 var element = originalSource as DependencyObject; while (element != null) { if (element is Rectangle) return (UIElement)element; if (element is TextBlock) return (UIElement)element; if (element is Grid) return (UIElement)element; // 命中箭头内部:Line/Polygon -> 往上找 Grid element = System.Windows.Media.VisualTreeHelper.GetParent(element); } return null; } private void SelectAnnotation(UIElement element) { _selectedAnnotation = element; // 简单选中效果(可选):给选中项加虚线描边 // 只处理 Rectangle / Grid(箭头) 的视觉效果;TextBlock 不做边框避免影响显示 foreach (var child in AnnoCanvas.Children) { if (child is Rectangle rr) { rr.StrokeDashArray = null; } } if (_selectedAnnotation is Rectangle r) { r.StrokeDashArray = new System.Windows.Media.DoubleCollection() { 2, 2 }; } } private void DeleteSelectedAnnotation() { if (Vm == null) return; if (_selectedAnnotation == null) return; Vm.RemoveAnnotation(_selectedAnnotation); _selectedAnnotation = null; } private void AnnoCanvas_MouseRightButtonDown(object sender, MouseButtonEventArgs e) { if (Vm == null) return; var hit = GetAnnotationElementFromOriginalSource(e.OriginalSource); if (hit == null || hit == AnnoCanvas) { _selectedAnnotation = null; return; } SelectAnnotation(hit); AnnoCanvas.Focus(); var menu = new ContextMenu(); var deleteItem = new MenuItem() { Header = "删除" }; deleteItem.Click += (s, ev) => DeleteSelectedAnnotation(); menu.Items.Add(deleteItem); // 打开菜单 menu.PlacementTarget = AnnoCanvas; menu.IsOpen = true; e.Handled = true; } private void AnnoCanvas_KeyDown(object sender, KeyEventArgs e) { if (e.Key == Key.Delete) { DeleteSelectedAnnotation(); e.Handled = true; } } private void AnnoCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { if (Vm == null) return; _isDown = true; _start = GetPixelPoint(e); AnnoCanvas.CaptureMouse(); // Select 模式:左键点中即选中 if (Vm.Tool == "Select") { var hit = GetAnnotationElementFromOriginalSource(e.OriginalSource); if (hit != null && hit != AnnoCanvas) { SelectAnnotation(hit); _dragTarget = hit; var p = _start; var left = Canvas.GetLeft(_dragTarget); var top = Canvas.GetTop(_dragTarget); if (double.IsNaN(left)) left = 0; if (double.IsNaN(top)) top = 0; _dragOffset = new Point(p.X - left, p.Y - top); } else { _selectedAnnotation = null; _dragTarget = null; } return; } if (Vm.Tool == "Rect") { var rect = Vm.CreateRect(); Canvas.SetLeft(rect, _start.X); Canvas.SetTop(rect, _start.Y); rect.Width = 1; rect.Height = 1; _previewShape = rect; AnnoCanvas.Children.Add(rect); return; } if (Vm.Tool == "Arrow") { var g = Vm.CreateArrowGroup(_start, _start); AnnoCanvas.Children.Add(g); _previewShape = g.Tag as Shape; return; } if (Vm.Tool == "Text") { Vm.CreateTextAt(AnnoCanvas, _start); _isDown = false; AnnoCanvas.ReleaseMouseCapture(); } } private void AnnoCanvas_MouseMove(object sender, MouseEventArgs e) { if (!_isDown || Vm == null) return; var p = GetPixelPoint(e); if (Vm.Tool == "Select") { if (_dragTarget != null && _dragTarget != AnnoCanvas && e.LeftButton == MouseButtonState.Pressed) { Canvas.SetLeft(_dragTarget, p.X - _dragOffset.X); Canvas.SetTop(_dragTarget, p.Y - _dragOffset.Y); } return; } if (Vm.Tool == "Rect" && _previewShape is Rectangle r) { var x = Math.Min(p.X, _start.X); var y = Math.Min(p.Y, _start.Y); var w = Math.Abs(p.X - _start.X); var h = Math.Abs(p.Y - _start.Y); Canvas.SetLeft(r, x); Canvas.SetTop(r, y); r.Width = Math.Max(1, w); r.Height = Math.Max(1, h); return; } if (Vm.Tool == "Arrow") { Vm.UpdateLastArrow(AnnoCanvas, p); } } private void AnnoCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) { if (Vm == null) return; _isDown = false; AnnoCanvas.ReleaseMouseCapture(); var p = GetPixelPoint(e); if (Vm.Tool == "Arrow") { Vm.CommitLastArrow(AnnoCanvas, p); } _previewShape = null; _dragTarget = null; } } }