修改CNC样式 √ 隐藏根节点,修改保存位置0 修改为位置1---N √ 运行对一级节点进行重命名 √
This commit is contained in:
@@ -722,9 +722,9 @@ namespace XplorePlane.Services.Cnc
|
||||
var manualImage = _mainViewportService?.LatestManualImage as BitmapSource;
|
||||
if (manualImage != null)
|
||||
{
|
||||
_logger.ForModule<CncExecutionService>().Info(
|
||||
"[图像链路] TryGetSourceImage:从 MainViewportService.LatestManualImage 获取图像,尺寸 {W}x{H}",
|
||||
manualImage.PixelWidth, manualImage.PixelHeight);
|
||||
//_logger.ForModule<CncExecutionService>().Info(
|
||||
// "[图像链路] TryGetSourceImage:从 MainViewportService.LatestManualImage 获取图像,尺寸 {W}x{H}",
|
||||
// manualImage.PixelWidth, manualImage.PixelHeight);
|
||||
return manualImage;
|
||||
}
|
||||
|
||||
|
||||
@@ -374,8 +374,8 @@ namespace XplorePlane.Services.Cnc
|
||||
result.Add(indexedNode switch
|
||||
{
|
||||
ReferencePointNode referencePointNode => referencePointNode with { Name = $"\u53C2\u8003\u70B9_{referencePointNumber++}" },
|
||||
SavePositionNode savePositionNode => savePositionNode with { Name = $"\u4FDD\u5B58\u4F4D\u7F6E_{savePositionNumber++}" },
|
||||
InspectionModuleNode inspectionModuleNode => inspectionModuleNode with { Name = $"\u68C0\u6D4B\u6A21\u5757_{inspectionModuleNumber++}" },
|
||||
SavePositionNode savePositionNode => savePositionNode with { Name = $"\u4F4D\u7F6E{++savePositionNumber}" },
|
||||
InspectionModuleNode inspectionModuleNode => inspectionModuleNode with { Name = $"\u6A21\u5757{++inspectionModuleNumber}" },
|
||||
_ => indexedNode
|
||||
});
|
||||
}
|
||||
|
||||
@@ -77,11 +77,11 @@ namespace XplorePlane.Services.MainViewport
|
||||
|
||||
try
|
||||
{
|
||||
_logger.Info(
|
||||
"[图像链路] DetectorFramePipeline 收到帧 #{N},分辨率 {W}x{H},采集时间 {T},当前队列深度 AcquireQ={AQ} ProcessQ={PQ}",
|
||||
args.FrameNumber, args.Width, args.Height,
|
||||
args.CaptureTime.ToString("HH:mm:ss.fff"),
|
||||
AcquireQueueCount, ProcessQueueCount);
|
||||
//_logger.Info(
|
||||
// "[图像链路] DetectorFramePipeline 收到帧 #{N},分辨率 {W}x{H},采集时间 {T},当前队列深度 AcquireQ={AQ} ProcessQ={PQ}",
|
||||
// args.FrameNumber, args.Width, args.Height,
|
||||
// args.CaptureTime.ToString("HH:mm:ss.fff"),
|
||||
// AcquireQueueCount, ProcessQueueCount);
|
||||
|
||||
var rawPixels = new ushort[args.ImageData.Length];
|
||||
Array.Copy(args.ImageData, rawPixels, rawPixels.Length);
|
||||
|
||||
@@ -1005,15 +1005,50 @@ namespace XplorePlane.ViewModels.Cnc
|
||||
result.Add(indexedNode switch
|
||||
{
|
||||
ReferencePointNode referencePointNode => referencePointNode with { Name = $"\u53C2\u8003\u70B9_{referencePointNumber++}" },
|
||||
SavePositionNode savePositionNode => savePositionNode with { Name = $"\u4FDD\u5B58\u4F4D\u7F6E_{savePositionNumber++}" },
|
||||
InspectionModuleNode inspectionModuleNode => inspectionModuleNode with { Name = $"\u68C0\u6D4B\u6A21\u5757_{inspectionModuleNumber++}" },
|
||||
// 保存位置:用户自定义名称时保留,否则用"位置N"(1-based)
|
||||
SavePositionNode savePositionNode => IsDefaultSavePositionName(savePositionNode.Name)
|
||||
? savePositionNode with { Name = $"\u4F4D\u7F6E{++savePositionNumber}" }
|
||||
: (CncNode)(savePositionNode with { Index = i }),
|
||||
// 检测模块:用户自定义名称时保留,否则用"模块N"(1-based)
|
||||
InspectionModuleNode inspectionModuleNode => IsDefaultInspectionModuleName(inspectionModuleNode.Name)
|
||||
? inspectionModuleNode with { Name = $"\u6A21\u5757{++inspectionModuleNumber}" }
|
||||
: (CncNode)(inspectionModuleNode with { Index = i }),
|
||||
_ => indexedNode
|
||||
});
|
||||
// 无论是否重命名,计数器都要递增以保持后续编号连续
|
||||
if (indexedNode is SavePositionNode sp && !IsDefaultSavePositionName(sp.Name))
|
||||
savePositionNumber++;
|
||||
if (indexedNode is InspectionModuleNode im && !IsDefaultInspectionModuleName(im.Name))
|
||||
inspectionModuleNumber++;
|
||||
}
|
||||
|
||||
return result.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>判断是否为系统默认的保存位置名称("位置N" 格式)</summary>
|
||||
private static bool IsDefaultSavePositionName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) return true;
|
||||
if (name.StartsWith("\u4F4D\u7F6E", StringComparison.Ordinal))
|
||||
return int.TryParse(name[2..], out _);
|
||||
// 兼容旧格式 "保存位置_N"
|
||||
if (name.StartsWith("\u4FDD\u5B58\u4F4D\u7F6E_", StringComparison.Ordinal))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>判断是否为系统默认的检测模块名称("模块N" 或旧格式 "检测模块_N")</summary>
|
||||
private static bool IsDefaultInspectionModuleName(string name)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(name)) return true;
|
||||
if (name.StartsWith("\u6A21\u5757", StringComparison.Ordinal))
|
||||
return int.TryParse(name[2..], out _);
|
||||
// 兼容旧格式 "检测模块_N"
|
||||
if (name.StartsWith("\u68C0\u6D4B\u6A21\u5757_", StringComparison.Ordinal))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
private static bool IsSavePositionChild(CncNodeType type)
|
||||
{
|
||||
return type is CncNodeType.InspectionMarker
|
||||
|
||||
@@ -20,18 +20,105 @@
|
||||
<local:BoolToDisplayTextConverter x:Key="BoolToDisplayTextConverter" />
|
||||
<local:NullToVisibilityConverter x:Key="NullToVisibilityConverter" />
|
||||
|
||||
<SolidColorBrush x:Key="PanelBg" Color="White" />
|
||||
<SolidColorBrush x:Key="PanelBg" Color="#F5F6FA" />
|
||||
<SolidColorBrush x:Key="PanelBorder" Color="#CDCBCB" />
|
||||
<SolidColorBrush x:Key="SeparatorBrush" Color="#E5E5E5" />
|
||||
<SolidColorBrush x:Key="HeaderBg" Color="#F7F7F7" />
|
||||
<SolidColorBrush x:Key="TreeChildLineBrush" Color="#C7D4DF" />
|
||||
<SolidColorBrush x:Key="HeaderBg" Color="#F0F1F5" />
|
||||
<SolidColorBrush x:Key="TreeChildLineBrush" Color="#C8CDD8" />
|
||||
<FontFamily x:Key="UiFont">Microsoft YaHei UI</FontFamily>
|
||||
|
||||
<!-- ══════════════════════════════════════════════
|
||||
现代风格 TreeViewItem:实心三角展开箭头
|
||||
══════════════════════════════════════════════ -->
|
||||
|
||||
<!-- 展开/折叠箭头:实心三角 ▶ / ▼ -->
|
||||
<Style x:Key="ExpandCollapseToggleStyle" TargetType="ToggleButton">
|
||||
<Setter Property="Focusable" Value="False" />
|
||||
<Setter Property="Width" Value="16" />
|
||||
<Setter Property="Height" Value="16" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ToggleButton">
|
||||
<Grid Width="16" Height="16" Background="Transparent">
|
||||
<!-- 折叠状态:▶ 右向三角 -->
|
||||
<Path
|
||||
x:Name="ArrowRight"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Data="M 0,0 L 5,4 L 0,8 Z"
|
||||
Fill="#666666"
|
||||
Stretch="None" />
|
||||
<!-- 展开状态:▼ 下向三角 -->
|
||||
<Path
|
||||
x:Name="ArrowDown"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Data="M 0,0 L 8,0 L 4,5 Z"
|
||||
Fill="#666666"
|
||||
Stretch="None"
|
||||
Visibility="Collapsed" />
|
||||
</Grid>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsChecked" Value="True">
|
||||
<Setter TargetName="ArrowRight" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="ArrowDown" Property="Visibility" Value="Visible" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<!-- TreeViewItem ControlTemplate -->
|
||||
<Style x:Key="TreeItemStyle" TargetType="TreeViewItem">
|
||||
<Setter Property="IsExpanded" Value="{Binding IsExpanded, Mode=TwoWay}" />
|
||||
<Setter Property="Padding" Value="0" />
|
||||
<Setter Property="Margin" Value="0" />
|
||||
<Setter Property="Margin" Value="0,0,0,0" />
|
||||
<Setter Property="HorizontalContentAlignment" Value="Stretch" />
|
||||
<Setter Property="Background" Value="Transparent" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="TreeViewItem">
|
||||
<StackPanel>
|
||||
<!-- 节点行:箭头 + 内容 -->
|
||||
<Grid x:Name="HeaderRow">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 展开/折叠箭头 -->
|
||||
<ToggleButton
|
||||
x:Name="Expander"
|
||||
Grid.Column="0"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
IsChecked="{Binding IsExpanded, RelativeSource={RelativeSource TemplatedParent}}"
|
||||
Style="{StaticResource ExpandCollapseToggleStyle}"
|
||||
Visibility="Hidden" />
|
||||
|
||||
<!-- 节点内容 -->
|
||||
<ContentPresenter
|
||||
x:Name="PART_Header"
|
||||
Grid.Column="1"
|
||||
ContentSource="Header"
|
||||
HorizontalAlignment="Stretch" />
|
||||
</Grid>
|
||||
|
||||
<!-- 子节点区域 -->
|
||||
<ItemsPresenter
|
||||
x:Name="ItemsHost"
|
||||
Margin="8,0,0,0"
|
||||
Visibility="Collapsed" />
|
||||
</StackPanel>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="HasItems" Value="True">
|
||||
<Setter TargetName="Expander" Property="Visibility" Value="Visible" />
|
||||
</Trigger>
|
||||
<Trigger Property="IsExpanded" Value="True">
|
||||
<Setter TargetName="ItemsHost" Property="Visibility" Value="Visible" />
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="EditorTitle" TargetType="TextBlock">
|
||||
@@ -265,237 +352,241 @@
|
||||
PreviewKeyDown="CncTreeView_PreviewKeyDown"
|
||||
SelectedItemChanged="CncTreeView_SelectedItemChanged">
|
||||
<TreeView.Resources>
|
||||
|
||||
<!-- ── 2级节点:CncNodeViewModel ── -->
|
||||
<HierarchicalDataTemplate
|
||||
DataType="{x:Type vm:CncNodeViewModel}"
|
||||
ItemsSource="{Binding TreeChildren}">
|
||||
<HierarchicalDataTemplate.ItemContainerStyle>
|
||||
<!-- 三级节点(流水线步骤)的 TreeViewItem:取消额外缩进 -->
|
||||
<Style BasedOn="{StaticResource TreeItemStyle}" TargetType="TreeViewItem">
|
||||
<Setter Property="Margin" Value="-16,0,0,0" />
|
||||
</Style>
|
||||
</HierarchicalDataTemplate.ItemContainerStyle>
|
||||
<!-- 节点行:圆角背景 + 图标 + 名称 + 状态点 + ⋮ 菜单 -->
|
||||
<Border
|
||||
x:Name="NodeCard"
|
||||
Margin="0,1,0,1"
|
||||
Padding="0"
|
||||
x:Name="NodeBg"
|
||||
Margin="0,1,4,1"
|
||||
Padding="6,4,6,4"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="1"
|
||||
CornerRadius="4">
|
||||
<Grid x:Name="NodeRoot" MinHeight="23">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
CornerRadius="6"
|
||||
SnapsToDevicePixels="True">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="15" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="22" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Grid Grid.RowSpan="2" Grid.Column="0">
|
||||
<Border
|
||||
x:Name="ChildStem"
|
||||
Width="1"
|
||||
Margin="7,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
Background="{StaticResource TreeChildLineBrush}" />
|
||||
<Border
|
||||
x:Name="ChildBranch"
|
||||
Width="8"
|
||||
Height="1"
|
||||
Margin="7,0,0,0"
|
||||
HorizontalAlignment="Left"
|
||||
VerticalAlignment="Center"
|
||||
Background="{StaticResource TreeChildLineBrush}" />
|
||||
</Grid>
|
||||
|
||||
<Border
|
||||
Grid.RowSpan="2"
|
||||
Grid.Column="1"
|
||||
Width="16"
|
||||
Height="16"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
CornerRadius="4">
|
||||
<Image
|
||||
Width="13"
|
||||
Height="13"
|
||||
Source="{Binding Icon}"
|
||||
Stretch="Uniform" />
|
||||
</Border>
|
||||
|
||||
<TextBlock
|
||||
x:Name="NodeNameText"
|
||||
Grid.Row="0"
|
||||
Grid.Column="2"
|
||||
Margin="3,0,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="11"
|
||||
FontWeight="SemiBold"
|
||||
Text="{Binding Name}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<StackPanel
|
||||
x:Name="NodeActions"
|
||||
Grid.Row="0"
|
||||
Grid.Column="3"
|
||||
Margin="0,0,2,0"
|
||||
VerticalAlignment="Center"
|
||||
Orientation="Horizontal"
|
||||
Visibility="Collapsed">
|
||||
<Button
|
||||
Width="20"
|
||||
Height="20"
|
||||
Margin="1,0"
|
||||
Background="White"
|
||||
BorderBrush="#E05050"
|
||||
BorderThickness="1"
|
||||
Command="{Binding DataContext.DeleteNodeCommand, RelativeSource={RelativeSource AncestorType=TreeView}}"
|
||||
Content="×"
|
||||
Cursor="Hand"
|
||||
FontSize="10"
|
||||
ToolTip="删除" />
|
||||
</StackPanel>
|
||||
<Grid
|
||||
x:Name="WaitDelayProgressHost"
|
||||
Grid.Row="1"
|
||||
Grid.Column="2"
|
||||
Grid.ColumnSpan="2"
|
||||
Margin="3,2,6,1"
|
||||
Visibility="Collapsed">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
<ProgressBar
|
||||
Height="6"
|
||||
Minimum="0"
|
||||
Maximum="100"
|
||||
Value="{Binding ExecutionProgressPercent}" />
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
Margin="6,-4,0,0"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="10"
|
||||
Foreground="#444444"
|
||||
Text="{Binding ExecutionProgressText}" />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Border>
|
||||
<DataTemplate.Triggers>
|
||||
<Trigger SourceName="NodeRoot" Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="NodeActions" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#F6FAFD" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#DFEAF3" />
|
||||
</Trigger>
|
||||
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="True">
|
||||
<Setter TargetName="NodeActions" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#DCEEFF" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#71A9DB" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="#1F2D3D" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Running">
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#FFD54F" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#C89B00" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="#1F1F1F" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Succeeded">
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#FF2E7D32" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF1B5E20" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Failed">
|
||||
<Setter TargetName="NodeCard" Property="Background" Value="#FFC62828" />
|
||||
<Setter TargetName="NodeCard" Property="BorderBrush" Value="#FF8E0000" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="White" />
|
||||
</DataTrigger>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsWaitDelay}" Value="True" />
|
||||
<Condition Binding="{Binding IsRunningNode}" Value="True" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter TargetName="WaitDelayProgressHost" Property="Visibility" Value="Visible" />
|
||||
</MultiDataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
|
||||
<!-- 三级节点:检测模块的流水线步骤 -->
|
||||
<DataTemplate DataType="{x:Type vm:CncPipelineStepViewModel}">
|
||||
<Border
|
||||
x:Name="StepCard"
|
||||
Margin="0,1,0,1"
|
||||
Padding="2,2,4,2"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
BorderThickness="1"
|
||||
CornerRadius="3">
|
||||
<Grid MinHeight="20">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="18" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="24" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 图标 -->
|
||||
<Border
|
||||
<Image
|
||||
Grid.Column="0"
|
||||
Width="14"
|
||||
Height="14"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
CornerRadius="3">
|
||||
<Image
|
||||
Width="12"
|
||||
Height="12"
|
||||
Source="{Binding IconPath}"
|
||||
Stretch="Uniform" />
|
||||
</Border>
|
||||
Width="16" Height="16"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Source="{Binding Icon}"
|
||||
Stretch="Uniform" />
|
||||
|
||||
<!-- 名称 -->
|
||||
<!-- 名称(普通显示) -->
|
||||
<TextBlock
|
||||
x:Name="StepNameText"
|
||||
x:Name="NodeNameText"
|
||||
Grid.Column="1"
|
||||
Margin="3,0,0,0"
|
||||
Margin="4,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="10.5"
|
||||
Text="{Binding DisplayName}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
FontSize="12"
|
||||
Foreground="#1A1A2E"
|
||||
Text="{Binding Name}"
|
||||
TextTrimming="CharacterEllipsis"
|
||||
MouseLeftButtonDown="NodeNameText_MouseLeftButtonDown" />
|
||||
|
||||
<!-- 启用状态标签 -->
|
||||
<Border
|
||||
x:Name="StepStateBadge"
|
||||
Grid.Column="2"
|
||||
Margin="4,0,0,0"
|
||||
Padding="4,1"
|
||||
<!-- 名称(编辑模式,仅 SavePosition 节点双击后显示) -->
|
||||
<TextBox
|
||||
x:Name="NodeNameEditor"
|
||||
Grid.Column="1"
|
||||
Margin="2,0,6,0"
|
||||
VerticalAlignment="Center"
|
||||
Background="#E8F5E9"
|
||||
BorderBrush="#A5D6A7"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="12"
|
||||
Text="{Binding Name, UpdateSourceTrigger=Explicit}"
|
||||
Visibility="Collapsed"
|
||||
BorderBrush="#7EB3F5"
|
||||
BorderThickness="1"
|
||||
CornerRadius="3">
|
||||
<TextBlock
|
||||
x:Name="StepStateText"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="9.5"
|
||||
Foreground="#2E7D32"
|
||||
Text="{Binding StateText}" />
|
||||
</Border>
|
||||
Padding="3,1"
|
||||
LostFocus="NodeNameEditor_LostFocus"
|
||||
KeyDown="NodeNameEditor_KeyDown" />
|
||||
|
||||
<!-- 状态点 -->
|
||||
<Ellipse
|
||||
x:Name="StatusDot"
|
||||
Grid.Column="2"
|
||||
Width="7" Height="7"
|
||||
Margin="0,0,4,0"
|
||||
VerticalAlignment="Center"
|
||||
Fill="#BBBBBB" />
|
||||
|
||||
<!-- 状态文字 -->
|
||||
<TextBlock
|
||||
x:Name="StatusText"
|
||||
Grid.Column="3"
|
||||
Margin="0,0,6,0"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="11"
|
||||
Foreground="#AAAAAA"
|
||||
Text="禁用" />
|
||||
|
||||
<!-- ⋮ 菜单按钮(选中时显示) -->
|
||||
<Button
|
||||
x:Name="NodeMenuBtn"
|
||||
Grid.Column="4"
|
||||
Width="22" Height="22"
|
||||
Padding="0"
|
||||
Background="Transparent"
|
||||
BorderThickness="0"
|
||||
Content="⋮"
|
||||
Cursor="Hand"
|
||||
FontSize="14"
|
||||
Foreground="#555555"
|
||||
ToolTip="更多操作"
|
||||
Visibility="Collapsed"
|
||||
Command="{Binding DataContext.DeleteNodeCommand, RelativeSource={RelativeSource AncestorType=TreeView}}"
|
||||
Tag="{Binding}" />
|
||||
</Grid>
|
||||
</Border>
|
||||
<DataTemplate.Triggers>
|
||||
<!-- 已停用:灰色文字 + 橙色徽章 -->
|
||||
<!-- 悬停 -->
|
||||
<Trigger SourceName="NodeBg" Property="IsMouseOver" Value="True">
|
||||
<Setter TargetName="NodeBg" Property="Background" Value="#EAECF5" />
|
||||
<Setter TargetName="NodeMenuBtn" Property="Visibility" Value="Visible" />
|
||||
</Trigger>
|
||||
<!-- 选中 -->
|
||||
<DataTrigger Binding="{Binding IsSelected, RelativeSource={RelativeSource AncestorType=TreeViewItem}}" Value="True">
|
||||
<Setter TargetName="NodeBg" Property="Background" Value="#DDE6FB" />
|
||||
<Setter TargetName="NodeNameText" Property="Foreground" Value="#1A1A2E" />
|
||||
<Setter TargetName="NodeMenuBtn" Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
<!-- 不涉及启用/禁用的节点类型:隐藏状态点和状态文字 -->
|
||||
<DataTrigger Binding="{Binding IsSavePosition}" Value="True">
|
||||
<Setter TargetName="StatusDot" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="StatusText" Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsReferencePoint}" Value="True">
|
||||
<Setter TargetName="StatusDot" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="StatusText" Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsPauseDialog}" Value="True">
|
||||
<Setter TargetName="StatusDot" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="StatusText" Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsWaitDelay}" Value="True">
|
||||
<Setter TargetName="StatusDot" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="StatusText" Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
<DataTrigger Binding="{Binding IsCompleteProgram}" Value="True">
|
||||
<Setter TargetName="StatusDot" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="StatusText" Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
<!-- 检测模块:同样不显示启用/禁用状态 -->
|
||||
<DataTrigger Binding="{Binding IsInspectionModule}" Value="True">
|
||||
<Setter TargetName="StatusDot" Property="Visibility" Value="Collapsed" />
|
||||
<Setter TargetName="StatusText" Property="Visibility" Value="Collapsed" />
|
||||
</DataTrigger>
|
||||
<!-- 执行中 -->
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Running">
|
||||
<Setter TargetName="NodeBg" Property="Background" Value="#FFF8E1" />
|
||||
<Setter TargetName="StatusDot" Property="Fill" Value="#FFA000" />
|
||||
<Setter TargetName="StatusDot" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="StatusText" Property="Text" Value="执行中" />
|
||||
<Setter TargetName="StatusText" Property="Foreground" Value="#FFA000" />
|
||||
<Setter TargetName="StatusText" Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
<!-- 执行成功 -->
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Succeeded">
|
||||
<Setter TargetName="NodeBg" Property="Background" Value="#E8F5E9" />
|
||||
<Setter TargetName="StatusDot" Property="Fill" Value="#43A047" />
|
||||
<Setter TargetName="StatusDot" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="StatusText" Property="Text" Value="完成" />
|
||||
<Setter TargetName="StatusText" Property="Foreground" Value="#43A047" />
|
||||
<Setter TargetName="StatusText" Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
<!-- 执行失败 -->
|
||||
<DataTrigger Binding="{Binding ExecutionState}" Value="Failed">
|
||||
<Setter TargetName="NodeBg" Property="Background" Value="#FFEBEE" />
|
||||
<Setter TargetName="StatusDot" Property="Fill" Value="#E53935" />
|
||||
<Setter TargetName="StatusDot" Property="Visibility" Value="Visible" />
|
||||
<Setter TargetName="StatusText" Property="Text" Value="失败" />
|
||||
<Setter TargetName="StatusText" Property="Foreground" Value="#E53935" />
|
||||
<Setter TargetName="StatusText" Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</HierarchicalDataTemplate>
|
||||
|
||||
<!-- ── 3级节点:流水线步骤 CncPipelineStepViewModel ── -->
|
||||
<DataTemplate DataType="{x:Type vm:CncPipelineStepViewModel}">
|
||||
<Grid MinHeight="20" Background="Transparent">
|
||||
<Grid.ColumnDefinitions>
|
||||
<!-- 左侧竖线区 -->
|
||||
<ColumnDefinition Width="3" />
|
||||
<ColumnDefinition Width="6" />
|
||||
<ColumnDefinition Width="20" />
|
||||
<ColumnDefinition Width="*" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
<ColumnDefinition Width="Auto" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!-- 左侧竖线 -->
|
||||
<Rectangle
|
||||
Grid.Column="0"
|
||||
Width="2"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Stretch"
|
||||
Fill="{StaticResource TreeChildLineBrush}"
|
||||
RadiusX="1" RadiusY="1" />
|
||||
|
||||
<!-- 图标 -->
|
||||
<Image
|
||||
Grid.Column="2"
|
||||
Width="16" Height="16"
|
||||
HorizontalAlignment="Center" VerticalAlignment="Center"
|
||||
Source="{Binding IconPath}"
|
||||
Stretch="Uniform" />
|
||||
|
||||
<!-- 名称 -->
|
||||
<TextBlock
|
||||
x:Name="StepNameText"
|
||||
Grid.Column="3"
|
||||
Margin="4,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="12"
|
||||
Foreground="#333344"
|
||||
Text="{Binding DisplayName}"
|
||||
TextTrimming="CharacterEllipsis" />
|
||||
|
||||
<!-- 状态点 -->
|
||||
<Ellipse
|
||||
x:Name="StepDot"
|
||||
Grid.Column="4"
|
||||
Width="7" Height="7"
|
||||
Margin="0,0,4,0"
|
||||
VerticalAlignment="Center"
|
||||
Fill="#43A047" />
|
||||
|
||||
<!-- 状态文字 -->
|
||||
<TextBlock
|
||||
x:Name="StepStateText"
|
||||
Grid.Column="5"
|
||||
Margin="0,0,8,0"
|
||||
VerticalAlignment="Center"
|
||||
FontFamily="{StaticResource UiFont}"
|
||||
FontSize="11"
|
||||
Foreground="#43A047"
|
||||
Text="启用" />
|
||||
</Grid>
|
||||
<DataTemplate.Triggers>
|
||||
<!-- 已停用 -->
|
||||
<DataTrigger Binding="{Binding IsEnabled}" Value="False">
|
||||
<Setter TargetName="StepNameText" Property="Foreground" Value="#999999" />
|
||||
<Setter TargetName="StepNameText" Property="Foreground" Value="#AAAAAA" />
|
||||
<Setter TargetName="StepNameText" Property="TextDecorations" Value="Strikethrough" />
|
||||
<Setter TargetName="StepStateBadge" Property="Background" Value="#FFF3E0" />
|
||||
<Setter TargetName="StepStateBadge" Property="BorderBrush" Value="#FFCC80" />
|
||||
<Setter TargetName="StepStateText" Property="Foreground" Value="#E65100" />
|
||||
<Setter TargetName="StepDot" Property="Fill" Value="#BBBBBB" />
|
||||
<Setter TargetName="StepStateText" Property="Text" Value="停用" />
|
||||
<Setter TargetName="StepStateText" Property="Foreground" Value="#AAAAAA" />
|
||||
</DataTrigger>
|
||||
</DataTemplate.Triggers>
|
||||
</DataTemplate>
|
||||
|
||||
@@ -2,6 +2,7 @@ using Prism.Ioc;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
@@ -86,18 +87,88 @@ namespace XplorePlane.Views.Cnc
|
||||
if (DataContext is CncEditorViewModel viewModel)
|
||||
{
|
||||
// 三级节点(CncPipelineStepViewModel)点击时,保持当前 2 级节点选中不变
|
||||
// Only update SelectedNode when a CncNodeViewModel is selected (1st or 2nd level)
|
||||
if (e.NewValue is CncNodeViewModel nodeVm)
|
||||
{
|
||||
viewModel.SelectedNode = nodeVm;
|
||||
}
|
||||
// else: pipeline step clicked — keep existing SelectedNode unchanged
|
||||
}
|
||||
|
||||
UpdateNodeVisualState();
|
||||
UpdatePropertyEditorState();
|
||||
}
|
||||
|
||||
// ── 节点名称内联编辑 ──────────────────────────────────────────
|
||||
|
||||
private void NodeNameText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.ClickCount != 2) return;
|
||||
if (sender is not TextBlock tb) return;
|
||||
|
||||
// 只允许 SavePosition 和 InspectionModule 节点重命名
|
||||
var nodeVm = (tb.DataContext as CncNodeViewModel)
|
||||
?? FindAncestor<TreeViewItem>(tb)?.DataContext as CncNodeViewModel;
|
||||
if (nodeVm == null || (!nodeVm.IsSavePosition && !nodeVm.IsInspectionModule)) return;
|
||||
|
||||
// 找到同级的 TextBox 编辑器
|
||||
var parent = VisualTreeHelper.GetParent(tb) as Grid;
|
||||
if (parent == null) return;
|
||||
|
||||
var editor = FindVisualDescendants<TextBox>(parent).FirstOrDefault(x => x.Name == "NodeNameEditor");
|
||||
if (editor == null) return;
|
||||
|
||||
// 切换到编辑模式
|
||||
tb.Visibility = Visibility.Collapsed;
|
||||
editor.Visibility = Visibility.Visible;
|
||||
editor.Text = nodeVm.Name;
|
||||
editor.SelectAll();
|
||||
editor.Focus();
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
private void NodeNameEditor_LostFocus(object sender, RoutedEventArgs e)
|
||||
{
|
||||
CommitNodeRename(sender as TextBox);
|
||||
}
|
||||
|
||||
private void NodeNameEditor_KeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.Return || e.Key == Key.Enter)
|
||||
{
|
||||
CommitNodeRename(sender as TextBox);
|
||||
e.Handled = true;
|
||||
}
|
||||
else if (e.Key == Key.Escape)
|
||||
{
|
||||
CancelNodeRename(sender as TextBox);
|
||||
e.Handled = true;
|
||||
}
|
||||
}
|
||||
|
||||
private void CommitNodeRename(TextBox editor)
|
||||
{
|
||||
if (editor == null) return;
|
||||
var nodeVm = FindAncestor<TreeViewItem>(editor)?.DataContext as CncNodeViewModel;
|
||||
if (nodeVm == null) return;
|
||||
|
||||
var newName = editor.Text?.Trim();
|
||||
if (!string.IsNullOrWhiteSpace(newName) && newName != nodeVm.Name)
|
||||
nodeVm.Name = newName;
|
||||
|
||||
ExitRenameMode(editor);
|
||||
}
|
||||
|
||||
private void CancelNodeRename(TextBox editor)
|
||||
{
|
||||
if (editor == null) return;
|
||||
ExitRenameMode(editor);
|
||||
}
|
||||
|
||||
private static void ExitRenameMode(TextBox editor)
|
||||
{
|
||||
var parent = VisualTreeHelper.GetParent(editor) as Grid;
|
||||
if (parent == null) return;
|
||||
|
||||
var tb = FindVisualDescendants<TextBlock>(parent).FirstOrDefault(x => x.Name == "NodeNameText");
|
||||
if (tb != null) tb.Visibility = Visibility.Visible;
|
||||
editor.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private void CncTreeView_PreviewKeyDown(object sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key != Key.Delete || DataContext is not CncEditorViewModel viewModel)
|
||||
@@ -113,47 +184,91 @@ namespace XplorePlane.Views.Cnc
|
||||
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)
|
||||
|
||||
// 右键点在节点上
|
||||
if (treeViewItem?.DataContext is CncNodeViewModel nodeVm)
|
||||
{
|
||||
CncTreeView.ContextMenu = null;
|
||||
return;
|
||||
viewModel.SelectedNode = nodeVm;
|
||||
UpdatePropertyEditorState();
|
||||
|
||||
CncTreeView.ContextMenu = new ContextMenu
|
||||
{
|
||||
Items =
|
||||
{
|
||||
new MenuItem
|
||||
{
|
||||
Header = "在上方插入位置",
|
||||
Command = viewModel.PrepareInsertAboveCommand,
|
||||
CommandParameter = nodeVm
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Header = "在下方插入位置",
|
||||
Command = viewModel.PrepareInsertBelowCommand,
|
||||
CommandParameter = nodeVm
|
||||
},
|
||||
new Separator(),
|
||||
new MenuItem
|
||||
{
|
||||
Header = "全部展开",
|
||||
Tag = "ExpandAll"
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Header = "全部折叠",
|
||||
Tag = "CollapseAll"
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
// 右键点在空白处:只显示展开/折叠
|
||||
CncTreeView.ContextMenu = new ContextMenu
|
||||
{
|
||||
Items =
|
||||
{
|
||||
new MenuItem { Header = "全部展开", Tag = "ExpandAll" },
|
||||
new MenuItem { Header = "全部折叠", Tag = "CollapseAll" }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
viewModel.SelectedNode = nodeVm;
|
||||
UpdateNodeVisualState();
|
||||
UpdatePropertyEditorState();
|
||||
|
||||
CncTreeView.ContextMenu = new ContextMenu
|
||||
// 绑定展开/折叠事件
|
||||
foreach (var item in CncTreeView.ContextMenu.Items)
|
||||
{
|
||||
Items =
|
||||
if (item is MenuItem mi && mi.Tag is string tag)
|
||||
{
|
||||
new MenuItem
|
||||
mi.Click += (_, _) =>
|
||||
{
|
||||
Header = "\u5728\u4E0A\u65B9\u63D2\u5165\u4F4D\u7F6E",
|
||||
Command = viewModel.PrepareInsertAboveCommand,
|
||||
CommandParameter = nodeVm
|
||||
},
|
||||
new MenuItem
|
||||
{
|
||||
Header = "\u5728\u4E0B\u65B9\u63D2\u5165\u4F4D\u7F6E",
|
||||
Command = viewModel.PrepareInsertBelowCommand,
|
||||
CommandParameter = nodeVm
|
||||
}
|
||||
bool expand = tag == "ExpandAll";
|
||||
SetAllExpanded(CncTreeView.Items, expand);
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>递归设置所有 CncNodeViewModel 的展开状态</summary>
|
||||
private static void SetAllExpanded(System.Collections.IEnumerable items, bool expand)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
if (item is CncNodeViewModel nodeVm)
|
||||
{
|
||||
nodeVm.IsExpanded = expand;
|
||||
SetAllExpanded(nodeVm.Children, expand);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CncTreeView_LayoutUpdated(object sender, EventArgs e)
|
||||
{
|
||||
HideInlineDeleteButtons();
|
||||
UpdateNodeVisualState();
|
||||
UpdatePropertyEditorState();
|
||||
}
|
||||
|
||||
@@ -171,23 +286,7 @@ namespace XplorePlane.Views.Cnc
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
card.ClearValue(Border.BackgroundProperty);
|
||||
card.ClearValue(Border.BorderBrushProperty);
|
||||
ClearNodeTextForeground(card);
|
||||
}
|
||||
// 新样式通过 DataTemplate Triggers 控制高亮,无需 code-behind 干预
|
||||
}
|
||||
|
||||
private void UpdatePropertyEditorState()
|
||||
@@ -319,14 +418,7 @@ namespace XplorePlane.Views.Cnc
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
// 新样式不再使用 NodeCard,保留方法避免编译错误
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -61,9 +61,9 @@
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="4*" MinHeight="180" />
|
||||
<RowDefinition Height="6*" MinHeight="200" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="2*" MinHeight="80" />
|
||||
<RowDefinition Height="5*" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Border
|
||||
|
||||
@@ -597,7 +597,7 @@
|
||||
</telerik:RadRibbonGroup>
|
||||
</telerik:RadRibbonTab>
|
||||
<telerik:RadRibbonTab Header="帮助">
|
||||
<telerik:RadRibbonGroup Header="关于">
|
||||
<telerik:RadRibbonGroup Header="帮助">
|
||||
<telerik:RadRibbonGroup.Variants>
|
||||
<telerik:GroupVariant Priority="0" Variant="Large" />
|
||||
</telerik:RadRibbonGroup.Variants>
|
||||
|
||||
Reference in New Issue
Block a user