Files
XplorePlane/XplorePlane/Views/Main/ViewportPanelView.xaml.cs
T

211 lines
9.1 KiB
C#

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
{
public partial class ViewportPanelView : UserControl
{
private MainViewModel _mainVm;
private void SetStatus(string msg)
{
if (_mainVm == null)
{
try { _mainVm = ContainerLocator.Current?.Resolve<MainViewModel>(); } catch { }
}
if (_mainVm != null) _mainVm.StatusMessage = msg;
}
public ViewportPanelView()
{
InitializeComponent();
DataContextChanged += OnDataContextChanged;
// 动态创建右键菜单,支持条件性阻止弹出
var menu = new System.Windows.Controls.ContextMenu();
menu.Items.Add(new System.Windows.Controls.MenuItem { Header = "放大" });
menu.Items.Add(new System.Windows.Controls.MenuItem { Header = "缩小" });
menu.Items.Add(new System.Windows.Controls.MenuItem { Header = "适应窗口" });
menu.Items.Add(new System.Windows.Controls.Separator());
menu.Items.Add(new System.Windows.Controls.MenuItem { Header = "保存原始图像" });
menu.Items.Add(new System.Windows.Controls.MenuItem { Header = "保存结果图像" });
menu.Items.Add(new System.Windows.Controls.Separator());
menu.Items.Add(new System.Windows.Controls.MenuItem { Header = "清除所有测量" });
((System.Windows.Controls.MenuItem)menu.Items[0]).Click += ZoomIn_Click;
((System.Windows.Controls.MenuItem)menu.Items[1]).Click += ZoomOut_Click;
((System.Windows.Controls.MenuItem)menu.Items[2]).Click += ResetView_Click;
((System.Windows.Controls.MenuItem)menu.Items[4]).Click += SaveOriginalImage_Click;
((System.Windows.Controls.MenuItem)menu.Items[5]).Click += SaveResultImage_Click;
((System.Windows.Controls.MenuItem)menu.Items[7]).Click += ClearAllMeasurements_Click;
RoiCanvas.ContextMenu = menu;
RoiCanvas.ContextMenuOpening += (s, e) =>
{
if (RoiCanvas.SuppressContextMenu)
{
RoiCanvas.SuppressContextMenu = false;
e.Handled = true;
}
};
// 测量事件 → 更新主界面状态栏
RoiCanvas.MeasureCompleted += (s, e) =>
{
if (e is MeasureCompletedEventArgs args)
{
string typeLabel = args.MeasureType switch
{
"PointToLine" => "点线距",
"Angle" => "角度",
"FillRate" => "填锡率",
"BgaVoid" => "BGA空隙",
"BubbleVoid" => "气泡空隙",
_ => "点点距"
};
string valueText = args.MeasureType switch
{
"Angle" => $"{args.Distance:F2}°",
"FillRate" => $"{args.Distance:F1}%",
"BgaVoid" => $"{args.Distance:F1}%",
"BubbleVoid" => $"{args.Distance:F1}%",
_ => $"{args.Distance:F2} px"
};
SetStatus($"{typeLabel}: {valueText} | 共 {args.TotalCount} 条测量");
}
};
RoiCanvas.MeasureStatusChanged += (s, e) =>
{
if (e is MeasureStatusEventArgs args)
SetStatus(args.Message);
};
// 十字辅助线:直接订阅 Prism 事件
try
{
var ea = ContainerLocator.Current?.Resolve<Prism.Events.IEventAggregator>();
ea?.GetEvent<ToggleCrosshairEvent>().Subscribe(() =>
{
RoiCanvas.ShowCrosshair = !RoiCanvas.ShowCrosshair;
}, Prism.Events.ThreadOption.UIThread);
// 测量模式:直接订阅 Prism 事件
ea?.GetEvent<MeasurementToolEvent>().Subscribe(mode =>
{
RoiCanvas.CurrentMeasureMode = mode switch
{
MeasurementToolMode.PointDistance => XP.ImageProcessing.RoiControl.Models.MeasureMode.PointDistance,
MeasurementToolMode.PointLineDistance => XP.ImageProcessing.RoiControl.Models.MeasureMode.PointToLine,
MeasurementToolMode.Angle => XP.ImageProcessing.RoiControl.Models.MeasureMode.Angle,
MeasurementToolMode.ThroughHoleFillRate => XP.ImageProcessing.RoiControl.Models.MeasureMode.FillRate,
MeasurementToolMode.BgaVoid => XP.ImageProcessing.RoiControl.Models.MeasureMode.BgaVoid,
MeasurementToolMode.BubbleMeasure => XP.ImageProcessing.RoiControl.Models.MeasureMode.BubbleMeasure,
_ => 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();
RoiCanvas.ROIItems?.Clear();
RoiCanvas.SelectedROI = null;
if (DataContext is ViewportPanelViewModel vm)
vm.ResetMeasurementState();
SetStatus("已清除所有测量");
}
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<Canvas>(RoiCanvas, "mainCanvas");
if (target == null)
{
MessageBox.Show("当前没有可保存的图像", "提示", MessageBoxButton.OK, MessageBoxImage.Information);
return;
}
var width = (int)target.ActualWidth;
var height = (int)target.ActualHeight;
if (width == 0 || height == 0) return;
var rtb = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
rtb.Render(target);
SaveBitmapToFile(rtb, "保存结果图像");
}
private static void SaveBitmapToFile(BitmapSource bitmap, string title)
{
var dialog = new SaveFileDialog
{
Title = title,
Filter = "PNG 图像|*.png|BMP 图像|*.bmp|JPEG 图像|*.jpg",
DefaultExt = ".png"
};
if (dialog.ShowDialog() != true) return;
BitmapEncoder encoder = Path.GetExtension(dialog.FileName).ToLower() switch
{
".bmp" => new BmpBitmapEncoder(),
".jpg" or ".jpeg" => new JpegBitmapEncoder(),
_ => new PngBitmapEncoder()
};
encoder.Frames.Add(BitmapFrame.Create(bitmap));
using var fs = new FileStream(dialog.FileName, FileMode.Create);
encoder.Save(fs);
}
#endregion
private static T FindChildByName<T>(DependencyObject parent, string name) where T : FrameworkElement
{
int count = VisualTreeHelper.GetChildrenCount(parent);
for (int i = 0; i < count; i++)
{
var child = VisualTreeHelper.GetChild(parent, i);
if (child is T t && t.Name == name) return t;
var result = FindChildByName<T>(child, name);
if (result != null) return result;
}
return null;
}
}
}