diff --git a/XP.Hardware.Detector/bin/Debug/net8.0-windows7.0/XP.Hardware.Detector.deps.json b/XP.Hardware.Detector/bin/Debug/net8.0-windows7.0/XP.Hardware.Detector.deps.json
index 3f740dc..54eff8d 100644
--- a/XP.Hardware.Detector/bin/Debug/net8.0-windows7.0/XP.Hardware.Detector.deps.json
+++ b/XP.Hardware.Detector/bin/Debug/net8.0-windows7.0/XP.Hardware.Detector.deps.json
@@ -1878,7 +1878,10 @@
"Telerik.UI.for.Wpf.NetCore.Xaml": "2024.1.408"
},
"runtime": {
- "XP.Common.dll": {}
+ "XP.Common.dll": {
+ "assemblyVersion": "1.4.16.1",
+ "fileVersion": "1.4.16.1"
+ }
},
"resources": {
"en-US/XP.Common.resources.dll": {
diff --git a/XP.ImageProcessing.RoiControl/Controls/MeasureEventArgs.cs b/XP.ImageProcessing.RoiControl/Controls/MeasureEventArgs.cs
new file mode 100644
index 0000000..7278450
--- /dev/null
+++ b/XP.ImageProcessing.RoiControl/Controls/MeasureEventArgs.cs
@@ -0,0 +1,31 @@
+using System.Windows;
+
+namespace XP.ImageProcessing.RoiControl.Controls
+{
+ /// 测量完成事件参数
+ public class MeasureCompletedEventArgs : RoutedEventArgs
+ {
+ public Point P1 { get; }
+ public Point P2 { get; }
+ public double Distance { get; }
+ public int TotalCount { get; }
+ public string MeasureType { get; set; }
+
+ public MeasureCompletedEventArgs(RoutedEvent routedEvent, Point p1, Point p2, double distance, int totalCount)
+ : base(routedEvent)
+ {
+ P1 = p1; P2 = p2; Distance = distance; TotalCount = totalCount;
+ }
+ }
+
+ /// 测量状态变化事件参数
+ public class MeasureStatusEventArgs : RoutedEventArgs
+ {
+ public string Message { get; }
+
+ public MeasureStatusEventArgs(RoutedEvent routedEvent, string message) : base(routedEvent)
+ {
+ Message = message;
+ }
+ }
+}
diff --git a/XP.ImageProcessing.RoiControl/Controls/PolygonRoiCanvas.xaml b/XP.ImageProcessing.RoiControl/Controls/PolygonRoiCanvas.xaml
index 409d5d4..be035cd 100644
--- a/XP.ImageProcessing.RoiControl/Controls/PolygonRoiCanvas.xaml
+++ b/XP.ImageProcessing.RoiControl/Controls/PolygonRoiCanvas.xaml
@@ -17,35 +17,20 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
+
diff --git a/XplorePlane/Views/Main/ViewportPanelView.xaml.cs b/XplorePlane/Views/Main/ViewportPanelView.xaml.cs
index 7eddbee..bd03651 100644
--- a/XplorePlane/Views/Main/ViewportPanelView.xaml.cs
+++ b/XplorePlane/Views/Main/ViewportPanelView.xaml.cs
@@ -1,4 +1,15 @@
+using System;
+using System.ComponentModel;
+using System.IO;
+using System.Windows;
using System.Windows.Controls;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using Microsoft.Win32;
+using Prism.Ioc;
+using XP.ImageProcessing.RoiControl.Controls;
+using XplorePlane.Events;
+using XplorePlane.ViewModels;
namespace XplorePlane.Views
{
@@ -7,6 +18,137 @@ namespace XplorePlane.Views
public ViewportPanelView()
{
InitializeComponent();
+ DataContextChanged += OnDataContextChanged;
+
+ // 测量事件 → 更新状态栏
+ RoiCanvas.MeasureCompleted += (s, e) =>
+ {
+ if (e is MeasureCompletedEventArgs args && DataContext is ViewportPanelViewModel vm)
+ {
+ vm.MeasurementResult = $"{args.Distance:F2} px";
+ string typeLabel = args.MeasureType == "PointToLine" ? "点线距" : "点点距";
+ vm.ImageInfo = $"{typeLabel}: {args.Distance:F2} px | ({args.P1.X:F0},{args.P1.Y:F0}) → ({args.P2.X:F0},{args.P2.Y:F0}) | 共 {args.TotalCount} 条测量";
+ }
+ };
+ RoiCanvas.MeasureStatusChanged += (s, e) =>
+ {
+ if (e is MeasureStatusEventArgs args && DataContext is ViewportPanelViewModel vm)
+ vm.ImageInfo = args.Message;
+ };
+
+ // 十字辅助线:直接订阅 Prism 事件
+ try
+ {
+ var ea = ContainerLocator.Current?.Resolve();
+ ea?.GetEvent().Subscribe(() =>
+ {
+ RoiCanvas.ShowCrosshair = !RoiCanvas.ShowCrosshair;
+ }, Prism.Events.ThreadOption.UIThread);
+
+ // 测量模式:直接订阅 Prism 事件
+ ea?.GetEvent().Subscribe(mode =>
+ {
+ RoiCanvas.CurrentMeasureMode = mode switch
+ {
+ MeasurementToolMode.PointDistance => XP.ImageProcessing.RoiControl.Models.MeasureMode.PointDistance,
+ MeasurementToolMode.PointLineDistance => XP.ImageProcessing.RoiControl.Models.MeasureMode.PointToLine,
+ _ => XP.ImageProcessing.RoiControl.Models.MeasureMode.None
+ };
+ }, Prism.Events.ThreadOption.UIThread);
+ }
+ catch { }
+ }
+
+ private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
+ {
+ if (e.OldValue is INotifyPropertyChanged oldVm)
+ oldVm.PropertyChanged -= OnVmPropertyChanged;
+ if (e.NewValue is INotifyPropertyChanged newVm)
+ newVm.PropertyChanged += OnVmPropertyChanged;
+ }
+
+ private void OnVmPropertyChanged(object sender, PropertyChangedEventArgs e)
+ {
+ // 测量模式和十字线通过 Prism 事件直接驱动,不再依赖 PropertyChanged
+ }
+
+ #region 右键菜单
+
+ private void ZoomIn_Click(object sender, RoutedEventArgs e) => RoiCanvas.ZoomScale = Math.Min(10.0, RoiCanvas.ZoomScale * 1.2);
+ private void ZoomOut_Click(object sender, RoutedEventArgs e) => RoiCanvas.ZoomScale = Math.Max(0.1, RoiCanvas.ZoomScale / 1.2);
+ private void ResetView_Click(object sender, RoutedEventArgs e) => RoiCanvas.ResetView();
+
+ private void ClearAllMeasurements_Click(object sender, RoutedEventArgs e)
+ {
+ RoiCanvas.ClearMeasurements();
+ if (DataContext is ViewportPanelViewModel vm)
+ {
+ vm.ResetMeasurementState();
+ vm.ImageInfo = "已清除所有测量";
+ }
+ }
+
+ private void SaveOriginalImage_Click(object sender, RoutedEventArgs e)
+ {
+ if (DataContext is not ViewportPanelViewModel vm || vm.ImageSource is not BitmapSource bitmap)
+ {
+ MessageBox.Show("当前没有可保存的图像", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
+ return;
+ }
+ SaveBitmapToFile(bitmap, "保存原始图像");
+ }
+
+ private void SaveResultImage_Click(object sender, RoutedEventArgs e)
+ {
+ var target = FindChildByName