十字辅助线

This commit is contained in:
李伟
2026-04-24 08:31:21 +08:00
parent c2650e737b
commit 7a0731386e
8 changed files with 120 additions and 1 deletions
@@ -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": {
@@ -18,4 +18,9 @@ namespace XplorePlane.Events
/// 测量工具激活事件,由 MainViewModel 发布,ViewportPanelViewModel 订阅
/// </summary>
public class MeasurementToolEvent : PubSubEvent<MeasurementToolMode> { }
/// <summary>
/// 十字辅助线切换事件
/// </summary>
public class ToggleCrosshairEvent : PubSubEvent { }
}
@@ -81,6 +81,9 @@ namespace XplorePlane.ViewModels
public DelegateCommand AngleMeasureCommand { get; }
public DelegateCommand ThroughHoleFillRateMeasureCommand { get; }
// 辅助线命令
public DelegateCommand ToggleCrosshairCommand { get; }
// 设置命令
public DelegateCommand OpenLanguageSwitcherCommand { get; }
public DelegateCommand OpenRealTimeLogViewerCommand { get; }
@@ -164,6 +167,10 @@ namespace XplorePlane.ViewModels
AngleMeasureCommand = new DelegateCommand(ExecuteAngleMeasure);
ThroughHoleFillRateMeasureCommand = new DelegateCommand(ExecuteThroughHoleFillRateMeasure);
// 辅助线命令
ToggleCrosshairCommand = new DelegateCommand(() =>
_eventAggregator.GetEvent<ToggleCrosshairEvent>().Publish());
// 硬件命令
AxisResetCommand = new DelegateCommand(ExecuteAxisReset);
OpenDetectorConfigCommand = new DelegateCommand(ExecuteOpenDetectorConfig);
@@ -154,6 +154,10 @@ namespace XplorePlane.ViewModels
// 订阅测量工具事件
eventAggregator.GetEvent<MeasurementToolEvent>()
.Subscribe(OnMeasurementToolActivated, ThreadOption.UIThread);
// 订阅十字辅助线切换事件
eventAggregator.GetEvent<ToggleCrosshairEvent>()
.Subscribe(OnToggleCrosshair, ThreadOption.UIThread);
}
private void OnMeasurementToolActivated(MeasurementToolMode mode)
@@ -162,6 +166,23 @@ namespace XplorePlane.ViewModels
_logger?.Info("测量工具模式切换: {Mode}", mode);
}
#region 线
private bool _showCrosshair;
public bool ShowCrosshair
{
get => _showCrosshair;
set => SetProperty(ref _showCrosshair, value);
}
private void OnToggleCrosshair()
{
ShowCrosshair = !ShowCrosshair;
_logger?.Info("十字辅助线: {State}", ShowCrosshair ? "显示" : "隐藏");
}
#endregion
private void OnImageCaptured(ImageCapturedEventArgs args)
{
if (args?.ImageData == null || args.Width == 0 || args.Height == 0) return;
+16
View File
@@ -0,0 +1,16 @@
using System;
using System.Globalization;
using System.Windows.Data;
namespace XplorePlane.Views
{
/// <summary>返回输入值的一半,用于十字线居中定位</summary>
public class HalfValueConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
=> value is double d ? d / 2.0 : 0.0;
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
=> throw new NotSupportedException();
}
}
+1
View File
@@ -146,6 +146,7 @@
<StackPanel>
<telerik:RadRibbonButton
telerik:ScreenTip.Title="中心十字线"
Command="{Binding ToggleCrosshairCommand}"
Size="Medium"
SmallImage="/Assets/Icons/crosshair.png"
Text="辅助线" />
@@ -6,12 +6,14 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:prism="http://prismlibrary.com/"
xmlns:roi="clr-namespace:XP.ImageProcessing.RoiControl.Controls;assembly=XP.ImageProcessing.RoiControl"
xmlns:local="clr-namespace:XplorePlane.Views"
prism:ViewModelLocator.AutoWireViewModel="True"
d:DesignHeight="400"
d:DesignWidth="600"
mc:Ignorable="d">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
<local:HalfValueConverter x:Key="HalfConverter" />
</UserControl.Resources>
<Grid Background="#FFFFFF">
<Grid.RowDefinitions>
@@ -55,6 +57,8 @@
</ContextMenu>
</roi:PolygonRoiCanvas.ContextMenu>
</roi:PolygonRoiCanvas>
<!-- 十字辅助线通过代码绘制到 mainCanvas 内部,跟随图像缩放平移 -->
</Grid>
<!-- 图像信息栏 -->
@@ -10,6 +10,7 @@ using System.Windows.Media.Imaging;
using System.Windows.Shapes;
using Microsoft.Win32;
using XP.ImageProcessing.RoiControl.Controls;
using Prism.Ioc;
using XplorePlane.Events;
using XplorePlane.ViewModels;
@@ -65,10 +66,25 @@ namespace XplorePlane.Views
if (e is CanvasClickedEventArgs args)
OnCanvasClicked(args.Position);
};
// 直接订阅 Prism 事件,不依赖 DataContext 的 PropertyChanged
try
{
var ea = Prism.Ioc.ContainerLocator.Current?.Resolve<Prism.Events.IEventAggregator>();
ea?.GetEvent<ToggleCrosshairEvent>().Subscribe(() =>
{
_crosshairVisible = !_crosshairVisible;
ToggleCrosshairOnCanvas(_crosshairVisible);
}, Prism.Events.ThreadOption.UIThread);
}
catch { /* 设计时或容器未初始化时忽略 */ }
}
private bool _crosshairVisible;
private void OnDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
System.Diagnostics.Debug.WriteLine($"[VP] DataContextChanged: old={e.OldValue?.GetType().Name}, new={e.NewValue?.GetType().Name}");
if (e.OldValue is INotifyPropertyChanged oldVm)
oldVm.PropertyChanged -= OnVmPropertyChanged;
if (e.NewValue is INotifyPropertyChanged newVm)
@@ -78,6 +94,7 @@ namespace XplorePlane.Views
private void OnVmPropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (sender is not ViewportPanelViewModel vm) return;
System.Diagnostics.Debug.WriteLine($"[VP] PropertyChanged: {e.PropertyName}");
if (e.PropertyName == nameof(ViewportPanelViewModel.CurrentMeasurementMode))
{
if (vm.CurrentMeasurementMode != MeasurementToolMode.None)
@@ -86,6 +103,10 @@ namespace XplorePlane.Views
else
RemoveOverlay();
}
else if (e.PropertyName == nameof(ViewportPanelViewModel.ShowCrosshair))
{
// 十字线通过直接订阅 ToggleCrosshairEvent 处理
}
}
#region
@@ -324,6 +345,47 @@ namespace XplorePlane.Views
return null;
}
private void UpdateCrosshairLines()
{
// 十字线位置通过 XAML 绑定自动计算
}
private Line _crosshairH, _crosshairV;
private void ToggleCrosshairOnCanvas(bool show)
{
var mainCanvas = FindChildByName<Canvas>(RoiCanvas, "mainCanvas");
System.Diagnostics.Debug.WriteLine($"[VP] ToggleCrosshair show={show}, mainCanvas={mainCanvas != null}, W={RoiCanvas.CanvasWidth}, H={RoiCanvas.CanvasHeight}");
if (mainCanvas == null) return;
if (show)
{
double w = RoiCanvas.CanvasWidth;
double h = RoiCanvas.CanvasHeight;
if (w <= 0 || h <= 0) return;
_crosshairH = new Line
{
X1 = 0, Y1 = h / 2, X2 = w, Y2 = h / 2,
Stroke = Brushes.Red, StrokeThickness = 1, Opacity = 0.7,
IsHitTestVisible = false
};
_crosshairV = new Line
{
X1 = w / 2, Y1 = 0, X2 = w / 2, Y2 = h,
Stroke = Brushes.Red, StrokeThickness = 1, Opacity = 0.7,
IsHitTestVisible = false
};
mainCanvas.Children.Add(_crosshairH);
mainCanvas.Children.Add(_crosshairV);
}
else
{
if (_crosshairH != null) { mainCanvas.Children.Remove(_crosshairH); _crosshairH = null; }
if (_crosshairV != null) { mainCanvas.Children.Remove(_crosshairV); _crosshairV = null; }
}
}
#endregion
#region