对 流程图 新增 4个状态 已启用 已停用 执行到此 未参与本次执行 和右键菜单,执行到此,执行全部

This commit is contained in:
zhengxuan.zhang
2026-05-06 10:39:19 +08:00
parent fd4048a6c0
commit f5dceeceb7
8 changed files with 300 additions and 11 deletions
@@ -37,6 +37,7 @@ namespace XplorePlane.ViewModels.Cnc
private string _statusMessage = "请选择检测模块以编辑其流水线。";
private string _pipelineFileDisplayName = "未命名模块.xpm";
private string _currentFilePath;
private PipelineNodeViewModel _executionEndNode;
private bool _isSynchronizing;
private CancellationTokenSource _debounceCts;
@@ -65,6 +66,8 @@ namespace XplorePlane.ViewModels.Cnc
RemoveOperatorCommand = new DelegateCommand<PipelineNodeViewModel>(RemoveOperator);
ReorderOperatorCommand = new DelegateCommand<PipelineReorderArgs>(ReorderOperator);
ToggleOperatorEnabledCommand = new DelegateCommand<PipelineNodeViewModel>(ToggleOperatorEnabled);
ExecuteToNodeCommand = new DelegateCommand<PipelineNodeViewModel>(ExecuteToNode);
ClearExecutionRangeCommand = new DelegateCommand(ClearExecutionRange);
MoveNodeUpCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeUp);
MoveNodeDownCommand = new DelegateCommand<PipelineNodeViewModel>(MoveNodeDown);
NewPipelineCommand = new DelegateCommand(NewPipeline);
@@ -98,6 +101,16 @@ namespace XplorePlane.ViewModels.Cnc
private set => SetProperty(ref _pipelineFileDisplayName, value);
}
public PipelineNodeViewModel ExecutionEndNode
{
get => _executionEndNode;
private set
{
if (SetProperty(ref _executionEndNode, value))
UpdateExecutionRangeState();
}
}
public bool HasActiveModule => _activeModuleNode?.IsInspectionModule == true;
public Visibility EditorVisibility => HasActiveModule ? Visibility.Visible : Visibility.Collapsed;
@@ -112,6 +125,10 @@ namespace XplorePlane.ViewModels.Cnc
public ICommand ToggleOperatorEnabledCommand { get; }
public ICommand ExecuteToNodeCommand { get; }
public ICommand ClearExecutionRangeCommand { get; }
public ICommand MoveNodeUpCommand { get; }
public ICommand MoveNodeDownCommand { get; }
@@ -179,6 +196,7 @@ namespace XplorePlane.ViewModels.Cnc
LoadNodeParameters(node, null);
PipelineNodes.Add(node);
SelectedNode = node;
UpdateExecutionRangeState();
PersistActiveModule($"已添加算子:{displayName}");
}
@@ -190,6 +208,10 @@ namespace XplorePlane.ViewModels.Cnc
var removedIndex = PipelineNodes.IndexOf(node);
PipelineNodes.Remove(node);
RenumberNodes();
if (ReferenceEquals(ExecutionEndNode, node))
ExecutionEndNode = null;
else
UpdateExecutionRangeState();
SelectNeighborAfterRemoval(removedIndex);
PersistActiveModule($"已移除算子:{node.DisplayName}");
@@ -206,6 +228,7 @@ namespace XplorePlane.ViewModels.Cnc
PipelineNodes.Move(index, index - 1);
RenumberNodes();
UpdateExecutionRangeState();
PersistActiveModule($"已上移算子:{node.DisplayName}");
}
@@ -225,6 +248,7 @@ namespace XplorePlane.ViewModels.Cnc
var node = PipelineNodes[oldIndex];
PipelineNodes.Move(oldIndex, newIndex);
RenumberNodes();
UpdateExecutionRangeState();
SelectedNode = node;
PersistActiveModule($"已调整算子顺序:{node.DisplayName}");
}
@@ -240,6 +264,7 @@ namespace XplorePlane.ViewModels.Cnc
PipelineNodes.Move(index, index + 1);
RenumberNodes();
UpdateExecutionRangeState();
PersistActiveModule($"已下移算子:{node.DisplayName}");
}
@@ -255,6 +280,25 @@ namespace XplorePlane.ViewModels.Cnc
: $"已停用算子:{node.DisplayName}");
}
private void ExecuteToNode(PipelineNodeViewModel node)
{
if (!HasActiveModule || node == null || !PipelineNodes.Contains(node))
return;
SelectedNode = node;
ExecutionEndNode = node;
PersistActiveModule($"已设置执行截止点:{node.DisplayName}");
}
private void ClearExecutionRange()
{
if (!HasActiveModule || ExecutionEndNode == null)
return;
ExecutionEndNode = null;
PersistActiveModule("已切换为执行全部节点");
}
private void NewPipeline()
{
if (!HasActiveModule)
@@ -262,6 +306,7 @@ namespace XplorePlane.ViewModels.Cnc
PipelineNodes.Clear();
SelectedNode = null;
ExecutionEndNode = null;
_currentFilePath = null;
PipelineFileDisplayName = GetActivePipelineFileDisplayName();
PersistActiveModule("已为当前检测模块新建空流水线。");
@@ -326,6 +371,7 @@ namespace XplorePlane.ViewModels.Cnc
{
PipelineNodes.Clear();
SelectedNode = null;
ExecutionEndNode = null;
var orderedNodes = (pipeline?.Nodes ?? new List<PipelineNodeModel>())
.OrderBy(node => node.Order)
@@ -346,6 +392,7 @@ namespace XplorePlane.ViewModels.Cnc
}
SelectedNode = PipelineNodes.FirstOrDefault();
UpdateExecutionRangeState();
if (string.IsNullOrEmpty(_currentFilePath))
PipelineFileDisplayName = GetActivePipelineFileDisplayName();
StatusMessage = HasActiveModule
@@ -423,7 +470,7 @@ namespace XplorePlane.ViewModels.Cnc
try
{
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:开始执行,节点数={Count}", PipelineNodes.Count);
var result = await _executionService.ExecutePipelineAsync(PipelineNodes, sourceImage, null, token);
var result = await _executionService.ExecutePipelineAsync(GetNodesInExecutionScope(), sourceImage, null, token);
_logger.Info("[图像链路][CNC] ExecutePreviewAsync:执行完成,推送结果图像");
_mainViewportService.SetManualImage(result, string.Empty);
_eventAggregator?.GetEvent<PipelinePreviewUpdatedEvent>()
@@ -459,6 +506,15 @@ namespace XplorePlane.ViewModels.Cnc
};
}
private IEnumerable<PipelineNodeViewModel> GetNodesInExecutionScope()
{
var orderedNodes = PipelineNodes.OrderBy(node => node.Order);
if (ExecutionEndNode == null)
return orderedNodes;
return orderedNodes.Where(node => node.Order <= ExecutionEndNode.Order);
}
private string GetActivePipelineName()
{
if (!HasActiveModule)
@@ -492,6 +548,19 @@ namespace XplorePlane.ViewModels.Cnc
}
}
private void UpdateExecutionRangeState()
{
if (_executionEndNode != null && !PipelineNodes.Contains(_executionEndNode))
_executionEndNode = null;
var endOrder = _executionEndNode?.Order;
foreach (var node in PipelineNodes)
{
node.IsExecutionEndNode = endOrder.HasValue && node.Order == endOrder.Value;
node.IsSkippedByExecutionRange = endOrder.HasValue && node.Order > endOrder.Value;
}
}
private void SelectNeighborAfterRemoval(int removedIndex)
{
if (PipelineNodes.Count == 0)
@@ -23,6 +23,10 @@ namespace XplorePlane.ViewModels
ICommand ToggleOperatorEnabledCommand { get; }
ICommand ExecuteToNodeCommand { get; }
ICommand ClearExecutionRangeCommand { get; }
ICommand MoveNodeUpCommand { get; }
ICommand MoveNodeDownCommand { get; }
@@ -39,6 +39,7 @@ namespace XplorePlane.ViewModels
private string _statusMessage = string.Empty;
private string _pipelineFileDisplayName = DefaultPipelineFileDisplayName;
private string _currentFilePath;
private PipelineNodeViewModel _executionEndNode;
private CancellationTokenSource _executionCts;
private CancellationTokenSource _debounceCts;
@@ -64,6 +65,8 @@ namespace XplorePlane.ViewModels
ReorderOperatorCommand = new DelegateCommand<PipelineReorderArgs>(ReorderOperator);
ToggleOperatorEnabledCommand = new DelegateCommand<PipelineNodeViewModel>(ToggleOperatorEnabled);
ExecutePipelineCommand = new DelegateCommand(async () => await ExecutePipelineAsync(), () => !IsExecuting && SourceImage != null);
ExecuteToNodeCommand = new DelegateCommand<PipelineNodeViewModel>(async node => await ExecuteToNodeAsync(node), CanExecuteToNode);
ClearExecutionRangeCommand = new DelegateCommand(async () => await ClearExecutionRangeAsync(), CanClearExecutionRange);
CancelExecutionCommand = new DelegateCommand(CancelExecution, () => IsExecuting);
NewPipelineCommand = new DelegateCommand(NewPipeline);
SavePipelineCommand = new DelegateCommand(async () => await SavePipelineAsync());
@@ -162,6 +165,20 @@ namespace XplorePlane.ViewModels
private set => SetProperty(ref _pipelineFileDisplayName, value);
}
public PipelineNodeViewModel ExecutionEndNode
{
get => _executionEndNode;
private set
{
if (SetProperty(ref _executionEndNode, value))
{
UpdateExecutionRangeState();
ExecuteToNodeCommand.RaiseCanExecuteChanged();
ClearExecutionRangeCommand.RaiseCanExecuteChanged();
}
}
}
// ── Commands ──────────────────────────────────────────────────
public DelegateCommand<string> AddOperatorCommand { get; }
@@ -169,6 +186,8 @@ namespace XplorePlane.ViewModels
public DelegateCommand<PipelineReorderArgs> ReorderOperatorCommand { get; }
public DelegateCommand<PipelineNodeViewModel> ToggleOperatorEnabledCommand { get; }
public DelegateCommand ExecutePipelineCommand { get; }
public DelegateCommand<PipelineNodeViewModel> ExecuteToNodeCommand { get; }
public DelegateCommand ClearExecutionRangeCommand { get; }
public DelegateCommand CancelExecutionCommand { get; }
public DelegateCommand NewPipelineCommand { get; }
public DelegateCommand SavePipelineCommand { get; }
@@ -184,6 +203,8 @@ namespace XplorePlane.ViewModels
ICommand IPipelineEditorHostViewModel.RemoveOperatorCommand => RemoveOperatorCommand;
ICommand IPipelineEditorHostViewModel.ReorderOperatorCommand => ReorderOperatorCommand;
ICommand IPipelineEditorHostViewModel.ToggleOperatorEnabledCommand => ToggleOperatorEnabledCommand;
ICommand IPipelineEditorHostViewModel.ExecuteToNodeCommand => ExecuteToNodeCommand;
ICommand IPipelineEditorHostViewModel.ClearExecutionRangeCommand => ClearExecutionRangeCommand;
ICommand IPipelineEditorHostViewModel.MoveNodeUpCommand => MoveNodeUpCommand;
ICommand IPipelineEditorHostViewModel.MoveNodeDownCommand => MoveNodeDownCommand;
ICommand IPipelineEditorHostViewModel.NewPipelineCommand => NewPipelineCommand;
@@ -234,6 +255,7 @@ namespace XplorePlane.ViewModels
LoadNodeParameters(node);
PipelineNodes.Add(node);
SelectedNode = node;
UpdateExecutionRangeState();
_logger.Info("节点已添加到 PipelineNodes{Key} ({DisplayName}),当前节点数={Count}",
operatorKey, displayName, PipelineNodes.Count);
SetInfoStatus($"已添加算子:{displayName}");
@@ -247,6 +269,10 @@ namespace XplorePlane.ViewModels
var removedIndex = PipelineNodes.IndexOf(node);
PipelineNodes.Remove(node);
RenumberNodes();
if (ReferenceEquals(ExecutionEndNode, node))
ExecutionEndNode = null;
else
UpdateExecutionRangeState();
SelectNeighborAfterRemoval(removedIndex);
SetInfoStatus($"已移除算子:{node.DisplayName}");
@@ -260,6 +286,7 @@ namespace XplorePlane.ViewModels
if (index <= 0) return;
PipelineNodes.Move(index, index - 1);
RenumberNodes();
UpdateExecutionRangeState();
TriggerDebouncedExecution();
}
@@ -270,6 +297,7 @@ namespace XplorePlane.ViewModels
if (index < 0 || index >= PipelineNodes.Count - 1) return;
PipelineNodes.Move(index, index + 1);
RenumberNodes();
UpdateExecutionRangeState();
TriggerDebouncedExecution();
}
@@ -287,6 +315,7 @@ namespace XplorePlane.ViewModels
PipelineNodes.RemoveAt(oldIndex);
PipelineNodes.Insert(newIndex, node);
RenumberNodes();
UpdateExecutionRangeState();
SelectedNode = node;
SetInfoStatus($"已调整算子顺序:{node.DisplayName}");
TriggerDebouncedExecution();
@@ -304,6 +333,34 @@ namespace XplorePlane.ViewModels
TriggerDebouncedExecution();
}
private bool CanExecuteToNode(PipelineNodeViewModel node) =>
node != null && PipelineNodes.Contains(node) && !IsExecuting && SourceImage != null;
private async Task ExecuteToNodeAsync(PipelineNodeViewModel node)
{
if (!CanExecuteToNode(node))
return;
SelectedNode = node;
ExecutionEndNode = node;
await ExecutePipelineAsync();
}
private bool CanClearExecutionRange() =>
ExecutionEndNode != null && !IsExecuting;
private async Task ClearExecutionRangeAsync()
{
if (ExecutionEndNode == null)
return;
ExecutionEndNode = null;
SetInfoStatus("已切换为执行全部节点");
if (SourceImage != null)
await ExecutePipelineAsync();
}
private void RenumberNodes()
{
for (int i = 0; i < PipelineNodes.Count; i++)
@@ -367,10 +424,15 @@ namespace XplorePlane.ViewModels
_executionCts?.Cancel();
_executionCts = new CancellationTokenSource();
var token = _executionCts.Token;
var executionNodes = GetNodesInExecutionScope()
.Where(n => n.IsEnabled)
.OrderBy(n => n.Order)
.ToList();
IsExecuting = true;
SetInfoStatus("正在执行流水线...");
_logger.Info("[图像链路] ExecutePipelineAsync:开始执行,节点数={Count}", PipelineNodes.Count);
SetInfoStatus(BuildExecutionStartMessage(executionNodes.Count));
_logger.Info("[图像链路] ExecutePipelineAsync:开始执行,范围节点数={Count},截止节点={Node}",
executionNodes.Count, ExecutionEndNode?.DisplayName ?? "<all>");
try
{
@@ -378,10 +440,10 @@ namespace XplorePlane.ViewModels
SetInfoStatus($"执行中:{p.CurrentOperator} ({p.CurrentStep}/{p.TotalSteps})"));
var result = await _executionService.ExecutePipelineAsync(
PipelineNodes, SourceImage, progress, token);
executionNodes, SourceImage, progress, token);
PreviewImage = result;
SetInfoStatus("流水线执行完成");
SetInfoStatus(BuildExecutionCompletedMessage(executionNodes.Count));
_logger.Info("[图像链路] ExecutePipelineAsync:执行完成,准备发布 PipelinePreviewUpdatedEvent");
PublishPipelinePreviewUpdated(result, StatusMessage);
}
@@ -408,7 +470,7 @@ namespace XplorePlane.ViewModels
private bool TryReportInvalidParameters()
{
var firstInvalidNode = PipelineNodes
var firstInvalidNode = GetNodesInExecutionScope()
.Where(n => n.IsEnabled)
.OrderBy(n => n.Order)
.FirstOrDefault(n => n.Parameters.Any(p => !p.IsValueValid));
@@ -549,6 +611,7 @@ namespace XplorePlane.ViewModels
{
PipelineNodes.Clear();
SelectedNode = null;
ExecutionEndNode = null;
PipelineName = "新建流水线";
PreviewImage = null;
_currentFilePath = null;
@@ -648,6 +711,7 @@ namespace XplorePlane.ViewModels
PipelineNodes.Clear();
SelectedNode = null;
ExecutionEndNode = null;
PipelineName = model.Name;
SelectedDevice = model.DeviceId;
@@ -676,6 +740,8 @@ namespace XplorePlane.ViewModels
PipelineNodes.Add(node);
}
UpdateExecutionRangeState();
_logger.Info("流水线已加载:{Name},节点数={Count}", model.Name, PipelineNodes.Count);
SetInfoStatus($"已加载流水线:{model.Name}{PipelineNodes.Count} 个节点)");
}
@@ -704,6 +770,44 @@ namespace XplorePlane.ViewModels
};
}
private System.Collections.Generic.IEnumerable<PipelineNodeViewModel> GetNodesInExecutionScope()
{
var orderedNodes = PipelineNodes.OrderBy(n => n.Order);
if (ExecutionEndNode == null)
return orderedNodes;
return orderedNodes.Where(n => n.Order <= ExecutionEndNode.Order);
}
private void UpdateExecutionRangeState()
{
if (_executionEndNode != null && !PipelineNodes.Contains(_executionEndNode))
_executionEndNode = null;
var endOrder = _executionEndNode?.Order;
foreach (var node in PipelineNodes)
{
node.IsExecutionEndNode = endOrder.HasValue && node.Order == endOrder.Value;
node.IsSkippedByExecutionRange = endOrder.HasValue && node.Order > endOrder.Value;
}
}
private string BuildExecutionStartMessage(int executionCount)
{
if (ExecutionEndNode == null)
return "正在执行流水线...";
return $"正在执行到“{ExecutionEndNode.DisplayName}” ({executionCount} 个有效节点)...";
}
private string BuildExecutionCompletedMessage(int executionCount)
{
if (ExecutionEndNode == null)
return "流水线执行完成";
return $"已执行到“{ExecutionEndNode.DisplayName}” ({executionCount} 个有效节点)";
}
private static string GetPipelineDirectory()
{
var dir = Path.Combine(
@@ -11,6 +11,8 @@ namespace XplorePlane.ViewModels
private int _order;
private bool _isSelected;
private bool _isEnabled = true;
private bool _isExecutionEndNode;
private bool _isSkippedByExecutionRange;
public PipelineNodeViewModel(string operatorKey, string displayName, string iconPath = null)
{
@@ -51,9 +53,49 @@ namespace XplorePlane.ViewModels
public bool IsEnabled
{
get => _isEnabled;
set => SetProperty(ref _isEnabled, value);
set
{
if (SetProperty(ref _isEnabled, value))
RaisePropertyChanged(nameof(NodeStateText));
}
}
public ObservableCollection<ProcessorParameterVM> Parameters { get; }
public bool IsExecutionEndNode
{
get => _isExecutionEndNode;
set
{
if (SetProperty(ref _isExecutionEndNode, value))
RaisePropertyChanged(nameof(NodeStateText));
}
}
public bool IsSkippedByExecutionRange
{
get => _isSkippedByExecutionRange;
set
{
if (SetProperty(ref _isSkippedByExecutionRange, value))
RaisePropertyChanged(nameof(NodeStateText));
}
}
public string NodeStateText
{
get
{
if (IsExecutionEndNode && !IsEnabled)
return "执行到此(停用)";
if (IsExecutionEndNode)
return "执行到此";
if (!IsEnabled)
return "已停用";
if (IsSkippedByExecutionRange)
return "未参与本次执行";
return "已启用";
}
}
}
}
}
+1 -1
View File
@@ -205,7 +205,7 @@ namespace XplorePlane.ViewModels
LoadImageCommand = new DelegateCommand(ExecuteLoadImage);
OpenPipelineEditorCommand = new DelegateCommand(() => ShowWindow(new Views.PipelineEditorWindow(), "流水线编辑器"));
OpenCncEditorCommand = new DelegateCommand(ExecuteOpenCncEditor);
OpenMatrixEditorCommand = new DelegateCommand(() => ShowWindow(new Views.Cnc.MatrixEditorWindow(), "矩阵编排"));
OpenToolboxCommand = new DelegateCommand(ExecuteOpenToolbox);
@@ -99,6 +99,19 @@
Content="加载"
Style="{StaticResource ToolbarBtn}"
ToolTip="加载流水线" />
<Button
Width="84"
Command="{Binding ExecuteToNodeCommand}"
CommandParameter="{Binding SelectedNode}"
Content="执行到当前"
Style="{StaticResource ToolbarBtn}"
ToolTip="执行到当前选中的节点" />
<Button
Width="68"
Command="{Binding ClearExecutionRangeCommand}"
Content="执行全部"
Style="{StaticResource ToolbarBtn}"
ToolTip="清除截止位置并执行全部节点" />
</StackPanel>
<TextBlock
@@ -132,10 +145,20 @@
x:Name="NodeContainer"
Margin="2"
Padding="2"
Tag="{Binding DataContext, ElementName=RootControl}"
Background="Transparent"
BorderBrush="Transparent"
BorderThickness="1"
CornerRadius="3">
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="执行到此处"
Command="{Binding PlacementTarget.Tag.ExecuteToNodeCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
CommandParameter="{Binding}" />
<MenuItem Header="执行全部"
Command="{Binding PlacementTarget.Tag.ClearExecutionRangeCommand, RelativeSource={RelativeSource AncestorType=ContextMenu}}" />
</ContextMenu>
</Border.ContextMenu>
<Grid x:Name="NodeRoot" MinHeight="48">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="44" />
@@ -193,7 +216,7 @@
FontFamily="{StaticResource UiFont}"
FontSize="10"
Foreground="#6E6E6E"
Text="已启用" />
Text="{Binding NodeStateText}" />
</StackPanel>
</Grid>
</Border>
@@ -209,13 +232,37 @@
<Setter TargetName="IconBorder" Property="BorderBrush" Value="{StaticResource DisabledNodeLine}" />
<Setter TargetName="IconBorder" Property="Background" Value="#ECECEC" />
<Setter TargetName="NodeTitle" Property="Foreground" Value="{StaticResource DisabledNodeText}" />
<Setter TargetName="NodeState" Property="Text" Value="已停用" />
<Setter TargetName="NodeState" Property="Foreground" Value="#9A6767" />
</DataTrigger>
<DataTrigger Binding="{Binding IsExecutionEndNode}" Value="True">
<Setter TargetName="NodeContainer" Property="Background" Value="#E8F6EA" />
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#4F9D69" />
<Setter TargetName="IconBorder" Property="Background" Value="#E8F6EA" />
<Setter TargetName="IconBorder" Property="BorderBrush" Value="#4F9D69" />
<Setter TargetName="NodeState" Property="Foreground" Value="#2E7D32" />
</DataTrigger>
<DataTrigger Binding="{Binding IsSkippedByExecutionRange}" Value="True">
<Setter TargetName="NodeContainer" Property="Background" Value="#FAFAFA" />
<Setter TargetName="NodeContainer" Property="Opacity" Value="0.72" />
<Setter TargetName="TopLine" Property="Stroke" Value="#D0D0D0" />
<Setter TargetName="BottomLine" Property="Stroke" Value="#D0D0D0" />
<Setter TargetName="IconBorder" Property="BorderBrush" Value="#C8C8C8" />
<Setter TargetName="IconBorder" Property="Background" Value="#F4F4F4" />
<Setter TargetName="NodeTitle" Property="Foreground" Value="#909090" />
<Setter TargetName="NodeState" Property="Foreground" Value="#A0A0A0" />
</DataTrigger>
<DataTrigger Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True">
<Setter TargetName="NodeContainer" Property="Background" Value="#D9ECFF" />
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#5B9BD5" />
</DataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsExecutionEndNode}" Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="NodeContainer" Property="Background" Value="#DFF0E3" />
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#3F8A58" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsEnabled}" Value="False" />
@@ -225,6 +272,15 @@
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#7E9AB6" />
<Setter TargetName="NodeContainer" Property="Opacity" Value="1" />
</MultiDataTrigger>
<MultiDataTrigger>
<MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsSkippedByExecutionRange}" Value="True" />
<Condition Binding="{Binding RelativeSource={RelativeSource AncestorType=ListBoxItem}, Path=IsSelected}" Value="True" />
</MultiDataTrigger.Conditions>
<Setter TargetName="NodeContainer" Property="Background" Value="#EEF1F4" />
<Setter TargetName="NodeContainer" Property="BorderBrush" Value="#AAB4BF" />
<Setter TargetName="NodeContainer" Property="Opacity" Value="0.9" />
</MultiDataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
</ListBox.ItemTemplate>
@@ -59,6 +59,8 @@ namespace XplorePlane.Views
PipelineListBox.PreviewMouseMove += OnPreviewMouseMove;
PipelineListBox.PreviewMouseLeftButtonUp -= OnPreviewMouseLeftButtonUp;
PipelineListBox.PreviewMouseLeftButtonUp += OnPreviewMouseLeftButtonUp;
PipelineListBox.PreviewMouseRightButtonDown -= OnPreviewMouseRightButtonDown;
PipelineListBox.PreviewMouseRightButtonDown += OnPreviewMouseRightButtonDown;
PipelineListBox.MouseDoubleClick -= OnMouseDoubleClick;
PipelineListBox.MouseDoubleClick += OnMouseDoubleClick;
PipelineListBox.PreviewKeyDown -= OnPreviewKeyDown;
@@ -133,6 +135,16 @@ namespace XplorePlane.Views
ResetDragState();
}
private void OnPreviewMouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var clickedNode = FindNodeFromOriginalSource(e.OriginalSource);
if (clickedNode == null || IsInteractiveChild(e.OriginalSource))
return;
PipelineListBox.SelectedItem = clickedNode;
PipelineListBox.Focus();
}
private void OnMouseDoubleClick(object sender, MouseButtonEventArgs e)
{
var vm = DataContext as IPipelineEditorHostViewModel;
+2
View File
@@ -17,6 +17,7 @@
<Page Remove="MainWindow.xaml" />
<Page Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml" />
<Page Remove="Views\ImageProcessing\ImageProcessingWindow.xaml" />
<Page Remove="Views\ImageProcessing\PipelineEditorWindow.xaml" />
<Page Remove="Views\Main\MainWindowB.xaml" />
<Page Remove="Views\Main\NavigationPanelView.xaml" />
</ItemGroup>
@@ -151,6 +152,7 @@
</None>
<Compile Remove="Views\ImageProcessing\ImageProcessingPanelView.xaml.cs" />
<Compile Remove="Views\ImageProcessing\ImageProcessingWindow.xaml.cs" />
<Compile Remove="Views\ImageProcessing\PipelineEditorWindow.xaml.cs" />
<Compile Remove="Views\Main\MainWindowB.xaml.cs" />
<Compile Remove="Views\Main\NavigationPanelView.xaml.cs" />
<Content Include="XplorerPlane.ico">