Files
XplorePlane/XplorePlane/Views/Cnc/CncPageView.xaml.cs
T

264 lines
8.9 KiB
C#

using Prism.Ioc;
using System;
using System.Globalization;
using System.Windows.Media;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using XP.Common.Logging.Interfaces;
using XplorePlane.Services;
using XplorePlane.ViewModels.Cnc;
namespace XplorePlane.Views.Cnc
{
/// <summary>
/// CNC editor main page view.
/// </summary>
public partial class CncPageView : UserControl
{
private static readonly Brush SelectedNodeBackground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#E7F0F7"));
private static readonly Brush SelectedNodeBorder = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#9CB9D1"));
private static readonly Brush HoverNodeBackground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F6FAFC"));
private static readonly Brush HoverNodeBorder = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#D7E4EE"));
private static readonly Brush TransparentBrush = Brushes.Transparent;
private CncInspectionModulePipelineViewModel _inspectionModulePipelineViewModel;
public CncPageView()
{
InitializeComponent();
Loaded += OnLoaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
CncEditorViewModel editorViewModel = DataContext as CncEditorViewModel;
if (editorViewModel == null)
{
try
{
editorViewModel = ContainerLocator.Current?.Resolve<CncEditorViewModel>();
DataContext = editorViewModel;
}
catch (Exception)
{
// keep existing DataContext if resolution fails
}
}
if (editorViewModel == null || _inspectionModulePipelineViewModel != null)
return;
try
{
var imageProcessingService = ContainerLocator.Current.Resolve<IImageProcessingService>();
var persistenceService = ContainerLocator.Current.Resolve<IPipelinePersistenceService>();
var logger = ContainerLocator.Current.Resolve<ILoggerService>();
_inspectionModulePipelineViewModel = new CncInspectionModulePipelineViewModel(
editorViewModel,
imageProcessingService,
persistenceService,
logger);
InspectionModulePipelineEditor.DataContext = _inspectionModulePipelineViewModel;
InspectionModulePipelineEmptyState.DataContext = _inspectionModulePipelineViewModel;
}
catch (Exception)
{
// keep page usable even if pipeline editor host setup fails
}
CncTreeView.ContextMenuOpening -= CncTreeView_ContextMenuOpening;
CncTreeView.ContextMenuOpening += CncTreeView_ContextMenuOpening;
CncTreeView.LayoutUpdated -= CncTreeView_LayoutUpdated;
CncTreeView.LayoutUpdated += CncTreeView_LayoutUpdated;
UpdateNodeVisualState();
}
private void CncTreeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs<object> e)
{
if (DataContext is CncEditorViewModel viewModel)
{
viewModel.SelectedNode = e.NewValue as CncNodeViewModel;
}
UpdateNodeVisualState();
}
private void CncTreeView_PreviewKeyDown(object sender, KeyEventArgs e)
{
if (e.Key != Key.Delete || DataContext is not CncEditorViewModel viewModel)
return;
if (!viewModel.DeleteNodeCommand.CanExecute())
return;
viewModel.DeleteNodeCommand.Execute();
e.Handled = true;
}
private void CncTreeView_ContextMenuOpening(object sender, ContextMenuEventArgs e)
{
if (DataContext is not CncEditorViewModel viewModel)
{
return;
}
var position = Mouse.GetPosition(CncTreeView);
var hit = VisualTreeHelper.HitTest(CncTreeView, position);
var treeViewItem = FindAncestor<TreeViewItem>(hit?.VisualHit);
if (treeViewItem?.DataContext is not CncNodeViewModel nodeVm)
{
CncTreeView.ContextMenu = null;
return;
}
viewModel.SelectedNode = nodeVm;
UpdateNodeVisualState();
CncTreeView.ContextMenu = new ContextMenu
{
Items =
{
new MenuItem
{
Header = "在上方插入位置",
Command = viewModel.PrepareInsertAboveCommand,
CommandParameter = nodeVm
},
new MenuItem
{
Header = "在下方插入位置",
Command = viewModel.PrepareInsertBelowCommand,
CommandParameter = nodeVm
}
}
};
}
private void CncTreeView_LayoutUpdated(object sender, EventArgs e)
{
HideInlineDeleteButtons();
UpdateNodeVisualState();
}
private void HideInlineDeleteButtons()
{
foreach (var button in FindVisualDescendants<Button>(CncTreeView))
{
if (button.ToolTip is string)
{
button.Visibility = Visibility.Collapsed;
button.IsHitTestVisible = false;
}
}
}
private void UpdateNodeVisualState()
{
foreach (var item in FindVisualDescendants<TreeViewItem>(CncTreeView))
{
if (item.DataContext is not CncNodeViewModel)
{
continue;
}
var card = FindNodeCard(item);
if (card == null)
{
continue;
}
if (item.IsSelected)
{
card.Background = SelectedNodeBackground;
card.BorderBrush = SelectedNodeBorder;
}
else if (card.IsMouseOver)
{
card.Background = HoverNodeBackground;
card.BorderBrush = HoverNodeBorder;
}
else
{
card.Background = TransparentBrush;
card.BorderBrush = TransparentBrush;
}
}
}
private static Border FindNodeCard(DependencyObject root)
{
foreach (var border in FindVisualDescendants<Border>(root))
{
if (border.DataContext is CncNodeViewModel && border.CornerRadius.TopLeft == 4 && border.BorderThickness.Left == 1)
{
return border;
}
}
return null;
}
private static T FindAncestor<T>(DependencyObject dependencyObject) where T : DependencyObject
{
var current = dependencyObject;
while (current != null)
{
if (current is T match)
{
return match;
}
current = VisualTreeHelper.GetParent(current);
}
return null;
}
private static System.Collections.Generic.IEnumerable<T> FindVisualDescendants<T>(DependencyObject root) where T : DependencyObject
{
if (root == null)
{
yield break;
}
int childCount = VisualTreeHelper.GetChildrenCount(root);
for (int i = 0; i < childCount; i++)
{
var child = VisualTreeHelper.GetChild(root, i);
if (child is T typed)
{
yield return typed;
}
foreach (var descendant in FindVisualDescendants<T>(child))
{
yield return descendant;
}
}
}
}
public class NullToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
var invert = string.Equals(parameter as string, "Invert", StringComparison.OrdinalIgnoreCase);
var isVisible = value != null;
if (invert)
{
isVisible = !isVisible;
}
return isVisible ? Visibility.Visible : Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotSupportedException();
}
}
}