十字辅助线
This commit is contained in:
@@ -1878,7 +1878,10 @@
|
|||||||
"Telerik.UI.for.Wpf.NetCore.Xaml": "2024.1.408"
|
"Telerik.UI.for.Wpf.NetCore.Xaml": "2024.1.408"
|
||||||
},
|
},
|
||||||
"runtime": {
|
"runtime": {
|
||||||
"XP.Common.dll": {}
|
"XP.Common.dll": {
|
||||||
|
"assemblyVersion": "1.4.16.1",
|
||||||
|
"fileVersion": "1.4.16.1"
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"resources": {
|
"resources": {
|
||||||
"en-US/XP.Common.resources.dll": {
|
"en-US/XP.Common.resources.dll": {
|
||||||
|
|||||||
@@ -18,4 +18,9 @@ namespace XplorePlane.Events
|
|||||||
/// 测量工具激活事件,由 MainViewModel 发布,ViewportPanelViewModel 订阅
|
/// 测量工具激活事件,由 MainViewModel 发布,ViewportPanelViewModel 订阅
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class MeasurementToolEvent : PubSubEvent<MeasurementToolMode> { }
|
public class MeasurementToolEvent : PubSubEvent<MeasurementToolMode> { }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 十字辅助线切换事件
|
||||||
|
/// </summary>
|
||||||
|
public class ToggleCrosshairEvent : PubSubEvent { }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -81,6 +81,9 @@ namespace XplorePlane.ViewModels
|
|||||||
public DelegateCommand AngleMeasureCommand { get; }
|
public DelegateCommand AngleMeasureCommand { get; }
|
||||||
public DelegateCommand ThroughHoleFillRateMeasureCommand { get; }
|
public DelegateCommand ThroughHoleFillRateMeasureCommand { get; }
|
||||||
|
|
||||||
|
// 辅助线命令
|
||||||
|
public DelegateCommand ToggleCrosshairCommand { get; }
|
||||||
|
|
||||||
// 设置命令
|
// 设置命令
|
||||||
public DelegateCommand OpenLanguageSwitcherCommand { get; }
|
public DelegateCommand OpenLanguageSwitcherCommand { get; }
|
||||||
public DelegateCommand OpenRealTimeLogViewerCommand { get; }
|
public DelegateCommand OpenRealTimeLogViewerCommand { get; }
|
||||||
@@ -164,6 +167,10 @@ namespace XplorePlane.ViewModels
|
|||||||
AngleMeasureCommand = new DelegateCommand(ExecuteAngleMeasure);
|
AngleMeasureCommand = new DelegateCommand(ExecuteAngleMeasure);
|
||||||
ThroughHoleFillRateMeasureCommand = new DelegateCommand(ExecuteThroughHoleFillRateMeasure);
|
ThroughHoleFillRateMeasureCommand = new DelegateCommand(ExecuteThroughHoleFillRateMeasure);
|
||||||
|
|
||||||
|
// 辅助线命令
|
||||||
|
ToggleCrosshairCommand = new DelegateCommand(() =>
|
||||||
|
_eventAggregator.GetEvent<ToggleCrosshairEvent>().Publish());
|
||||||
|
|
||||||
// 硬件命令
|
// 硬件命令
|
||||||
AxisResetCommand = new DelegateCommand(ExecuteAxisReset);
|
AxisResetCommand = new DelegateCommand(ExecuteAxisReset);
|
||||||
OpenDetectorConfigCommand = new DelegateCommand(ExecuteOpenDetectorConfig);
|
OpenDetectorConfigCommand = new DelegateCommand(ExecuteOpenDetectorConfig);
|
||||||
|
|||||||
@@ -154,6 +154,10 @@ namespace XplorePlane.ViewModels
|
|||||||
// 订阅测量工具事件
|
// 订阅测量工具事件
|
||||||
eventAggregator.GetEvent<MeasurementToolEvent>()
|
eventAggregator.GetEvent<MeasurementToolEvent>()
|
||||||
.Subscribe(OnMeasurementToolActivated, ThreadOption.UIThread);
|
.Subscribe(OnMeasurementToolActivated, ThreadOption.UIThread);
|
||||||
|
|
||||||
|
// 订阅十字辅助线切换事件
|
||||||
|
eventAggregator.GetEvent<ToggleCrosshairEvent>()
|
||||||
|
.Subscribe(OnToggleCrosshair, ThreadOption.UIThread);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMeasurementToolActivated(MeasurementToolMode mode)
|
private void OnMeasurementToolActivated(MeasurementToolMode mode)
|
||||||
@@ -162,6 +166,23 @@ namespace XplorePlane.ViewModels
|
|||||||
_logger?.Info("测量工具模式切换: {Mode}", mode);
|
_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)
|
private void OnImageCaptured(ImageCapturedEventArgs args)
|
||||||
{
|
{
|
||||||
if (args?.ImageData == null || args.Width == 0 || args.Height == 0) return;
|
if (args?.ImageData == null || args.Width == 0 || args.Height == 0) return;
|
||||||
|
|||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -146,6 +146,7 @@
|
|||||||
<StackPanel>
|
<StackPanel>
|
||||||
<telerik:RadRibbonButton
|
<telerik:RadRibbonButton
|
||||||
telerik:ScreenTip.Title="中心十字线"
|
telerik:ScreenTip.Title="中心十字线"
|
||||||
|
Command="{Binding ToggleCrosshairCommand}"
|
||||||
Size="Medium"
|
Size="Medium"
|
||||||
SmallImage="/Assets/Icons/crosshair.png"
|
SmallImage="/Assets/Icons/crosshair.png"
|
||||||
Text="辅助线" />
|
Text="辅助线" />
|
||||||
|
|||||||
@@ -6,12 +6,14 @@
|
|||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:prism="http://prismlibrary.com/"
|
xmlns:prism="http://prismlibrary.com/"
|
||||||
xmlns:roi="clr-namespace:XP.ImageProcessing.RoiControl.Controls;assembly=XP.ImageProcessing.RoiControl"
|
xmlns:roi="clr-namespace:XP.ImageProcessing.RoiControl.Controls;assembly=XP.ImageProcessing.RoiControl"
|
||||||
|
xmlns:local="clr-namespace:XplorePlane.Views"
|
||||||
prism:ViewModelLocator.AutoWireViewModel="True"
|
prism:ViewModelLocator.AutoWireViewModel="True"
|
||||||
d:DesignHeight="400"
|
d:DesignHeight="400"
|
||||||
d:DesignWidth="600"
|
d:DesignWidth="600"
|
||||||
mc:Ignorable="d">
|
mc:Ignorable="d">
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
|
<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />
|
||||||
|
<local:HalfValueConverter x:Key="HalfConverter" />
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
<Grid Background="#FFFFFF">
|
<Grid Background="#FFFFFF">
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
@@ -55,6 +57,8 @@
|
|||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</roi:PolygonRoiCanvas.ContextMenu>
|
</roi:PolygonRoiCanvas.ContextMenu>
|
||||||
</roi:PolygonRoiCanvas>
|
</roi:PolygonRoiCanvas>
|
||||||
|
|
||||||
|
<!-- 十字辅助线通过代码绘制到 mainCanvas 内部,跟随图像缩放平移 -->
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<!-- 图像信息栏 -->
|
<!-- 图像信息栏 -->
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ using System.Windows.Media.Imaging;
|
|||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using Microsoft.Win32;
|
using Microsoft.Win32;
|
||||||
using XP.ImageProcessing.RoiControl.Controls;
|
using XP.ImageProcessing.RoiControl.Controls;
|
||||||
|
using Prism.Ioc;
|
||||||
using XplorePlane.Events;
|
using XplorePlane.Events;
|
||||||
using XplorePlane.ViewModels;
|
using XplorePlane.ViewModels;
|
||||||
|
|
||||||
@@ -65,10 +66,25 @@ namespace XplorePlane.Views
|
|||||||
if (e is CanvasClickedEventArgs args)
|
if (e is CanvasClickedEventArgs args)
|
||||||
OnCanvasClicked(args.Position);
|
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)
|
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)
|
if (e.OldValue is INotifyPropertyChanged oldVm)
|
||||||
oldVm.PropertyChanged -= OnVmPropertyChanged;
|
oldVm.PropertyChanged -= OnVmPropertyChanged;
|
||||||
if (e.NewValue is INotifyPropertyChanged newVm)
|
if (e.NewValue is INotifyPropertyChanged newVm)
|
||||||
@@ -78,6 +94,7 @@ namespace XplorePlane.Views
|
|||||||
private void OnVmPropertyChanged(object sender, PropertyChangedEventArgs e)
|
private void OnVmPropertyChanged(object sender, PropertyChangedEventArgs e)
|
||||||
{
|
{
|
||||||
if (sender is not ViewportPanelViewModel vm) return;
|
if (sender is not ViewportPanelViewModel vm) return;
|
||||||
|
System.Diagnostics.Debug.WriteLine($"[VP] PropertyChanged: {e.PropertyName}");
|
||||||
if (e.PropertyName == nameof(ViewportPanelViewModel.CurrentMeasurementMode))
|
if (e.PropertyName == nameof(ViewportPanelViewModel.CurrentMeasurementMode))
|
||||||
{
|
{
|
||||||
if (vm.CurrentMeasurementMode != MeasurementToolMode.None)
|
if (vm.CurrentMeasurementMode != MeasurementToolMode.None)
|
||||||
@@ -86,6 +103,10 @@ namespace XplorePlane.Views
|
|||||||
else
|
else
|
||||||
RemoveOverlay();
|
RemoveOverlay();
|
||||||
}
|
}
|
||||||
|
else if (e.PropertyName == nameof(ViewportPanelViewModel.ShowCrosshair))
|
||||||
|
{
|
||||||
|
// 十字线通过直接订阅 ToggleCrosshairEvent 处理
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 覆盖层管理
|
#region 覆盖层管理
|
||||||
@@ -324,6 +345,47 @@ namespace XplorePlane.Views
|
|||||||
return null;
|
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
|
#endregion
|
||||||
|
|
||||||
#region 右键菜单
|
#region 右键菜单
|
||||||
|
|||||||
Reference in New Issue
Block a user