mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-20 08:16:34 +08:00
优化了中断功能。
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
Closing="Window_Closing">
|
||||
<Grid>
|
||||
<TextBox x:Name="LogTextBox"
|
||||
FontSize="14"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Auto"
|
||||
IsReadOnly="True"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
xmlns:local="clr-namespace:Serein.WorkBench"
|
||||
xmlns:custom="clr-namespace:Serein.WorkBench.Node.View"
|
||||
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
|
||||
Title="Dynamic Node Flow" Height="700" Width="1200"
|
||||
Title="Dynamic Node Flow" Height="900" Width="1400"
|
||||
AllowDrop="True" Drop="Window_Drop" DragOver="Window_DragOver"
|
||||
Loaded="Window_Loaded"
|
||||
ContentRendered="Window_ContentRendered"
|
||||
@@ -26,44 +26,47 @@
|
||||
</Window.InputBindings>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="1*"/>
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="3*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<DockPanel Grid.Column="0" Background="#F5F5F5">
|
||||
<DockPanel Grid.Column="0" Background="#F5F5F5">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="40"></RowDefinition>
|
||||
<RowDefinition Height="Auto"></RowDefinition>
|
||||
<RowDefinition Height="2*"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
<RowDefinition Height="3"></RowDefinition>
|
||||
<RowDefinition Height="3*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Margin="2,2,2,5" Grid.Row="0" >
|
||||
<Grid Margin="2,2,2,5" Grid.Row="0" Grid.ColumnSpan="2" >
|
||||
<Button Grid.Row="0" Content="保存项目" Click="ButtonSaveFile_Click" HorizontalAlignment="Left" Margin="5,5,5,5"/>
|
||||
<!--<Button Grid.Row="0" Content="卸载清空" Click="UnloadAllButton_Click" HorizontalAlignment="Right" Margin="5,5,5,5"/>-->
|
||||
</Grid>
|
||||
|
||||
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto">
|
||||
|
||||
<ScrollViewer Grid.Row="1" HorizontalScrollBarVisibility="Auto" Grid.ColumnSpan="2">
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<custom:ExpOpNodeControl x:Name="ExpOpNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||
<custom:ConditionNodeControl x:Name="ConditionNodeControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||
<custom:ConditionRegionControl x:Name="ConditionRegionControl" Margin="10" AllowDrop="True" PreviewMouseMove="BaseNodeControl_PreviewMouseMove"/>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
<ScrollViewer Grid.Row="2" VerticalScrollBarVisibility="Auto" MaxHeight="400">
|
||||
<ScrollViewer Grid.Row="1" VerticalScrollBarVisibility="Auto" MaxHeight="400" Grid.ColumnSpan="2" Margin="0,72,0,5" Grid.RowSpan="3">
|
||||
<StackPanel x:Name="DllStackPanel" Margin="5"/>
|
||||
</ScrollViewer>
|
||||
<Grid Grid.Row="3" >
|
||||
|
||||
<GridSplitter Grid.Row="3" Height="5" HorizontalAlignment="Stretch" VerticalAlignment="Center" ResizeBehavior="PreviousAndNext" Background="Gray" Grid.ColumnSpan="2"/>
|
||||
<Grid Grid.Row="4" Grid.ColumnSpan="2">
|
||||
|
||||
<themes:ObjectViewerControl x:Name="ObjectViewer"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</DockPanel>
|
||||
|
||||
|
||||
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray"/>
|
||||
<!--<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" DragDelta="GridSplitter_DragDelta"/>-->
|
||||
<GridSplitter Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
|
||||
|
||||
<Grid Grid.Column="2" >
|
||||
<Grid.ColumnDefinitions>
|
||||
|
||||
@@ -144,19 +144,21 @@ namespace Serein.WorkBench
|
||||
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
|
||||
ViewModel = new MainWindowViewModel(this);
|
||||
FlowEnvironment = ViewModel.FlowEnvironment;
|
||||
InitFlowEvent();
|
||||
ObjectViewer.FlowEnvironment = FlowEnvironment;
|
||||
|
||||
InitializeComponent();
|
||||
InitFlowEnvironmentEvent(); // 配置环境事件
|
||||
|
||||
logWindow = new LogWindow();
|
||||
logWindow.Show();
|
||||
// 重定向 Console 输出
|
||||
var logTextWriter = new LogTextWriter(WriteLog,() => logWindow.Clear());;
|
||||
var logTextWriter = new LogTextWriter(msg => logWindow.AppendText(msg), () => logWindow.Clear());;
|
||||
Console.SetOut(logTextWriter);
|
||||
|
||||
|
||||
InitUI();
|
||||
InitCanvasUI();
|
||||
|
||||
var project = App.FlowProjectData;
|
||||
if (project == null)
|
||||
@@ -164,12 +166,10 @@ namespace Serein.WorkBench
|
||||
return;
|
||||
}
|
||||
InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Lenght);// 设置画布大小
|
||||
|
||||
|
||||
FlowEnvironment.LoadProject(project, App.FileDataPath); // 加载项目
|
||||
}
|
||||
|
||||
private void InitFlowEvent()
|
||||
private void InitFlowEnvironmentEvent()
|
||||
{
|
||||
FlowEnvironment.OnDllLoad += FlowEnvironment_DllLoadEvent;
|
||||
// FlowEnvironment.OnLoadNode += FlowEnvironment_NodeLoadEvent;
|
||||
@@ -183,13 +183,13 @@ namespace Serein.WorkBench
|
||||
|
||||
FlowEnvironment.OnMonitorObjectChange += FlowEnvironment_OnMonitorObjectChange;
|
||||
FlowEnvironment.OnNodeInterruptStateChange += FlowEnvironment_OnNodeInterruptStateChange;
|
||||
FlowEnvironment.OnNodeInterruptTrigger += FlowEnvironment_OnNodeInterruptTrigger;
|
||||
FlowEnvironment.OnInterruptTrigger += FlowEnvironment_OnInterruptTrigger;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void InitUI()
|
||||
private void InitCanvasUI()
|
||||
{
|
||||
canvasTransformGroup = new TransformGroup();
|
||||
scaleTransform = new ScaleTransform();
|
||||
@@ -239,10 +239,7 @@ namespace Serein.WorkBench
|
||||
}
|
||||
#endregion
|
||||
|
||||
public void WriteLog(string message)
|
||||
{
|
||||
logWindow.AppendText(message);
|
||||
}
|
||||
|
||||
|
||||
#region 运行环境事件
|
||||
/// <summary>
|
||||
@@ -265,7 +262,7 @@ namespace Serein.WorkBench
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
private void FlowEnvironment_OnFlowRunComplete(FlowEventArgs eventArgs)
|
||||
{
|
||||
WriteLog("-------运行完成---------\r\n");
|
||||
Console.WriteLine("-------运行完成---------\r\n");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -462,20 +459,31 @@ namespace Serein.WorkBench
|
||||
private void FlowEnvironment_OnMonitorObjectChange(MonitorObjectEventArgs eventArgs)
|
||||
{
|
||||
string nodeGuid = eventArgs.NodeGuid;
|
||||
if (string.IsNullOrEmpty(ObjectViewer.NodeGuid)) // 如果没有加载过
|
||||
{
|
||||
ObjectViewer.NodeGuid = nodeGuid;
|
||||
ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点
|
||||
}
|
||||
else
|
||||
{
|
||||
// 加载过,如果显示的对象来源并非同一个节点,则停止监听之前的节点
|
||||
if (!ObjectViewer.NodeGuid.Equals(nodeGuid))
|
||||
NodeControlBase nodeControl = GuidToControl(nodeGuid);
|
||||
ObjectViewer.Dispatcher.BeginInvoke(() => {
|
||||
if (string.IsNullOrEmpty(ObjectViewer.NodeGuid)) // 如果没有加载过
|
||||
{
|
||||
FlowEnvironment.SetNodeFLowDataMonitorState(ObjectViewer.NodeGuid, false);
|
||||
ObjectViewer.NodeGuid = nodeGuid;
|
||||
ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点
|
||||
//ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点
|
||||
}
|
||||
ObjectViewer.RefreshObjectTree(eventArgs.NewData);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 加载过,如果显示的对象来源并非同一个节点,则停止监听之前的节点
|
||||
if (!ObjectViewer.NodeGuid.Equals(nodeGuid))
|
||||
{
|
||||
FlowEnvironment.SetNodeFLowDataMonitorState(ObjectViewer.NodeGuid, false);
|
||||
ObjectViewer.NodeGuid = nodeGuid;
|
||||
//ObjectViewer.LoadObjectInformation(eventArgs.NewData); // 加载节点
|
||||
}
|
||||
else
|
||||
{
|
||||
ObjectViewer.RefreshObjectTree(eventArgs.NewData);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -491,12 +499,28 @@ namespace Serein.WorkBench
|
||||
if (eventArgs.Class == InterruptClass.None)
|
||||
{
|
||||
nodeControl.ViewModel.IsInterrupt = false;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeControl.ViewModel.IsInterrupt = true;
|
||||
}
|
||||
|
||||
foreach (var menuItem in nodeControl.ContextMenu.Items)
|
||||
{
|
||||
if (menuItem is MenuItem menu)
|
||||
{
|
||||
if ("取消中断".Equals(menu.Header))
|
||||
{
|
||||
menu.Header = "在此中断";
|
||||
}
|
||||
else if ("在此中断".Equals(menu.Header))
|
||||
{
|
||||
menu.Header = "取消中断";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -505,12 +529,19 @@ namespace Serein.WorkBench
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
private void FlowEnvironment_OnNodeInterruptTrigger(NodeInterruptTriggerEventArgs eventArgs)
|
||||
private void FlowEnvironment_OnInterruptTrigger(InterruptTriggerEventArgs eventArgs)
|
||||
{
|
||||
string nodeGuid = eventArgs.NodeGuid;
|
||||
NodeControlBase nodeControl = GuidToControl(nodeGuid);
|
||||
|
||||
Console.WriteLine("节点触发了中断");
|
||||
if (nodeControl is null) return;
|
||||
if(eventArgs.Type == InterruptTriggerEventArgs.InterruptTriggerType.Exp)
|
||||
{
|
||||
Console.WriteLine($"表达式触发了中断:{eventArgs.Expression}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"节点触发了中断:{nodeGuid}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -579,7 +610,7 @@ namespace Serein.WorkBench
|
||||
var childNodeControl = CreateNodeControlOfNodeInfo(childNode, md);
|
||||
if (childNodeControl == null)
|
||||
{
|
||||
WriteLog($"无法为节点类型创建节点控件: {childNode.MethodName}\r\n");
|
||||
Console.WriteLine($"无法为节点类型创建节点控件: {childNode.MethodName}\r\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -687,13 +718,13 @@ namespace Serein.WorkBench
|
||||
{
|
||||
if (nodeControl?.ViewModel?.Node?.DebugSetting?.InterruptClass == InterruptClass.None)
|
||||
{
|
||||
FlowEnvironment.NodeInterruptChange(nodeGuid, InterruptClass.Branch);
|
||||
FlowEnvironment.SetNodeInterrupt(nodeGuid, InterruptClass.Branch);
|
||||
|
||||
menuItem.Header = "取消中断";
|
||||
}
|
||||
else
|
||||
{
|
||||
FlowEnvironment.NodeInterruptChange(nodeGuid, InterruptClass.None);
|
||||
FlowEnvironment.SetNodeInterrupt(nodeGuid, InterruptClass.None);
|
||||
menuItem.Header = "在此中断";
|
||||
|
||||
}
|
||||
@@ -707,6 +738,8 @@ namespace Serein.WorkBench
|
||||
var node = nodeControl?.ViewModel?.Node;
|
||||
if(node is not null)
|
||||
{
|
||||
FlowEnvironment.SetNodeFLowDataMonitorState(ObjectViewer.NodeGuid, false); // 通知环境,该节点的数据更新后需要传到UI
|
||||
ObjectViewer.NodeGuid = node.Guid;
|
||||
FlowEnvironment.SetNodeFLowDataMonitorState(node.Guid, true); // 通知环境,该节点的数据更新后需要传到UI
|
||||
}
|
||||
|
||||
@@ -2533,6 +2566,23 @@ namespace Serein.WorkBench
|
||||
break;
|
||||
}
|
||||
|
||||
// 计算角落
|
||||
//switch (localhost)
|
||||
//{
|
||||
// case Localhost.Right:
|
||||
// point = new Point(0, element.ActualHeight / 2); // 左边中心
|
||||
// break;
|
||||
// case Localhost.Left:
|
||||
// point = new Point(element.ActualWidth, element.ActualHeight / 2); // 右边中心
|
||||
// break;
|
||||
// case Localhost.Bottom:
|
||||
// point = new Point(element.ActualWidth / 2, 0); // 上边中心
|
||||
// break;
|
||||
// case Localhost.Top:
|
||||
// point = new Point(element.ActualWidth / 2, element.ActualHeight); // 下边中心
|
||||
// break;
|
||||
//}
|
||||
|
||||
// 将相对控件的坐标转换到画布中的全局坐标
|
||||
return element.TranslatePoint(point, canvas);
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace Serein.WorkBench.Node.ViewModel
|
||||
if (value != null)
|
||||
{
|
||||
Node.DebugSetting = value;
|
||||
OnPropertyChanged(nameof(DebugSetting));
|
||||
OnPropertyChanged(/*nameof(DebugSetting)*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -62,7 +62,7 @@ namespace Serein.WorkBench.Node.ViewModel
|
||||
if(value != null)
|
||||
{
|
||||
Node.MethodDetails = value;
|
||||
OnPropertyChanged(nameof(MethodDetails));
|
||||
OnPropertyChanged(/*nameof(MethodDetails)*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -74,7 +74,7 @@ namespace Serein.WorkBench.Node.ViewModel
|
||||
set
|
||||
{
|
||||
isInterrupt = value;
|
||||
OnPropertyChanged(nameof(IsInterrupt));
|
||||
OnPropertyChanged(/*nameof(IsInterrupt)*/);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Style.Triggers>
|
||||
<!-- 当 DebugSetting.IsInterrupt 为 True 时,显示红色边框 -->
|
||||
<DataTrigger Binding="{Binding IsInterrupt}" Value="True">
|
||||
<Setter Property="BorderBrush" Value="Red" />
|
||||
<Setter Property="BorderThickness" Value="2" />
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
d:DesignHeight="400" d:DesignWidth="400">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
@@ -14,16 +14,18 @@
|
||||
<!-- 树视图 -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal">
|
||||
<!--Click="RefreshButton_Click"
|
||||
Click="TimerRefreshButton_Click"-->
|
||||
<!--<Button Grid.Row="0" HorizontalAlignment="Left" Margin="4,2,4,2" Content="刷新" Width="100" Height="20" Name="RefreshButton"/>-->
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="0" >
|
||||
<!---->
|
||||
<!--<Button Grid.Row="0" HorizontalAlignment="Left" Margin="14,2,4,2" Content="监视" Width="100" Height="20" Name="TimerRefreshButton"/>-->
|
||||
<Button Grid.Row="0" HorizontalAlignment="Left" Margin="14,2,4,2" Content="添加监视表达式" Width="100" Height="20" Name="AddMonitorExpressionButton" Click="AddMonitorExpressionButton_Click"/>
|
||||
<!--<Button Grid.Row="0" HorizontalAlignment="Left" Margin="14,2,4,2" Content="添加监视表达式" Width="100" Height="20" Name="AddMonitorExpressionButton" Click="AddMonitorExpressionButton_Click"/>-->
|
||||
<Button Grid.Row="0" HorizontalAlignment="Left" Margin="4,2,4,2" Content="刷新" Width="40" Height="20" Name="RefreshButton" Click="RefreshButton_Click"/>
|
||||
<Button Grid.Row="0" HorizontalAlignment="Left" Margin="4,2,4,2" Content="添加监视表达式" Width="80" Height="20" Name="UpMonitorExpressionButton" Click="UpMonitorExpressionButton_Click"/>
|
||||
<TextBox x:Name="ExpressionTextBox" Margin="4,2,4,2" Width="300"/>
|
||||
|
||||
</StackPanel>
|
||||
<!-- 刷新按钮 -->
|
||||
|
||||
<!-- 树视图,用于显示对象属性 -->
|
||||
<TreeView FontSize="13" x:Name="ObjectTreeView" Grid.Row="1" />
|
||||
<TreeView FontSize="14" x:Name="ObjectTreeView" Grid.Row="1" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.NodeFlow.Tool.SereinExpression;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
@@ -18,7 +20,6 @@ using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Xml.Linq;
|
||||
using static Serein.WorkBench.Themes.TypeViewerWindow;
|
||||
using static System.Collections.Specialized.BitVector32;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
@@ -55,36 +56,89 @@ namespace Serein.WorkBench.Themes
|
||||
{
|
||||
private object _objectInstance;
|
||||
public string NodeGuid { get;set; }
|
||||
|
||||
// private NodeModelBase _nodeFlowData;
|
||||
public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
|
||||
public IFlowEnvironment FlowEnvironment { get;set; }
|
||||
public NodeModelBase NodeModel { get;set; }
|
||||
|
||||
public ObjectViewerControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private DateTime _lastRefreshTime = DateTime.MinValue; // 上次刷新时间
|
||||
private TimeSpan _refreshInterval = TimeSpan.FromSeconds(0.1); // 刷新间隔(2秒)
|
||||
|
||||
/// <summary>
|
||||
/// 加载对象信息,展示其成员
|
||||
/// </summary>
|
||||
/// <param name="obj">要展示的对象</param>
|
||||
//public void LoadObjectInformation(NodeModelBase nodeModel)
|
||||
public void LoadObjectInformation(object obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return;
|
||||
//IsTimerRefres = false;
|
||||
//TimerRefreshButton.Content = "定时刷新";
|
||||
|
||||
|
||||
// 当前时间
|
||||
var currentTime = DateTime.Now;
|
||||
|
||||
// 如果上次刷新时间和当前时间之间的差值小于设定的间隔,则跳过
|
||||
if (currentTime - _lastRefreshTime < _refreshInterval)
|
||||
{
|
||||
// 跳过过于频繁的刷新调用
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录这次的刷新时间
|
||||
_lastRefreshTime = currentTime;
|
||||
|
||||
_objectInstance = obj;
|
||||
RefreshObjectTree(obj);
|
||||
|
||||
}
|
||||
|
||||
private void AddMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
|
||||
///// <summary>
|
||||
///// 添加表达式
|
||||
///// </summary>
|
||||
///// <param name="sender"></param>
|
||||
///// <param name="e"></param>
|
||||
//private void AddMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
|
||||
// OpenInputDialog((exp) =>
|
||||
// {
|
||||
// FlowEnvironment.AddInterruptExpression(NodeGuid, exp);
|
||||
// });
|
||||
//}
|
||||
|
||||
|
||||
private void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
//RefreshObjectTree(_objectInstance);
|
||||
FlowEnvironment.SetNodeFLowDataMonitorState(NodeGuid, true);
|
||||
}
|
||||
|
||||
private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
//MonitorExpression = ExpressionTextBox.Text.ToString();
|
||||
|
||||
if(FlowEnvironment.AddInterruptExpression(NodeGuid, MonitorExpression))
|
||||
{
|
||||
if (string.IsNullOrEmpty(MonitorExpression))
|
||||
{
|
||||
ExpressionTextBox.Text = "表达式已清空";
|
||||
}
|
||||
else
|
||||
{
|
||||
UpMonitorExpressionButton.Content = "更新监视表达式";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 用于存储当前展开的节点路径
|
||||
private HashSet<string> _expandedNodePaths = new HashSet<string>();
|
||||
|
||||
/// <summary>
|
||||
/// 刷新对象属性树
|
||||
/// </summary>
|
||||
@@ -92,7 +146,19 @@ namespace Serein.WorkBench.Themes
|
||||
{
|
||||
if (obj is null)
|
||||
return;
|
||||
// _objectInstance = obj;
|
||||
// 当前时间
|
||||
var currentTime = DateTime.Now;
|
||||
|
||||
// 如果上次刷新时间和当前时间之间的差值小于设定的间隔,则跳过
|
||||
if (currentTime - _lastRefreshTime < _refreshInterval)
|
||||
{
|
||||
// 跳过过于频繁的刷新调用
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录这次的刷新时间
|
||||
_lastRefreshTime = currentTime;
|
||||
|
||||
var objectType = obj.GetType();
|
||||
|
||||
FlowDataDetails flowDataDetails = new FlowDataDetails
|
||||
@@ -124,9 +190,23 @@ namespace Serein.WorkBench.Themes
|
||||
rootNode.Items.Clear();
|
||||
AddMembersToTreeNode(rootNode, obj, objectType);
|
||||
}
|
||||
// 遍历节点,展开之前记录的节点
|
||||
ExpandPreviouslyExpandedNodes(rootNode);
|
||||
}
|
||||
|
||||
// 遍历并展开之前记录的节点
|
||||
private void ExpandPreviouslyExpandedNodes(TreeViewItem node)
|
||||
{
|
||||
if (_expandedNodePaths.Contains(GetNodeFullPath(node)))
|
||||
{
|
||||
node.IsExpanded = true;
|
||||
}
|
||||
|
||||
foreach (TreeViewItem child in node.Items)
|
||||
{
|
||||
ExpandPreviouslyExpandedNodes(child);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -138,7 +218,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// 添加父节点
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
private static void AddPlaceholderNode(TreeViewItem node)
|
||||
private void AddPlaceholderNode(TreeViewItem node)
|
||||
{
|
||||
node.Items.Add(new TreeViewItem { Header = "Loading..." });
|
||||
}
|
||||
@@ -148,7 +228,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private static void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
|
||||
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var item = (TreeViewItem)sender;
|
||||
|
||||
@@ -157,6 +237,8 @@ namespace Serein.WorkBench.Themes
|
||||
item.Items.Clear();
|
||||
if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj
|
||||
{
|
||||
// 记录当前节点的路径
|
||||
_expandedNodePaths.Add(GetNodeFullPath(item));
|
||||
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
|
||||
}
|
||||
}
|
||||
@@ -168,7 +250,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="treeViewNode"></param>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="type"></param>
|
||||
private static void AddMembersToTreeNode(TreeViewItem treeViewNode, object obj, Type type)
|
||||
private void AddMembersToTreeNode(TreeViewItem treeViewNode, object obj, Type type)
|
||||
{
|
||||
// 获取属性和字段
|
||||
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
@@ -176,11 +258,13 @@ namespace Serein.WorkBench.Themes
|
||||
{
|
||||
TreeViewItem memberNode = ConfigureTreeViewItem(obj, member);
|
||||
treeViewNode.Items.Add(memberNode);
|
||||
if (ConfigureTreeItemMenu(memberNode, member, out ContextMenu? contextMenu))
|
||||
{
|
||||
memberNode.ContextMenu = contextMenu; // 设置子项节点的事件
|
||||
|
||||
}
|
||||
|
||||
|
||||
//if (ConfigureTreeItemMenu(memberNode, member, out ContextMenu? contextMenu))
|
||||
//{
|
||||
// memberNode.ContextMenu = contextMenu; // 设置子项节点的事件
|
||||
//}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -191,23 +275,25 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <returns></returns>
|
||||
private static TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
|
||||
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
|
||||
if (member is PropertyInfo property)
|
||||
{
|
||||
|
||||
string propertyValue = GetPropertyValue(obj, property,out object value);
|
||||
FlowDataDetails flowDataDetails = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Property,
|
||||
DataType = property.PropertyType,
|
||||
Name = property.Name,
|
||||
DataValue = property,
|
||||
DataValue = value,
|
||||
DataPath = GetNodeFullPath(memberNode),
|
||||
};
|
||||
|
||||
memberNode.Tag = flowDataDetails;
|
||||
|
||||
string propertyValue = GetPropertyValue(obj, property);
|
||||
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} = {propertyValue}";
|
||||
|
||||
if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string))
|
||||
@@ -218,18 +304,19 @@ namespace Serein.WorkBench.Themes
|
||||
}
|
||||
else if (member is FieldInfo field)
|
||||
{
|
||||
|
||||
string fieldValue = GetFieldValue(obj, field, out object value);
|
||||
FlowDataDetails flowDataDetails = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Field,
|
||||
DataType = field.FieldType,
|
||||
Name = field.Name,
|
||||
DataValue = field,
|
||||
DataValue = value,
|
||||
DataPath = GetNodeFullPath(memberNode),
|
||||
};
|
||||
|
||||
memberNode.Tag = flowDataDetails;
|
||||
|
||||
|
||||
string fieldValue = GetFieldValue(obj, field);
|
||||
memberNode.Header = $"{field.Name} : {field.FieldType.Name} = {fieldValue}";
|
||||
|
||||
if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
|
||||
@@ -248,15 +335,22 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
private static string GetPropertyValue(object obj, PropertyInfo property)
|
||||
{
|
||||
private string GetPropertyValue(object obj, PropertyInfo property,out object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = property.GetValue(obj);
|
||||
return value?.ToString() ?? "null";
|
||||
|
||||
var properties = obj.GetType().GetProperties();
|
||||
|
||||
|
||||
|
||||
// 获取实例属性值
|
||||
value = property.GetValue(obj);
|
||||
return value?.ToString() ?? "null"; // 返回值或“null”
|
||||
}
|
||||
catch
|
||||
{
|
||||
value = null;
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
@@ -268,15 +362,16 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="field"></param>
|
||||
/// <returns></returns>
|
||||
private static string GetFieldValue(object obj, FieldInfo field)
|
||||
private string GetFieldValue(object obj, FieldInfo field, out object value)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = field.GetValue(obj);
|
||||
value = field.GetValue(obj);
|
||||
return value?.ToString() ?? "null";
|
||||
}
|
||||
catch
|
||||
{
|
||||
value = null;
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
@@ -288,13 +383,21 @@ namespace Serein.WorkBench.Themes
|
||||
/// <param name="member"></param>
|
||||
/// <param name="contextMenu"></param>
|
||||
/// <returns></returns>
|
||||
private static bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member, out ContextMenu? contextMenu)
|
||||
private bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member, out ContextMenu? contextMenu)
|
||||
{
|
||||
bool isChange = false;
|
||||
if (member is PropertyInfo property)
|
||||
{
|
||||
//isChange = true;
|
||||
isChange = true;
|
||||
contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
{
|
||||
string fullPath = GetNodeFullPath(memberNode);
|
||||
string copyValue = /*"@Get " + */fullPath;
|
||||
ExpressionTextBox.Text = copyValue;
|
||||
// Clipboard.SetDataObject(copyValue);
|
||||
|
||||
}));
|
||||
}
|
||||
else if (member is MethodInfo method)
|
||||
{
|
||||
@@ -305,29 +408,13 @@ namespace Serein.WorkBench.Themes
|
||||
{
|
||||
isChange = true;
|
||||
contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"取值表达式", (s, e) =>
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
{
|
||||
string fullPath = ObjectViewerControl.GetNodeFullPath(memberNode);
|
||||
string copyValue = "@Get " + fullPath;
|
||||
Clipboard.SetDataObject(copyValue);
|
||||
string fullPath = GetNodeFullPath(memberNode);
|
||||
string copyValue = /*"@Get " +*/ fullPath;
|
||||
ExpressionTextBox.Text = copyValue;
|
||||
// Clipboard.SetDataObject(copyValue);
|
||||
}));
|
||||
//contextMenu.Items.Add(MainWindow.CreateMenuItem($"监视中断", (s, e) =>
|
||||
//{
|
||||
// string fullPath = GetNodeFullPath(memberNode);
|
||||
// Clipboard.SetDataObject(fullPath);
|
||||
// OpenInputDialog((exp) =>
|
||||
// {
|
||||
// if (node.DebugSetting.InterruptExpression.Contains(exp))
|
||||
// {
|
||||
// Console.WriteLine("表达式已存在");
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// node.DebugSetting.InterruptExpression.Add(exp);
|
||||
// }
|
||||
// });
|
||||
|
||||
//}));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -336,12 +423,14 @@ namespace Serein.WorkBench.Themes
|
||||
return isChange;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前节点的完整路径,例如 "node1.node2.node3.node4"
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>节点路径</returns>
|
||||
private static string GetNodeFullPath(TreeViewItem node)
|
||||
private string GetNodeFullPath(TreeViewItem node)
|
||||
{
|
||||
if (node == null)
|
||||
return string.Empty;
|
||||
@@ -366,7 +455,7 @@ namespace Serein.WorkBench.Themes
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>父节点</returns>
|
||||
private static TreeViewItem GetParentTreeViewItem(TreeViewItem node)
|
||||
private TreeViewItem GetParentTreeViewItem(TreeViewItem node)
|
||||
{
|
||||
DependencyObject parent = VisualTreeHelper.GetParent(node);
|
||||
while (parent != null && !(parent is TreeViewItem))
|
||||
@@ -378,7 +467,7 @@ namespace Serein.WorkBench.Themes
|
||||
|
||||
|
||||
|
||||
private static InputDialog OpenInputDialog(Action<string> action)
|
||||
private InputDialog OpenInputDialog(Action<string> action)
|
||||
{
|
||||
var inputDialog = new InputDialog();
|
||||
inputDialog.Closed += (s, e) =>
|
||||
@@ -398,7 +487,6 @@ namespace Serein.WorkBench.Themes
|
||||
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 刷新按钮的点击事件
|
||||
///// </summary>
|
||||
|
||||
@@ -163,8 +163,14 @@ namespace Serein.WorkBench.Themes
|
||||
bool isChange = false;
|
||||
if (member is PropertyInfo property)
|
||||
{
|
||||
//isChange = true;
|
||||
isChange = true;
|
||||
contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"取值表达式", (s, e) =>
|
||||
{
|
||||
string fullPath = GetNodeFullPath(memberNode);
|
||||
string copyValue = "@Get " + fullPath;
|
||||
Clipboard.SetDataObject(copyValue);
|
||||
}));
|
||||
}
|
||||
else if (member is MethodInfo method)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user