diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index f2d4214..8662879 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -319,7 +319,7 @@ namespace Serein.Library.Api /// /// IOC容器发生变化 /// - public delegate void IOCMembersChangedHandler(); + public delegate void IOCMembersChangedHandler(IOCMembersChangedEventArgs eventArgs); /// @@ -338,18 +338,39 @@ namespace Serein.Library.Api /// Completeuild, } - public IOCMembersChangedEventArgs(Type[] types, object[] dependencies, object[] unfinishedDependencies) + public IOCMembersChangedEventArgs(string key, object instance) { - this.Types = types; - this.Dependencies = dependencies; - this.UnfinishedDependencies = unfinishedDependencies; + this.Key = key; + this.Instance = instance; } - public Type[] Types { get; protected set; } - public object[] Dependencies { get; private set; } - public object[] UnfinishedDependencies { get; private set; } + public string Key { get; private set; } + public object Instance { get; private set; } } + //public class IOCMembersChangedEventArgs : FlowEventArgs + //{ + // //public enum EventType + // //{ + // // /// + // // /// 登记了类型 + // // /// + // // Registered, + // // /// + // // /// 构建了类型 + // // /// + // // Completeuild, + // //} + // public IOCMembersChangedEventArgs(Type[] types, object[] dependencies, object[] unfinishedDependencies) + // { + // this.Types = types; + // this.Dependencies = dependencies; + // this.UnfinishedDependencies = unfinishedDependencies; + // } + // public Type[] Types { get; protected set; } + // public object[] Dependencies { get; private set; } + // public object[] UnfinishedDependencies { get; private set; } + //} public interface IFlowEnvironment { #region 属性 @@ -420,9 +441,26 @@ namespace Serein.Library.Api /// event ExpInterruptTriggerHandler OnInterruptTrigger; + /// + /// IOC容器发生改变 + /// + event IOCMembersChangedHandler OnIOCMembersChanged; + #endregion + + /// + /// 获取方法描述 + /// + /// + /// + /// + bool TryGetMethodDetails(string methodName, out MethodDetails md); + + + //bool TryGetNodeData(string methodName, out NodeData node); + #region Workbench /// @@ -445,14 +483,7 @@ namespace Serein.Library.Api /// 清理加载的DLL(待更改) /// void ClearAll(); - /// - /// 获取方法描述 - /// - /// - /// - /// - bool TryGetMethodDetails(string methodName, out MethodDetails md); - + /// /// 开始运行 @@ -517,7 +548,7 @@ namespace Serein.Library.Api /// /// /// - bool AddInterruptExpression(object obj, string expression); + bool AddInterruptExpression(string key, string expression); /// /// 添加作用于指定节点的中断表达式 /// @@ -538,7 +569,7 @@ namespace Serein.Library.Api /// /// 需要监视的对象 /// 是否启用监视 - void SetMonitorObjState(object obj, bool isMonitor); + void SetMonitorObjState(string key,bool isMonitor); /// /// 检查一个对象是否处于监听状态,如果是,则传出与该对象相关的表达式(用于中断),如果不是,则返回false。 @@ -546,7 +577,7 @@ namespace Serein.Library.Api /// 判断的对象 /// 表达式 /// - bool CheckObjMonitorState(object obj, out List exps); + bool CheckObjMonitorState(string key, out List exps); /// diff --git a/Library/Entity/MethodDetails.cs b/Library/Entity/MethodDetails.cs index 0b748f9..55b9155 100644 --- a/Library/Entity/MethodDetails.cs +++ b/Library/Entity/MethodDetails.cs @@ -37,7 +37,7 @@ namespace Serein.Library.Entity /// /// 是否保护参数 /// - public bool IsProtectionParameter { get; set; } = true; + public bool IsProtectionParameter { get; set; } = false; /// /// 作用实例的类型 diff --git a/Library/Entity/NodeData.cs b/Library/Entity/NodeData.cs new file mode 100644 index 0000000..e0ef99e --- /dev/null +++ b/Library/Entity/NodeData.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Serein.Library.Entity +{ + public class NodeData + { + } +} diff --git a/Library/Enums/ConnectionType.cs b/Library/Enums/ConnectionType.cs index 7a90ea3..e7706dc 100644 --- a/Library/Enums/ConnectionType.cs +++ b/Library/Enums/ConnectionType.cs @@ -7,25 +7,26 @@ namespace Serein.Library.Enums public enum ConnectionType { /// - /// 不执行分支 + /// 默认属性 /// None, /// - /// 真分支 + /// 上游分支(执行当前节点前会执行一次上游分支),默认执行。 + /// + Upstream, + /// + /// 真分支(表示当前节点顺利完成) /// IsSucceed, /// - /// 假分支 + /// 假分支(一般用于条件控件,条件为假时才会触发该类型的分支) /// IsFail, /// - /// 异常发生分支 + /// 异常发生分支(当前节点对应的方法执行时出现非预期的异常) /// IsError, - /// - /// 上游分支(执行当前节点前会执行一次上游分支) - /// - Upstream, + } diff --git a/Library/Utils/SereinIoc.cs b/Library/Utils/SereinIoc.cs index da5f435..15bf99f 100644 --- a/Library/Utils/SereinIoc.cs +++ b/Library/Utils/SereinIoc.cs @@ -33,7 +33,7 @@ namespace Serein.Library.Utils /// private readonly ConcurrentDictionary> _unfinishedDependencies; - + public event IOCMembersChangedHandler OnIOCMembersChanged; public SereinIOC() { @@ -112,7 +112,7 @@ namespace Serein.Library.Utils // 是接口类型,存在注册信息 Register(type);// 注册类型信息 value = Instantiate(implementationType); // 创建实例对象,并注入依赖 - _dependencies.TryAdd(type.FullName, value); // 登记到IOC容器中 + CustomRegisterInstance(type.FullName, value);// 登记到IOC容器中 _typeMappings.TryRemove(type.FullName, out _); // 取消类型的注册信息 } else @@ -127,7 +127,7 @@ namespace Serein.Library.Utils // 不是接口,直接注册 Register(type);// 注册类型信息 value = Instantiate(type); // 创建实例对象,并注入依赖 - _dependencies.TryAdd(type.FullName, value); // 登记到IOC容器中 + CustomRegisterInstance(type.FullName, value);// 登记到IOC容器中 } } return value; @@ -151,33 +151,26 @@ namespace Serein.Library.Utils #region 通过名称记录或获取一个实例 - - public void CustomRegisterInstance(string name, object instance, bool needInjectProperty = true) + /// + /// 指定key值注册一个已经实例化的实例对象 + /// + /// + /// + /// + public void CustomRegisterInstance(string key, object instance, bool needInjectProperty = true) { // 不存在时才允许创建 - if (!_dependencies.ContainsKey(name)) + if (!_dependencies.ContainsKey(key)) { - _dependencies.TryAdd(name, instance); + _dependencies.TryAdd(key, instance); } if (needInjectProperty) { InjectDependencies(instance); // 注入实例需要的依赖项 } - - // 检查是否存在其它实例 - if (_unfinishedDependencies.TryGetValue(name, out var unfinishedPropertyList)) - { - foreach ((object obj, PropertyInfo property) in unfinishedPropertyList) - { - property.SetValue(obj, instance); //注入依赖项 - } - - if (_unfinishedDependencies.TryRemove(name, out unfinishedPropertyList)) - { - unfinishedPropertyList.Clear(); - } - } + InjectUnfinishedDependencies(key, instance); // 检查是否存在其它实例需要该类型 + OnIOCMembersChanged?.Invoke(new IOCMembersChangedEventArgs(key, instance)); } public object Get(Type type) { @@ -237,30 +230,20 @@ namespace Serein.Library.Utils // 遍历已注册类型 foreach (var type in _typeMappings.Values.ToArray()) { - - if (_dependencies.ContainsKey(type.FullName)) + if (!_dependencies.ContainsKey(type.FullName)) { - // 已经存在实例,不用管 + var value = CreateInstance(type); // 绑定时注册的类型如果没有创建实例,则创建对应的实例 + CustomRegisterInstance(type.FullName, value);// 登记到IOC容器中 } - else - { - // 如果没有创建实例,则创建对应的实例 - _dependencies[type.FullName] = CreateInstance(type); - } - // 移除类型的注册记录 - _typeMappings.TryRemove(type.FullName, out _); + _typeMappings.TryRemove(type.FullName, out _); // 移除类型的注册记录 } - // 注入实例的依赖项 + foreach (var instance in _dependencies.Values) { - InjectDependencies(instance); + InjectDependencies(instance); // 绑定时注入实例的依赖项 } - //var instance = Instantiate(item.Value); - - // TryInstantiateWaitingDependencies(); - return true; } #endregion @@ -292,19 +275,24 @@ namespace Serein.Library.Utils private object CreateInstance(Type type, params object[] parameters) { var instance = Activator.CreateInstance(type); - if (_unfinishedDependencies.TryGetValue(type.FullName, out var unfinishedPropertyList)) + InjectUnfinishedDependencies(type.FullName, instance); + return instance; + } + + private void InjectUnfinishedDependencies(string key,object instance) + { + if (_unfinishedDependencies.TryGetValue(key, out var unfinishedPropertyList)) { foreach ((object obj, PropertyInfo property) in unfinishedPropertyList) { property.SetValue(obj, instance); //注入依赖项 } - if (_unfinishedDependencies.TryRemove(type.FullName, out unfinishedPropertyList)) + if (_unfinishedDependencies.TryRemove(key, out unfinishedPropertyList)) { unfinishedPropertyList.Clear(); } } - return instance; } diff --git a/NodeFlow/Base/NodeModelBaseData.cs b/NodeFlow/Base/NodeModelBaseData.cs index 6a98e7a..115b2d5 100644 --- a/NodeFlow/Base/NodeModelBaseData.cs +++ b/NodeFlow/Base/NodeModelBaseData.cs @@ -7,17 +7,14 @@ namespace Serein.NodeFlow.Base /// /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域 /// - public abstract partial class NodeModelBase :IDynamicFlowNode + public abstract partial class NodeModelBase : IDynamicFlowNode { - private static readonly ConnectionType[] ct = [ConnectionType.IsSucceed, - ConnectionType.IsFail, - ConnectionType.IsError, - ConnectionType.Upstream]; + public NodeModelBase() { PreviousNodes = []; SuccessorNodes = []; - foreach (ConnectionType ctType in ct) + foreach (ConnectionType ctType in NodeStaticConfig.ConnectionTypes) { PreviousNodes[ctType] = []; SuccessorNodes[ctType] = []; @@ -71,6 +68,9 @@ namespace Serein.NodeFlow.Base /// public Dictionary> SuccessorNodes { get; } + /// + /// 当前节点执行完毕后需要执行的下一个分支的类别 + /// public ConnectionType NextOrientation { get; set; } = ConnectionType.None; /// diff --git a/NodeFlow/Base/NodeModelBaseFunc.cs b/NodeFlow/Base/NodeModelBaseFunc.cs index 441c00c..a3cdb30 100644 --- a/NodeFlow/Base/NodeModelBaseFunc.cs +++ b/NodeFlow/Base/NodeModelBaseFunc.cs @@ -426,10 +426,10 @@ namespace Serein.NodeFlow.Base private static async Task MonitorObjExpInterrupt(IDynamicContext context, NodeModelBase nodeModel, object data, int type) { MonitorObjectEventArgs.ObjSourceType sourceType; - object key; + string key; if(type == 0) { - key = data; + key = data.GetType().FullName; sourceType = MonitorObjectEventArgs.ObjSourceType.IOCObj; } else @@ -450,6 +450,7 @@ namespace Serein.NodeFlow.Base for (int i = 0; i < exps.Count && !isExpInterrupt; i++) { exp = exps[i]; + if (string.IsNullOrEmpty(exp)) continue; isExpInterrupt = SereinConditionParser.To(data, exp); } @@ -458,7 +459,7 @@ namespace Serein.NodeFlow.Base InterruptClass interruptClass = InterruptClass.Branch; // 分支中断 if (context.Env.SetNodeInterrupt(nodeModel.Guid, interruptClass)) { - context.Env.TriggerInterrupt(nodeModel.Guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Obj); + context.Env.TriggerInterrupt(nodeModel.Guid, exp, InterruptTriggerEventArgs.InterruptTriggerType.Exp); var cancelType = await nodeModel.DebugSetting.GetInterruptTask(); await Console.Out.WriteLineAsync($"[{data}]中断已{cancelType},开始执行后继分支"); } diff --git a/NodeFlow/FlowEnvironment.cs b/NodeFlow/FlowEnvironment.cs index cd09371..2851f82 100644 --- a/NodeFlow/FlowEnvironment.cs +++ b/NodeFlow/FlowEnvironment.cs @@ -55,8 +55,12 @@ namespace Serein.NodeFlow FlipflopNodes = new List(); IsGlobalInterrupt = false; flowStarter = null; + + sereinIOC.OnIOCMembersChanged += e => this?.OnIOCMembersChanged?.Invoke(e) ; // 监听IOC容器的注册 } + + /// /// 节点的命名空间 /// @@ -113,6 +117,11 @@ namespace Serein.NodeFlow /// public event ExpInterruptTriggerHandler OnInterruptTrigger; + /// + /// 容器改变 + /// + public event IOCMembersChangedHandler OnIOCMembersChanged; + #endregion #region 属性 @@ -133,13 +142,14 @@ namespace Serein.NodeFlow public ChannelFlowInterrupt ChannelFlowInterrupt { get; set; } public ISereinIOC IOC { get => this; } + #endregion #region 私有变量 /// /// 容器管理 /// - private SereinIOC sereinIOC; + private readonly SereinIOC sereinIOC; /// /// 存储加载的程序集路径 @@ -666,9 +676,9 @@ namespace Serein.NodeFlow /// /// /// - public bool AddInterruptExpression(object obj, string expression) + public bool AddInterruptExpression(string key, string expression) { - if (dictMonitorObjExpInterrupt.TryGetValue(obj, out var condition)) + if (dictMonitorObjExpInterrupt.TryGetValue(key, out var condition)) { condition.Clear(); // 暂时 condition.Add(expression);// 暂时 @@ -678,7 +688,7 @@ namespace Serein.NodeFlow { var exps = new List(); exps.Add(expression); - dictMonitorObjExpInterrupt.TryAdd(obj, exps); + dictMonitorObjExpInterrupt.TryAdd(key, exps); return true; } } @@ -735,7 +745,7 @@ namespace Serein.NodeFlow /// /// 要监视的对象,以及与其关联的表达式 /// - private ConcurrentDictionary> dictMonitorObjExpInterrupt = []; + private ConcurrentDictionary> dictMonitorObjExpInterrupt = []; /// /// 设置对象的监视状态 @@ -743,22 +753,22 @@ namespace Serein.NodeFlow /// /// /// - public void SetMonitorObjState(object obj, bool isMonitor) + public void SetMonitorObjState(string key, bool isMonitor) { - if (obj is null) { return; } - var isExist = dictMonitorObjExpInterrupt.ContainsKey(obj); + if (string.IsNullOrEmpty(key)) { return; } + var isExist = dictMonitorObjExpInterrupt.ContainsKey(key); if (isExist) { if (!isMonitor) // 对象存在且需要不监视 { - dictMonitorObjExpInterrupt.Remove(obj, out _); + dictMonitorObjExpInterrupt.Remove(key, out _); } } else { if (isMonitor) // 对象不存在且需要监视,添加在集合中。 { - dictMonitorObjExpInterrupt.TryAdd(obj, new List()); ; + dictMonitorObjExpInterrupt.TryAdd(key, new List()); ; } } } @@ -769,10 +779,10 @@ namespace Serein.NodeFlow /// /// /// - public bool CheckObjMonitorState(object obj, out List? exps) + public bool CheckObjMonitorState(string key, out List? exps) { - if (obj is null) { exps = null; return false; } - return dictMonitorObjExpInterrupt.TryGetValue(obj, out exps); + if (string.IsNullOrEmpty(key)) { exps = null; return false; } + return dictMonitorObjExpInterrupt.TryGetValue(key, out exps); } /// diff --git a/NodeFlow/NodeStaticConfig.cs b/NodeFlow/NodeStaticConfig.cs index 0ae2063..1b56a0a 100644 --- a/NodeFlow/NodeStaticConfig.cs +++ b/NodeFlow/NodeStaticConfig.cs @@ -1,4 +1,5 @@ using Serein.Library.Api; +using Serein.Library.Enums; using System; using System.Collections.Generic; using System.Linq; @@ -14,6 +15,10 @@ namespace Serein.NodeFlow /// public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}"; - + public static readonly ConnectionType[] ConnectionTypes = [ + ConnectionType.Upstream, + ConnectionType.IsSucceed, + ConnectionType.IsFail, + ConnectionType.IsError]; } } diff --git a/WorkBench/MainWindow.xaml b/WorkBench/MainWindow.xaml index 5b09700..81588a9 100644 --- a/WorkBench/MainWindow.xaml +++ b/WorkBench/MainWindow.xaml @@ -78,6 +78,7 @@ + @@ -190,12 +191,13 @@ Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Con - + + diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index 7142878..ac1fac0 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -75,6 +75,11 @@ namespace Serein.WorkBench /// private List Connections { get; } = []; + /// + /// 起始节点 + /// + //private NodeControlBase StartNodeControl{ get; set; } + #region 与画布相关的字段 /// @@ -149,6 +154,7 @@ namespace Serein.WorkBench ViewModel = new MainWindowViewModel(this); FlowEnvironment = ViewModel.FlowEnvironment; ViewObjectViewer.FlowEnvironment = FlowEnvironment; + IOCObjectViewer.FlowEnvironment = FlowEnvironment; InitFlowEnvironmentEvent(); // 配置环境事件 logWindow = InitConsoleOut(); // 重定向 Console 输出 @@ -160,6 +166,9 @@ namespace Serein.WorkBench { FlowEnvironment.LoadProject(App.FlowProjectData, App.FileDataPath); // 加载项目 } + + + IOCObjectViewer.SelectObj += ViewObjectViewer.LoadObjectInformation; } private void InitFlowEnvironmentEvent() @@ -178,6 +187,8 @@ namespace Serein.WorkBench FlowEnvironment.OnNodeInterruptStateChange += FlowEnvironment_OnNodeInterruptStateChange; FlowEnvironment.OnInterruptTrigger += FlowEnvironment_OnInterruptTrigger; + FlowEnvironment.OnIOCMembersChanged += FlowEnvironment_OnIOCMembersChanged; + } private void InitCanvasUI() { @@ -242,8 +253,6 @@ namespace Serein.WorkBench } #endregion - - #region 运行环境事件 /// /// 加载完成 @@ -265,7 +274,8 @@ namespace Serein.WorkBench /// private void FlowEnvironment_OnFlowRunComplete(FlowEventArgs eventArgs) { - Console.WriteLine("-------运行完成---------\r\n"); + Console.WriteLine("-------运行完成---------\r\n"); + IOCObjectViewer.ClearObjItem(); } /// @@ -307,48 +317,69 @@ namespace Serein.WorkBench /// private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs) { - this.Dispatcher.Invoke(() => + string fromNodeGuid = eventArgs.FromNodeGuid; + string toNodeGuid = eventArgs.ToNodeGuid; + NodeControlBase fromNode = GuidToControl(fromNodeGuid); + NodeControlBase toNode = GuidToControl(toNodeGuid); + + ConnectionType connectionType = eventArgs.ConnectionType; + Action? action = null; + if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接 { - string fromNodeGuid = eventArgs.FromNodeGuid; - string toNodeGuid = eventArgs.ToNodeGuid; - NodeControlBase fromNode = GuidToControl(fromNodeGuid); - NodeControlBase toNode = GuidToControl(toNodeGuid); - ConnectionType connectionType = eventArgs.ConnectionType; - if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) + // 添加连接 + var connection = new Connection { - lock (Connections) - { - // 添加连接 - var connection = new Connection - { - Start = fromNode, - End = toNode, - Type = connectionType - }; - - BsControl.Draw(FlowChartCanvas, connection); // 添加贝塞尔曲线显示 - ConfigureLineContextMenu(connection); // 设置连接右键事件 - Connections.Add(connection); - EndConnection(); - - } - + Start = fromNode, + End = toNode, + Type = connectionType + }; + if (toNode is FlipflopNodeControl flipflopControl) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器 + { + var nodeModel = flipflopControl?.ViewModel?.Node; + NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除 } - else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remote) + + action = () => { + BsControl.Draw(FlowChartCanvas, connection); // 添加贝塞尔曲线显示 + ConfigureLineContextMenu(connection); // 设置连接右键事件 + Connections.Add(connection); + EndConnection(); + }; + + + + } + else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remote) // 移除连接 + { + // 需要移除连接 + var removeConnections = Connections.Where(c => c.Start.ViewModel.Node.Guid.Equals(fromNodeGuid) + && c.End.ViewModel.Node.Guid.Equals(toNodeGuid)) + .ToList(); + + + action = () => { - // 需要移除连接 - var removeConnections = Connections.Where(c => c.Start.ViewModel.Node.Guid.Equals(fromNodeGuid) - && c.End.ViewModel.Node.Guid.Equals(toNodeGuid)) - .ToList(); foreach (var connection in removeConnections) { connection.RemoveFromCanvas(); Connections.Remove(connection); + JudgmentFlipFlopNode(connection.End); // 连接关系变更时判断 } - } + }; + } + + + this.Dispatcher.Invoke(() => + { + action?.Invoke(); }); + + + } + + /// /// 节点移除事件 /// @@ -364,9 +395,19 @@ namespace Serein.WorkBench selectNodeControls.Remove(nodeControl); } } + #region 节点树视图 + if (nodeControl is FlipflopNodeControl flipflopControl) // 判断是否为触发器 + { + var node = flipflopControl?.ViewModel?.Node; + if (node is not null) + { + NodeTreeViewer.RemoteGlobalFlipFlop(node); // 从全局触发器树树视图中移除 + } + } + #endregion + this.Dispatcher.Invoke(() => { - FlowChartCanvas.Children.Remove(nodeControl); NodeControls.Remove(nodeControl.ViewModel.Node.Guid); }); @@ -404,7 +445,6 @@ namespace Serein.WorkBench return; } NodeControls.TryAdd(nodeModelBase.Guid, nodeControl); - if (eventArgs.IsAddInRegion && NodeControls.TryGetValue(eventArgs.RegeionGuid, out NodeControlBase? regionControl)) { if (regionControl is not null) @@ -415,13 +455,23 @@ namespace Serein.WorkBench } else { - if (!TryPlaceNodeInRegion(nodeControl, position)) + if (!TryPlaceNodeInRegion(nodeControl, position)) // 将节点放置在区域中 { - PlaceNodeOnCanvas(nodeControl, position.X, position.Y); + PlaceNodeOnCanvas(nodeControl, position.X, position.Y); // 将节点放置在画布上 } } + #region 节点树视图 + if (nodeModelBase.ControlType == NodeControlType.Flipflop) + { + var node = nodeControl?.ViewModel?.Node; + if(node is not null) + { + NodeTreeViewer.AddGlobalFlipFlop(FlowEnvironment, node); // 新增的触发器节点添加到全局触发器 + } + } + #endregion }); @@ -450,6 +500,11 @@ namespace Serein.WorkBench newStartNodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")); newStartNodeControl.BorderThickness = new Thickness(2); + var node = newStartNodeControl?.ViewModel?.Node; + if (node is not null) + { + NodeTreeViewer.LoadNodeTreeOfStartNode(FlowEnvironment, node); + } }); } @@ -463,10 +518,10 @@ namespace Serein.WorkBench { string nodeGuid = eventArgs.NodeGuid; - object monitorKey = MonitorObjectEventArgs.ObjSourceType.NodeFlowData switch + string monitorKey = MonitorObjectEventArgs.ObjSourceType.NodeFlowData switch { MonitorObjectEventArgs.ObjSourceType.NodeFlowData => nodeGuid, - _ => eventArgs.NewData, + _ => eventArgs.NewData.GetType().FullName, }; //NodeControlBase nodeControl = GuidToControl(nodeGuid); @@ -548,6 +603,15 @@ namespace Serein.WorkBench } } + /// + /// IOC变更 + /// + /// + /// + private void FlowEnvironment_OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs) + { + IOCObjectViewer.AddDependenciesInstance(eventArgs.Key, eventArgs.Instance); + } /// /// Guid 转 NodeControl @@ -569,7 +633,6 @@ namespace Serein.WorkBench } #endregion - #region 加载项目文件后触发事件相关方法 /// @@ -1081,46 +1144,9 @@ namespace Serein.WorkBench ((UIElement)sender).CaptureMouse(); // 捕获鼠标 e.Handled = true; // 防止事件传播影响其他控件 - } } - - private void ChangeViewerObjOfNode(NodeControlBase nodeControl) - { - // int i = false; - var node = nodeControl?.ViewModel?.Node; - if (node is not null && node.MethodDetails.ReturnType != typeof(void)) - { - if (ViewObjectViewer.MonitorObj is null) - { - FlowEnvironment.SetMonitorObjState(node.Guid, true); // 通知环境,该节点的数据更新后需要传到UI - // FlowEnvironment.SetMonitorObjState(nodeObj, true); // 通知环境,该节点的数据更新后需要传到UI - return; - } - var nodeObj = node.GetFlowData(); - if (nodeObj is null) - { - return; - } - //if (nodeObj.Equals(ViewObjectViewer.MonitorObj) == true) - //{ - // // 选择同一个控件,不再监视 - // ViewObjectViewer.RefreshObjectTree(nodeObj); - // return; - //} - if (node.Guid.Equals(ViewObjectViewer.MonitorKey) == true) - { - ViewObjectViewer.RefreshObjectTree(nodeObj); - return; - } - else - { - FlowEnvironment.SetMonitorObjState(ViewObjectViewer.MonitorKey, false); // 取消对旧节点的监视 - FlowEnvironment.SetMonitorObjState(node.Guid, true); // 通知环境,该节点的数据更新后需要传到UI - } - } - } /// /// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。 /// @@ -1217,6 +1243,41 @@ namespace Serein.WorkBench startControlDragPoint = currentPosition; // 更新起始点位置 } } + private void ChangeViewerObjOfNode(NodeControlBase nodeControl) + { + + var node = nodeControl?.ViewModel?.Node; + if (node is not null && node.MethodDetails.ReturnType != typeof(void)) + { + var key = node.Guid; + var instance = node.GetFlowData(); + ViewObjectViewer.LoadObjectInformation(key, instance); + ChangeViewerObj(key, instance); + } + } + public void ChangeViewerObj(string key, object instance) + { + if (ViewObjectViewer.MonitorObj is null) + { + FlowEnvironment.SetMonitorObjState(key, true); // 通知环境,该节点的数据更新后需要传到UI + return; + } + if (instance is null) + { + return; + } + if (key.Equals(ViewObjectViewer.MonitorKey) == true) + { + ViewObjectViewer.RefreshObjectTree(instance); + return; + } + else + { + FlowEnvironment.SetMonitorObjState(ViewObjectViewer.MonitorKey,false); // 取消对旧节点的监视 + FlowEnvironment.SetMonitorObjState(key, true); // 通知环境,该节点的数据更新后需要传到UI + } + } + #region UI连接控件操作 @@ -1229,6 +1290,7 @@ namespace Serein.WorkBench { IsControlDragging = false; ((UIElement)sender).ReleaseMouseCapture(); // 释放鼠标捕获 + } if (IsConnecting) @@ -1240,6 +1302,7 @@ namespace Serein.WorkBench return; } FlowEnvironment.ConnectNode(formNodeGuid, toNodeGuid, currentConnectionType); + } /*else if (IsConnecting) { @@ -2156,7 +2219,28 @@ namespace Serein.WorkBench #endregion - #region IOC视图管理 + #region 节点数、IOC视图管理 + + private void JudgmentFlipFlopNode(NodeControlBase nodeControl) + { + if (nodeControl is FlipflopNodeControl flipflopControl) // 判断是否为触发器 + { + var nodeModel = flipflopControl?.ViewModel?.Node; + int count = 0; + foreach (var ct in NodeStaticConfig.ConnectionTypes) + { + count += nodeModel.PreviousNodes[ct].Count; + } + if (count == 0) + { + NodeTreeViewer.AddGlobalFlipFlop(FlowEnvironment, nodeModel); // 添加到全局触发器树树视图 + } + else + { + NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除 + } + } + } void LoadIOCObjectViewer() { @@ -2366,7 +2450,6 @@ namespace Serein.WorkBench } } - } #region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线 diff --git a/WorkBench/Node/NodeControlViewModelBase.cs b/WorkBench/Node/NodeControlViewModelBase.cs index dad81e6..5abe74b 100644 --- a/WorkBench/Node/NodeControlViewModelBase.cs +++ b/WorkBench/Node/NodeControlViewModelBase.cs @@ -24,8 +24,6 @@ namespace Serein.WorkBench.Node.ViewModel internal NodeModelBase Node { get; } - - private bool isSelect; /// /// 表示节点控件是否被选中 diff --git a/WorkBench/Serein.WorkBench.csproj b/WorkBench/Serein.WorkBench.csproj index 96ae77d..2e097eb 100644 --- a/WorkBench/Serein.WorkBench.csproj +++ b/WorkBench/Serein.WorkBench.csproj @@ -33,6 +33,7 @@ + diff --git a/WorkBench/Themes/IOCObjectViewControl.xaml b/WorkBench/Themes/IOCObjectViewControl.xaml index 4913c16..bc70033 100644 --- a/WorkBench/Themes/IOCObjectViewControl.xaml +++ b/WorkBench/Themes/IOCObjectViewControl.xaml @@ -9,15 +9,18 @@ - - + - - - - + + + diff --git a/WorkBench/Themes/IOCObjectViewControl.xaml.cs b/WorkBench/Themes/IOCObjectViewControl.xaml.cs index 6432b42..cce3074 100644 --- a/WorkBench/Themes/IOCObjectViewControl.xaml.cs +++ b/WorkBench/Themes/IOCObjectViewControl.xaml.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; +using System.Windows.Controls.Primitives; using System.Windows.Data; using System.Windows.Documents; using System.Windows.Input; @@ -14,6 +15,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Navigation; using System.Windows.Shapes; +using System.Xml.Linq; namespace Serein.WorkBench.Themes { @@ -22,18 +24,96 @@ namespace Serein.WorkBench.Themes /// public partial class IOCObjectViewControl : UserControl { - private IOCObjectViewMoel IOCObjectViewMoel; - private SereinIOC sereinIOC; - public void SetIOC(SereinIOC sereinIOC) - { - this.sereinIOC = sereinIOC; - } + public Action SelectObj { get; set; } public IOCObjectViewControl() { InitializeComponent(); - IOCObjectViewMoel = new IOCObjectViewMoel(); - DataContext = IOCObjectViewMoel; + } + + private class IOCObj + { + public string Key { get; set; } + public object Instance { get; set; } + } + + /// + /// 运行环境 + /// + public IFlowEnvironment FlowEnvironment { get; set; } + + /// + /// 添加一个实例 + /// + /// + /// + public void AddDependenciesInstance(string key,object instance) + { + IOCObj iOCObj = new IOCObj + { + Key = key, + Instance = instance, + }; + TextBlock textBlock = new TextBlock(); + textBlock.Text = key; + textBlock.Tag = iOCObj; + textBlock.MouseDown += (s, e) => + { + if(s is TextBlock block && block.Tag is IOCObj iocObj) + { + SelectObj?.Invoke(iocObj.Key, iocObj.Instance); + //FlowEnvironment.SetMonitorObjState(iocObj.Instance, true); // 通知环境,该节点的数据更新后需要传到UI + } + }; + DependenciesListBox.Items.Add(textBlock); + SortLisbox(DependenciesListBox); + } + + /// + /// 刷新一个实例 + /// + /// + /// + public void RefreshDependenciesInstance(string key, object instance) + { + foreach (var item in DependenciesListBox.Items) + { + if (item is TextBlock block && block.Tag is IOCObj iocObj && iocObj.Key.Equals(key)) + { + iocObj.Instance = instance; + } + } + } + + public void ClearObjItem() + { + DependenciesListBox.Items.Clear(); + } + + private static void SortLisbox(ListBox listBox) + { + var sortedItems = listBox.Items.Cast().OrderBy(x => x.Text).ToList(); + listBox.Items.Clear(); + foreach (var item in sortedItems) + { + listBox.Items.Add(item); + } + } + + public void RemoveDependenciesInstance(string key) + { + object? itemControl = null; + foreach (var item in DependenciesListBox.Items) + { + if (item is TextBlock block && block.Tag is IOCObj iocObj && iocObj.Key.Equals(key)) + { + itemControl = item; + } + } + if (itemControl is not null) + { + DependenciesListBox.Items.Remove(itemControl); + } } } diff --git a/WorkBench/Themes/IOCObjectViewMoel.cs b/WorkBench/Themes/IOCObjectViewMoel.cs deleted file mode 100644 index 366a8a2..0000000 --- a/WorkBench/Themes/IOCObjectViewMoel.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.WorkBench.Themes -{ - internal class IOCObjectViewMoel - { - } -} diff --git a/WorkBench/Themes/LazyTreeView.xaml b/WorkBench/Themes/LazyTreeView.xaml deleted file mode 100644 index cbf7d7f..0000000 --- a/WorkBench/Themes/LazyTreeView.xaml +++ /dev/null @@ -1,26 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/WorkBench/Themes/LazyTreeView.xaml.cs b/WorkBench/Themes/LazyTreeView.xaml.cs deleted file mode 100644 index 2c68696..0000000 --- a/WorkBench/Themes/LazyTreeView.xaml.cs +++ /dev/null @@ -1,58 +0,0 @@ -using Serein.Library.Enums; -using Serein.NodeFlow.Base; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows; -using System.Windows.Controls; -using System.Windows.Controls.Primitives; -using System.Windows.Data; -using System.Windows.Documents; -using System.Windows.Input; -using System.Windows.Media; -using System.Windows.Media.Imaging; -using System.Windows.Navigation; -using System.Windows.Shapes; - -namespace Serein.WorkBench.Themes -{ - /// - /// LazyTreeView.xaml 的交互逻辑 - /// - public partial class LazyTreeView : UserControl - { - public ObservableCollection RootNodes { get; set; } - - public LazyTreeView() - { - InitializeComponent(); - RootNodes = new ObservableCollection(); - treeView.DataContext = this; - } - - private void treeView_SelectedItemChanged(object sender, RoutedPropertyChangedEventArgs e) - { - if (e.NewValue is NodeModelBase node) - { - // 在这里设置 Expanded 事件 - var treeViewItem = (TreeViewItem)treeView.ItemContainerGenerator.ContainerFromItem(node); - treeViewItem.Expanded += (s, args) => LoadChildren(node, treeViewItem); - } - } - private void LoadChildren(NodeModelBase node, TreeViewItem treeViewItem) - { - // 懒加载逻辑 - if (node.SuccessorNodes.Count > 0) - { - treeViewItem.Items.Clear(); - foreach (var child in node.SuccessorNodes[ConnectionType.IsSucceed]) // 根据类型加载子项 - { - treeViewItem.Items.Add(child); - } - } - } - } -} diff --git a/WorkBench/Themes/NodeTreeItemViewControl.xaml b/WorkBench/Themes/NodeTreeItemViewControl.xaml new file mode 100644 index 0000000..47be97c --- /dev/null +++ b/WorkBench/Themes/NodeTreeItemViewControl.xaml @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WorkBench/Themes/NodeTreeItemViewControl.xaml.cs b/WorkBench/Themes/NodeTreeItemViewControl.xaml.cs new file mode 100644 index 0000000..20be0b1 --- /dev/null +++ b/WorkBench/Themes/NodeTreeItemViewControl.xaml.cs @@ -0,0 +1,270 @@ +using Serein.Library.Api; +using Serein.Library.Enums; +using Serein.NodeFlow; +using Serein.NodeFlow.Base; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; +using System.Xml.Linq; +using static Serein.WorkBench.Themes.TypeViewerWindow; + +namespace Serein.WorkBench.Themes +{ + /// + /// NodeTreeVIewControl.xaml 的交互逻辑 + /// + public partial class NodeTreeItemViewControl : UserControl + { + public NodeTreeItemViewControl() + { + InitializeComponent(); + foreach (var ct in NodeStaticConfig.ConnectionTypes) + { + var guid = ToGridView(this, ct); + guid.Visibility = Visibility.Collapsed; + } + } + + + /// + /// 保存的节点数据 + /// + private NodeModelBase nodeModel; + private IFlowEnvironment flowEnvironment { get; set; } + + + private class NodeTreeModel + { + public NodeModelBase RootNode { get; set; } + public Dictionary> ChildNodes { get; set; } + } + + + public void InitAndLoadTree(IFlowEnvironment flowEnvironment, NodeModelBase nodeModel) + { + this.flowEnvironment = flowEnvironment; + this.nodeModel = nodeModel; + RefreshTree(); + } + + public TreeViewItem RefreshTree() + { + NodeModelBase rootNodeModel = this.nodeModel; + NodeTreeModel nodeTreeModel = new NodeTreeModel + { + RootNode = rootNodeModel, + ChildNodes = new Dictionary>() + { + {ConnectionType.Upstream, []}, + {ConnectionType.IsSucceed, [rootNodeModel]}, + {ConnectionType.IsFail, []}, + {ConnectionType.IsError, []}, + } + }; + var rootNode = new TreeViewItem + { + Header = rootNodeModel.MethodDetails.MethodTips, + Tag = nodeTreeModel, + }; + LoadNodeItem(this, nodeTreeModel); + rootNode.Expanded += TreeViewItem_Expanded; // 监听展开事件 + rootNode.IsExpanded = true; + return rootNode; + } + + + + + /// + /// 展开子项事件 + /// + /// + /// + private void TreeViewItem_Expanded(object sender, RoutedEventArgs e) + { + if (sender is TreeViewItem item && item.Tag is NodeTreeModel nodeTreeModel) + { + item.Items.Clear(); + NodeTreeItemViewControl? nodeTreeItemViewControl = LoadTNoderee(nodeTreeModel); + + if (nodeTreeItemViewControl is not null) + { + LoadNodeItem(nodeTreeItemViewControl, nodeTreeModel); + item.Items.Add(nodeTreeItemViewControl); + + } + item.IsSelected = false; + } + + e.Handled = true; + } + + /// + /// 加载面板 + /// + /// + /// + private void LoadNodeItem(NodeTreeItemViewControl nodeTreeItemViewControl, NodeTreeModel nodeTreeModel) + { + + foreach (var ct in NodeStaticConfig.ConnectionTypes) + { + var treeViewer = ToTreeView(nodeTreeItemViewControl, ct); + var guid = ToGridView(nodeTreeItemViewControl, ct); + treeViewer.Items.Clear(); // 移除对象树的所有节点 + var list = nodeTreeModel.ChildNodes[ct]; + + if (list.Count > 0) + { + foreach (var child in list) + { + NodeTreeModel tmpNodeTreeModel = new NodeTreeModel + { + RootNode = child, + ChildNodes = child.SuccessorNodes, + }; + TreeViewItem treeViewItem = new TreeViewItem + { + Header = child.MethodDetails.MethodTips, + Tag = tmpNodeTreeModel + }; + treeViewItem.Expanded += TreeViewItem_Expanded; + ContextMenu contextMenu = new ContextMenu(); + contextMenu.Items.Add(MainWindow.CreateMenuItem("从此节点执行", (s, e) => + { + flowEnvironment.StartFlowInSelectNodeAsync(tmpNodeTreeModel.RootNode.Guid); + })); + treeViewItem.ContextMenu = contextMenu; + + treeViewer.Items.Add(treeViewItem); + } + guid.Visibility = Visibility.Visible; + } + else + { + guid.Visibility = Visibility.Collapsed; + } + } + + + } + + /// + /// 加载节点子项 + /// + /// + /// + private NodeTreeItemViewControl? LoadTNoderee(NodeTreeModel nodeTreeModel) + { + NodeTreeItemViewControl nodeTreeItemViewControl = null; + foreach (var connectionType in NodeStaticConfig.ConnectionTypes) + { + var childNodeModels = nodeTreeModel.ChildNodes[connectionType]; + if (childNodeModels.Count > 0) + { + nodeTreeItemViewControl ??= new NodeTreeItemViewControl(); + } + else + { + continue; + } + + TreeView treeView = ToTreeView(nodeTreeItemViewControl, connectionType); + foreach (var childNodeModel in childNodeModels) + { + NodeTreeModel tempNodeTreeModel = new NodeTreeModel + { + RootNode = childNodeModel, + ChildNodes = childNodeModel.SuccessorNodes, + }; + TreeViewItem treeViewItem = new TreeViewItem + { + Header = childNodeModel.MethodDetails.MethodTips, + Tag = tempNodeTreeModel + }; + treeViewItem.Margin = new Thickness(-15, 0, 0, 0); + treeViewItem.Visibility = Visibility.Visible; + treeView.Items.Add(treeViewItem); + } + } + if (nodeTreeItemViewControl is not null) + { + foreach (var connectionType in NodeStaticConfig.ConnectionTypes) + { + var childNodeModels = nodeTreeModel.ChildNodes[connectionType]; + if (childNodeModels.Count > 0) + { + nodeTreeItemViewControl ??= new NodeTreeItemViewControl(); + } + else + { + continue; + } + } + } + return nodeTreeItemViewControl; + } + + /// + /// 折叠事件 + /// + /// + /// + private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e) + { + if (sender is TreeViewItem item && item.Items.Count > 0) + { + item.Items.Clear(); + } + } + public static TreeView ToTreeView(NodeTreeItemViewControl item, ConnectionType connectionType) + { + return connectionType switch + { + ConnectionType.Upstream => item.UpstreamTreeNodes, + ConnectionType.IsError => item.IsErrorTreeNodes, + ConnectionType.IsFail => item.IsFailTreeNodes, + ConnectionType.IsSucceed => item.IsSucceedTreeNodes, + _ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType) + }; + } + public static Grid ToGridView(NodeTreeItemViewControl item, ConnectionType connectionType) + { + return connectionType switch + { + ConnectionType.Upstream => item.UpstreamTreeGuid, + ConnectionType.IsError => item.IsErrorTreeGuid, + ConnectionType.IsFail => item.IsFailTreeGuid, + ConnectionType.IsSucceed => item.IsSucceedTreeGuid, + _ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType) + }; + } + + //public static System.Windows.Shapes.Rectangle ToRectangle(NodeTreeItemViewControl item, ConnectionType connectionType) + //{ + // return connectionType switch + // { + // ConnectionType.Upstream => item.UpstreamTreeRectangle, + // ConnectionType.IsError => item.IsErrorRectangle, + // ConnectionType.IsFail => item.IsFailRectangle, + // ConnectionType.IsSucceed => item.IsSucceedRectangle, + // _ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType) + // }; + //} + + + } +} diff --git a/WorkBench/Themes/NodeTreeView.cs b/WorkBench/Themes/NodeTreeView.cs deleted file mode 100644 index 67b0b52..0000000 --- a/WorkBench/Themes/NodeTreeView.cs +++ /dev/null @@ -1,71 +0,0 @@ -using Serein.NodeFlow.Base; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.Windows.Controls.Primitives; -using System.Windows.Controls; -using System.Windows.Media; -using System.Windows; -using Serein.Library.Enums; - -namespace Serein.WorkBench.Themes -{ - public class NodeTreeView : TreeView - { - public NodeTreeView() - { - this.ItemContainerGenerator.StatusChanged += OnStatusChanged; - } - - private void OnStatusChanged(object sender, EventArgs e) - { - if (this.ItemContainerGenerator.Status == GeneratorStatus.ContainersGenerated) - { - foreach (var item in Items) - { - var treeViewItem = (TreeViewItem)this.ItemContainerGenerator.ContainerFromItem(item); - if (treeViewItem != null) - { - treeViewItem.Expanded += TreeViewItem_Expanded; - ApplyColor(treeViewItem, item as NodeModelBase); - } - } - } - } - - private void TreeViewItem_Expanded(object sender, RoutedEventArgs e) - { - if (sender is TreeViewItem item && item.DataContext is NodeModelBase node) - { - if (item.Items.Count == 0) // 懒加载 - { - foreach (var childNode in node.SuccessorNodes[ConnectionType.Upstream]) - { - item.Items.Add(childNode); - } - } - } - } - - private void ApplyColor(TreeViewItem item, NodeModelBase node) - { - // 根据 ControlType 设置颜色 - switch (node.ControlType) - { - case NodeControlType.Flipflop: - item.Background = Brushes.LightGreen; - break; - case NodeControlType.Action: - item.Background = Brushes.LightCoral; - break; - // 添加更多条件 - default: - item.Background = Brushes.Transparent; - break; - } - } - } - -} diff --git a/WorkBench/Themes/NodeTreeViewControl.xaml b/WorkBench/Themes/NodeTreeViewControl.xaml new file mode 100644 index 0000000..bee4a99 --- /dev/null +++ b/WorkBench/Themes/NodeTreeViewControl.xaml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/WorkBench/Themes/NodeTreeViewControl.xaml.cs b/WorkBench/Themes/NodeTreeViewControl.xaml.cs new file mode 100644 index 0000000..70f4a0e --- /dev/null +++ b/WorkBench/Themes/NodeTreeViewControl.xaml.cs @@ -0,0 +1,99 @@ +using Serein.Library.Api; +using Serein.NodeFlow.Base; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Serein.WorkBench.Themes +{ + /// + /// NodeTreeViewControl.xaml 的交互逻辑 + /// + public partial class NodeTreeViewControl : UserControl + { + private IFlowEnvironment FlowEnvironment { get; set; } + public NodeTreeViewControl() + { + InitializeComponent(); + } + + private string startNodeGuid = string.Empty; + private Dictionary globalFlipflopNodes = []; + private Dictionary unemployedNodes = []; + + public void LoadNodeTreeOfStartNode(IFlowEnvironment flowEnvironment, NodeModelBase nodeModel) + { + startNodeGuid = nodeModel.Guid; + StartNodeViewer.InitAndLoadTree(flowEnvironment, nodeModel); + } + + #region 触发器 + public void AddGlobalFlipFlop(IFlowEnvironment flowEnvironment, NodeModelBase nodeModel) + { + if (!globalFlipflopNodes.ContainsKey(nodeModel.Guid)) + { + NodeTreeItemViewControl flipflopTreeViewer = new NodeTreeItemViewControl(); + flipflopTreeViewer.InitAndLoadTree(flowEnvironment, nodeModel); + globalFlipflopNodes.Add(nodeModel.Guid, flipflopTreeViewer); + GlobalFlipflopNodeListbox.Items.Add(flipflopTreeViewer); + } + } + public void RefreshGlobalFlipFlop(NodeModelBase nodeModel) + { + if (globalFlipflopNodes.TryGetValue(nodeModel.Guid, out var viewer)) + { + viewer.RefreshTree(); + } + } + public void RemoteGlobalFlipFlop(NodeModelBase nodeModel) + { + if (globalFlipflopNodes.TryGetValue(nodeModel.Guid, out var viewer)) + { + globalFlipflopNodes.Remove(nodeModel.Guid); + GlobalFlipflopNodeListbox.Items.Remove(viewer); + } + } + #endregion + + + #region 无业游民(定义:不存在于起始节点与全局触发器的调用链上的节点,只能手动刷新?) + public void AddUnemployed(IFlowEnvironment flowEnvironment, NodeModelBase nodeModel) + { + if (!unemployedNodes.ContainsKey(nodeModel.Guid)) + { + NodeTreeItemViewControl flipflopTreeViewer = new NodeTreeItemViewControl(); + flipflopTreeViewer.InitAndLoadTree(flowEnvironment, nodeModel); + unemployedNodes.Add(nodeModel.Guid, flipflopTreeViewer); + GlobalFlipflopNodeListbox.Items.Add(flipflopTreeViewer); + } + } + public void RefreshUnemployed(NodeModelBase nodeModel) + { + if (unemployedNodes.TryGetValue(nodeModel.Guid, out var viewer)) + { + viewer.RefreshTree(); + } + } + public void RemoteUnemployed(NodeModelBase nodeModel) + { + if (unemployedNodes.TryGetValue(nodeModel.Guid, out var viewer)) + { + unemployedNodes.Remove(nodeModel.Guid); + GlobalFlipflopNodeListbox.Items.Remove(viewer); + } + } + #endregion + + } +} diff --git a/WorkBench/Themes/ObjectViewerControl.xaml.cs b/WorkBench/Themes/ObjectViewerControl.xaml.cs index 3bbeaa6..98399b1 100644 --- a/WorkBench/Themes/ObjectViewerControl.xaml.cs +++ b/WorkBench/Themes/ObjectViewerControl.xaml.cs @@ -85,7 +85,7 @@ namespace Serein.WorkBench.Themes /// /// 监视对象的键 /// - public object MonitorKey { get => monitorKey; } + public string MonitorKey { get => monitorKey; } /// /// 正在监视的对象 /// @@ -96,7 +96,7 @@ namespace Serein.WorkBench.Themes /// public string MonitorExpression { get => ExpressionTextBox.Text.ToString(); } - private object monitorKey; + private string monitorKey; private object monitorObj; // 用于存储当前展开的节点路径 @@ -107,7 +107,7 @@ namespace Serein.WorkBench.Themes /// 加载对象信息,展示其成员 /// /// 要展示的对象 - public void LoadObjectInformation(object key, object obj) + public void LoadObjectInformation(string key, object obj) { if (obj == null) return; monitorKey = key; @@ -171,6 +171,7 @@ namespace Serein.WorkBench.Themes // 这里创建了一个子项,并给这个子项创建了“正在加载”的子项 // 然后移除了原来对象树的所有项,再把这个新创建的子项添加上去 // 绑定了展开/折叠事件后,自动展开第一层,开始反射obj的成员,并判断obj的成员生成什么样的节点 + rootNode.IsExpanded = true; return rootNode; } @@ -454,6 +455,11 @@ namespace Serein.WorkBench.Themes { try { + if(obj is null) + { + value = null; + return "Error"; + } var properties = obj.GetType().GetProperties(); // 获取实例属性值