From cf7760ef842e6b5369bea221ee5751077f91847b Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Sat, 22 Mar 2025 18:14:48 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=90=E8=A1=8C=E7=8E=AF=E5=A2=83=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=E4=BA=86=E7=94=BB=E5=B8=83=E7=9B=B8=E5=85=B3=E7=9A=84?= =?UTF-8?q?=E5=B1=9E=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Api/IFlowEnvironment.cs | 222 +++++++--- ...odelExtension.cs => FlowModelExtension.cs} | 40 +- Library/FlowNode/ContainerFlowEnvironment.cs | 390 ----------------- Library/FlowNode/FlowCanvasModel.cs | 57 +++ Library/FlowNode/NodeModelBaseData.cs | 8 + Library/FlowNode/SereinProjectData.cs | 14 +- NodeFlow/Env/EnvMsgTheme.cs | 8 + NodeFlow/Env/FlowEnvironment.cs | 261 +++++++---- NodeFlow/Env/FlowEnvironmentDecorator.cs | 78 +++- NodeFlow/Env/MsgControllerOfClient.cs | 13 +- NodeFlow/Env/MsgControllerOfServer.cs | 61 ++- NodeFlow/Env/RemoteFlowEnvironment.cs | 144 +++++-- NodeFlow/Model/SingleGlobalDataNode.cs | 7 +- Workbench/App.xaml | 3 +- Workbench/App.xaml.cs | 18 +- Workbench/MainWindow.xaml.cs | 35 +- Workbench/Models/TabModel.cs | 50 +++ Workbench/Node/View/ConnectionControl.cs | 5 +- Workbench/Services/FlowEEForwardingService.cs | 33 ++ Workbench/Services/NodeControlService.cs | 63 +++ Workbench/Services/NodeOperationService.cs | 406 ------------------ .../Converters/BoolToVisibilityConverter.cs | 28 ++ Workbench/ViewModels/FlowCanvasViewModel.cs | 26 +- Workbench/ViewModels/FlowEditViewModel.cs | 81 ++++ Workbench/Views/FlowCanvasView.xaml | 13 +- Workbench/Views/FlowCanvasView.xaml.cs | 57 ++- Workbench/Views/FlowEditView.xaml | 56 ++- Workbench/Views/FlowEditView.xaml.cs | 104 ++++- Workbench/Views/FlowWorkbenchView.xaml.cs | 1 - 29 files changed, 1179 insertions(+), 1103 deletions(-) rename Library/Extension/{NodeModelExtension.cs => FlowModelExtension.cs} (94%) delete mode 100644 Library/FlowNode/ContainerFlowEnvironment.cs create mode 100644 Library/FlowNode/FlowCanvasModel.cs create mode 100644 Workbench/Models/TabModel.cs create mode 100644 Workbench/Services/NodeControlService.cs delete mode 100644 Workbench/Services/NodeOperationService.cs create mode 100644 Workbench/Tool/Converters/BoolToVisibilityConverter.cs diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index 172b81a..e6f34de 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -28,7 +28,6 @@ namespace Serein.Library.Api public delegate void ProjectSavingHandler(ProjectSavingEventArgs eventArgs); - /// /// 加载项目文件时成功加载了DLL文件 /// @@ -46,12 +45,34 @@ namespace Serein.Library.Api /// public delegate void NodeConnectChangeHandler(NodeConnectChangeEventArgs eventArgs); + + + + /// + /// 环境中新增了一个画布 + /// + /// + public delegate void CanvasCreateHandler(CanvasCreateEventArgs eventArgs); + + /// + /// 环境中移除了一个画布 + /// + /// + public delegate void CanvasRemoveHandler(CanvasRemoveEventArgs eventArgs); + /// /// 环境中加载了一个节点 /// /// public delegate void NodeCreateHandler(NodeCreateEventArgs eventArgs); + /// + /// 环境中移除了一个节点 + /// + /// + + public delegate void NodeRemoveHandler(NodeRemoveEventArgs eventArgs); + /// /// 节点放置事件 /// @@ -132,23 +153,9 @@ namespace Serein.Library.Api public string ErrorTips { get; protected set; } = string.Empty; } - //public class LoadNodeEventArgs : FlowEventArgs - //{ - // public LoadNodeEventArgs(NodeInfo NodeInfo, MethodDetails MethodDetailss) - // { - // this.NodeInfo = NodeInfo; - // this.MethodDetailss = MethodDetailss; - // } - // /// - // /// 项目文件节点信息参数 - // /// - // public NodeInfo NodeInfo { get; protected set; } - // /// - // /// 已加载在环境中的方法描述 - // /// - // public MethodDetails MethodDetailss { get; protected set; } - //} - + /// + /// 项目加载完成 + /// public class ProjectLoadedEventArgs : FlowEventArgs { public ProjectLoadedEventArgs() @@ -156,6 +163,9 @@ namespace Serein.Library.Api } } + /// + /// 项目保存 + /// public class ProjectSavingEventArgs : FlowEventArgs { public ProjectSavingEventArgs() @@ -163,6 +173,9 @@ namespace Serein.Library.Api } } + /// + /// 加载了DLL外部依赖 + /// public class LoadDllEventArgs : FlowEventArgs { public LoadDllEventArgs(NodeLibraryInfo nodeLibraryInfo, List MethodDetailss) @@ -180,6 +193,9 @@ namespace Serein.Library.Api public List MethodDetailss { get; protected set; } } + /// + /// 移除了DLL外部依赖 + /// public class RemoteDllEventArgs : FlowEventArgs { public RemoteDllEventArgs() @@ -187,6 +203,9 @@ namespace Serein.Library.Api } } + /// + /// 改变节点连接关系 + /// public class NodeConnectChangeEventArgs : FlowEventArgs { @@ -213,12 +232,14 @@ namespace Serein.Library.Api /// /// /// - public NodeConnectChangeEventArgs(string fromNodeGuid, + public NodeConnectChangeEventArgs(string canvasGuid, + string fromNodeGuid, string toNodeGuid, JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线 ConnectionInvokeType connectionInvokeType, // 节点调用的方法类型(true/false/error/cancel ) ConnectChangeType changeType) // 需要创建连接线还是删除连接线 { + this.CanvasGuid = canvasGuid; this.FromNodeGuid = fromNodeGuid; this.ToNodeGuid = toNodeGuid; this.ConnectionInvokeType = connectionInvokeType; @@ -235,13 +256,15 @@ namespace Serein.Library.Api /// /// /// - public NodeConnectChangeEventArgs(string fromNodeGuid, + public NodeConnectChangeEventArgs(string canvasGuid, + string fromNodeGuid, string toNodeGuid, JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线 int argIndex, ConnectionArgSourceType connectionArgSourceType, // 节点对应的方法入参所需参数来源 ConnectChangeType changeType) // 需要创建连接线还是删除连接线 { + CanvasGuid = canvasGuid; this.FromNodeGuid = fromNodeGuid; this.ToNodeGuid = toNodeGuid; this.ChangeType = changeType; @@ -250,6 +273,9 @@ namespace Serein.Library.Api this.JunctionOfConnectionType = junctionOfConnectionType; } + + public string CanvasGuid { get; } + /// /// 连接关系中始节点的Guid /// @@ -282,7 +308,36 @@ namespace Serein.Library.Api } + /// + /// 添加了一个画布 + /// + public class CanvasCreateEventArgs : FlowEventArgs + { + public CanvasCreateEventArgs( + FlowCanvasInfo info) + { + Info = info; + } + public FlowCanvasInfo Info { get; } + } + + /// + /// 移除了一个画布 + /// + public class CanvasRemoveEventArgs : FlowEventArgs + { + public CanvasRemoveEventArgs(string canvasGuid) + { + CanvasGuid = canvasGuid; + } + + public string CanvasGuid { get; } + } + + /// + /// 添加了节点 + /// public class NodeCreateEventArgs : FlowEventArgs { /// @@ -290,31 +345,56 @@ namespace Serein.Library.Api /// /// 节点对象 /// 位置 - public NodeCreateEventArgs(NodeModelBase nodeModel, PositionOfUI position) + public NodeCreateEventArgs(string canvasGuid, NodeModelBase nodeModel, PositionOfUI position) { + CanvasGuid = canvasGuid; this.NodeModel = nodeModel; this.Position = position; } + public string CanvasGuid { get; } + /// /// 节点Model对象 /// public NodeModelBase NodeModel { get; private set; } public PositionOfUI Position { get; private set; } - //public bool IsAddInRegion { get; private set; } public string RegeionGuid { get; private set; } } + /// + /// 移除了节点的事件 + /// + public class NodeRemoveEventArgs : FlowEventArgs + { + public NodeRemoveEventArgs(string canvasGuid, string nodeGuid) + { + CanvasGuid = canvasGuid; + this.NodeGuid = nodeGuid; + } + + public string CanvasGuid { get; } + + /// + /// 被移除节点的Guid + /// + public string NodeGuid { get; private set; } + } + /// /// 节点放置事件参数 /// public class NodePlaceEventArgs : FlowEventArgs { - public NodePlaceEventArgs(string nodeGuid, string containerNodeGuid) + public NodePlaceEventArgs(string canvasGuid, string nodeGuid, string containerNodeGuid) { + CanvasGuid = canvasGuid; NodeGuid = nodeGuid; ContainerNodeGuid = containerNodeGuid; } + + public string CanvasGuid { get; } + /// /// 子节点,该数据为此次时间的主节点 /// @@ -330,10 +410,14 @@ namespace Serein.Library.Api /// public class NodeTakeOutEventArgs : FlowEventArgs { - public NodeTakeOutEventArgs(string nodeGuid) + public NodeTakeOutEventArgs(string canvasGuid, string nodeGuid) { + CanvasGuid = canvasGuid; NodeGuid = nodeGuid; } + + public string CanvasGuid { get; } + /// /// 需要取出的节点Guid /// @@ -341,34 +425,21 @@ namespace Serein.Library.Api } - /// - /// 环境中移除了一个节点 - /// - /// - - public delegate void NodeRemoveHandler(NodeRemoveEventArgs eventArgs); - public class NodeRemoveEventArgs : FlowEventArgs - { - public NodeRemoveEventArgs(string nodeGuid) - { - this.NodeGuid = nodeGuid; - } - /// - /// 被移除节点的Guid - /// - public string NodeGuid { get; private set; } - } public class StartNodeChangeEventArgs : FlowEventArgs { - public StartNodeChangeEventArgs(string oldNodeGuid, string newNodeGuid) + public StartNodeChangeEventArgs(string canvasGuid, string oldNodeGuid, string newNodeGuid) { + CanvasGuid = canvasGuid; this.OldNodeGuid = oldNodeGuid; this.NewNodeGuid = newNodeGuid; ; } + + public string CanvasGuid { get; } + /// /// 原来的起始节点Guid /// @@ -577,6 +648,16 @@ namespace Serein.Library.Api /// event NodeConnectChangeHandler OnNodeConnectChange; + /// + /// 增加画布事件 + /// + event CanvasCreateHandler OnCanvasCreate; + + /// + /// 删除画布事件 + /// + event CanvasRemoveHandler OnCanvasRemove; + /// /// 节点创建事件 /// @@ -793,20 +874,37 @@ namespace Serein.Library.Api #region 流程节点操作接口 + /// + /// 增加画布 + /// + /// 画布名称 + /// 宽度 + /// 高度 + /// + Task CreateCanvasAsync(string canvasName, int width , int height); + + /// + /// 删除画布 + /// + /// 画布Guid + /// + Task RemoteCanvasAsync(string canvasGuid); + + /// /// 移动了某个节点(远程插件使用) /// /// /// /// - void MoveNode(string nodeGuid,double x, double y); + void MoveNode(string canvasGuid, string nodeGuid, double x, double y); /// /// 设置流程起点节点 /// /// 尝试设置为起始节点的节点Guid /// 被设置为起始节点的Guid - Task SetStartNodeAsync(string nodeGuid); + Task SetStartNodeAsync(string canvasGuid, string nodeGuid); /// /// 在两个节点之间创建连接关系 @@ -816,11 +914,12 @@ namespace Serein.Library.Api /// 起始节点控制点 /// 目标节点控制点 /// 决定了方法执行后的后继行为 - Task ConnectInvokeNodeAsync(string fromNodeGuid, - string toNodeGuid, - JunctionType fromNodeJunctionType, - JunctionType toNodeJunctionType, - ConnectionInvokeType invokeType); + Task ConnectInvokeNodeAsync(string canvasGuid, + string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionInvokeType invokeType); /// /// 在两个节点之间创建连接关系 @@ -831,12 +930,13 @@ namespace Serein.Library.Api /// 目标节点控制点 /// 决定了方法参数来源 /// 设置第几个参数 - Task ConnectArgSourceNodeAsync(string fromNodeGuid, - string toNodeGuid, - JunctionType fromNodeJunctionType, - JunctionType toNodeJunctionType, - ConnectionArgSourceType argSourceType, - int argIndex); + Task ConnectArgSourceNodeAsync(string canvasGuid, + string fromNodeGuid, + string toNodeGuid, + JunctionType fromNodeJunctionType, + JunctionType toNodeJunctionType, + ConnectionArgSourceType argSourceType, + int argIndex); @@ -846,19 +946,19 @@ namespace Serein.Library.Api /// 控件类型 /// 节点在画布上的位置( /// 节点绑定的方法说明 - Task CreateNodeAsync(NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null); + Task CreateNodeAsync(string canvasGuid, NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null); /// /// 将节点放置在容器中 /// /// - Task PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid); + Task PlaceNodeToContainerAsync(string canvasGuid, string nodeGuid, string containerNodeGuid); /// /// 将节点从容器中脱离 /// /// - Task TakeOutNodeToContainerAsync(string nodeGuid); + Task TakeOutNodeToContainerAsync(string canvasGuid, string nodeGuid); /// @@ -876,7 +976,7 @@ namespace Serein.Library.Api /// 起始节点 /// 目标节点 /// 连接类型 - Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType); + Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType); /// /// 移除连接节点之间参数传递的关系 @@ -885,13 +985,13 @@ namespace Serein.Library.Api /// 目标节点Guid /// 连接到第几个参数 /// 参数来源类型 - Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex); + Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex); /// /// 移除节点/区域/基础控件 /// /// 待移除的节点Guid - Task RemoveNodeAsync(string nodeGuid); + Task RemoveNodeAsync(string canvasGuid, string nodeGuid); /// /// 改变可选参数的数目 diff --git a/Library/Extension/NodeModelExtension.cs b/Library/Extension/FlowModelExtension.cs similarity index 94% rename from Library/Extension/NodeModelExtension.cs rename to Library/Extension/FlowModelExtension.cs index b400bf4..9e6e652 100644 --- a/Library/Extension/NodeModelExtension.cs +++ b/Library/Extension/FlowModelExtension.cs @@ -7,14 +7,52 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using System.Xml.Linq; namespace Serein.Library { /// /// 节点方法拓展 /// - public static class NodeModelExtension + public static class FlowModelExtension { + /// + /// 导出为画布信息 + /// + /// + /// + public static FlowCanvasInfo ToInfo(this FlowCanvasModel model) + { + return new FlowCanvasInfo + { + Guid = model.Guid, + Height = model.Height, + Width = model.Width, + Name = model.Name, + ScaleX = model.ScaleX, + ScaleY = model.ScaleY, + ViewX = model.ViewX, + ViewY = model.ViewY, + }; + } + + /// + /// 从画布信息加载 + /// + /// + /// + public static void LoadInfo(this FlowCanvasModel model, FlowCanvasInfo info) + { + model.Guid = info.Guid; + model.Height = info.Height; + model.Width = info.Width; + model.Name = info.Name; + model.ScaleX = info.ScaleX; + model.ScaleY = info.ScaleY; + model.ViewX = info.ViewX; + model.ViewY = info.ViewY; + } + /// /// 输出方法参数信息 /// diff --git a/Library/FlowNode/ContainerFlowEnvironment.cs b/Library/FlowNode/ContainerFlowEnvironment.cs deleted file mode 100644 index 607b65c..0000000 --- a/Library/FlowNode/ContainerFlowEnvironment.cs +++ /dev/null @@ -1,390 +0,0 @@ -using Serein.Library.Api; -using Serein.Library.Utils; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Serein.Library -{ - - /// - /// 不提供流程操作能力,仅提供容器功能 - /// - public class ContainerFlowEnvironment : IFlowEnvironment, ISereinIOC - { - /// - /// 本地运行环境缓存的持久化实例 - /// - private Dictionary PersistennceInstance { get; } = new Dictionary(); - public ContainerFlowEnvironment() - { - - } - - private ISereinIOC sereinIOC => this; - public ISereinIOC IOC => sereinIOC; - - public string EnvName => throw new NotImplementedException(); - public string ProjectFileLocation => throw new NotImplementedException(); - - public bool IsGlobalInterrupt => throw new NotImplementedException(); - - public bool IsControlRemoteEnv => throw new NotImplementedException(); - - public InfoClass InfoClass { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public RunState FlowState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - public RunState FlipFlopState { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } - - public IFlowEnvironment CurrentEnv => this; - - public UIContextOperation UIContextOperation { get; set; } - public NodeMVVMManagement NodeMVVMManagement { get; set; } - - /// - /// 设置在UI线程操作的线程上下文 - /// - /// - public void SetUIContextOperation(UIContextOperation uiContextOperation) - { - this.UIContextOperation = uiContextOperation; - } - - public void ActivateFlipflopNode(string nodeGuid) - { - throw new NotImplementedException(); - } - - public Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex) - { - throw new NotImplementedException(); - } - - public Task ConnectArgSourceNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionArgSourceType argSourceType, int argIndex) - { - throw new NotImplementedException(); - } - - public Task ConnectInvokeNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionInvokeType invokeType) - { - throw new NotImplementedException(); - } - - public Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) - { - throw new NotImplementedException(); - } - - public Task CreateNodeAsync(NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null) - { - throw new NotImplementedException(); - } - - public Task ExitFlowAsync() - { - throw new NotImplementedException(); - } - - public void ExitRemoteEnv() - { - throw new NotImplementedException(); - } - - public Task GetEnvInfoAsync() - { - throw new NotImplementedException(); - } - - public Task GetProjectInfoAsync() - { - throw new NotImplementedException(); - } - - public Task InvokeNodeAsync(IDynamicContext context, string nodeGuid) - { - throw new NotImplementedException(); - } - - public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true) - { - throw new NotImplementedException(); - } - - public void LoadLibrary(string dllPath) - { - throw new NotImplementedException(); - } - - public bool LoadNativeLibraryOfRuning(string file) - { - throw new NotImplementedException(); - } - - public Task LoadNodeInfosAsync(List nodeInfos) - { - throw new NotImplementedException(); - } - - public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) - { - throw new NotImplementedException(); - } - - public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) - { - throw new NotImplementedException(); - } - - public void MoveNode(string nodeGuid, double x, double y) - { - throw new NotImplementedException(); - } - - public void NodeLocated(string nodeGuid) - { - throw new NotImplementedException(); - } - - public Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) - { - throw new NotImplementedException(); - } - - public Task PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid) - { - throw new NotImplementedException(); - } - - public Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex) - { - throw new NotImplementedException(); - } - - public Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) - { - throw new NotImplementedException(); - } - - public Task RemoveNodeAsync(string nodeGuid) - { - throw new NotImplementedException(); - } - - public void SaveProject() - { - throw new NotImplementedException(); - } - - public Task SetConnectPriorityInvoke(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) - { - throw new NotImplementedException(); - } - - public Task SetStartNodeAsync(string nodeGuid) - { - throw new NotImplementedException(); - } - - public Task StartAsyncInSelectNode(string startNodeGuid) - { - throw new NotImplementedException(); - } - - public Task StartFlowAsync() - { - throw new NotImplementedException(); - } - - public Task StartRemoteServerAsync(int port = 7525) - { - throw new NotImplementedException(); - } - - public void StopRemoteServer() - { - throw new NotImplementedException(); - } - - public Task TakeOutNodeToContainerAsync(string nodeGuid) - { - throw new NotImplementedException(); - } - - public void TerminateFlipflopNode(string nodeGuid) - { - throw new NotImplementedException(); - } - - public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type) - { - throw new NotImplementedException(); - } - - public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del) - { - throw new NotImplementedException(); - } - - public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo mdInfo) - { - throw new NotImplementedException(); - } - - public bool TryUnloadLibrary(string assemblyFullName) - { - throw new NotImplementedException(); - } - - public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial) - { - throw new NotImplementedException(); - } - - - - #region IOC容器相关 - ISereinIOC ISereinIOC.Reset() - { - sereinIOC.Reset(); - lock (PersistennceInstance) - { - foreach (var kvp in PersistennceInstance) - { - IOC.RegisterPersistennceInstance(kvp.Key, kvp.Value); - } - } // 重置后重新登记 - return this; - } - - ISereinIOC ISereinIOC.Register(Type type, params object[] parameters) - { - sereinIOC.Register(type, parameters); - return this; - } - - ISereinIOC ISereinIOC.Register(params object[] parameters) - { - sereinIOC.Register(parameters); - return this; - } - - ISereinIOC ISereinIOC.Register(params object[] parameters) - { - sereinIOC.Register(parameters); - return this; - } - - //T ISereinIOC.GetOrRegisterInstantiate() - //{ - // return sereinIOC.GetOrRegisterInstantiate(); - - //} - - //object ISereinIOC.GetOrRegisterInstantiate(Type type) - //{ - // return sereinIOC.GetOrRegisterInstantiate(type); - //} - - object ISereinIOC.Get(Type type) - { - return sereinIOC.Get(type); - - } - T ISereinIOC.Get() - { - return (T)sereinIOC.Get(typeof(T)); - } - //T ISereinIOC.Get(string key) - //{ - // return sereinIOC.Get(key); - //} - - - bool ISereinIOC.RegisterPersistennceInstance(string key, object instance) - { - if (PersistennceInstance.ContainsKey(key)) - { - return false; - } - PersistennceInstance.Add(key, instance); // 记录需要持久化的实例 - return sereinIOC.RegisterPersistennceInstance(key, instance); - } - - //bool ISereinIOC.RegisterInstance(string key, object instance) - //{ - // return sereinIOC.RegisterInstance(key, instance); - //} - - - object ISereinIOC.Instantiate(Type type) - { - return sereinIOC.Instantiate(type); - } - T ISereinIOC.Instantiate() - { - return sereinIOC.Instantiate(); - } - ISereinIOC ISereinIOC.Build() - { - sereinIOC.Build(); - return this; - } - - ISereinIOC ISereinIOC.Run(Action action) - { - sereinIOC.Run(action); - return this; - } - - ISereinIOC ISereinIOC.Run(Action action) - { - sereinIOC.Run(action); - return this; - } - - ISereinIOC ISereinIOC.Run(Action action) - { - sereinIOC.Run(action); - return this; - } - - ISereinIOC ISereinIOC.Run(Action action) - { - sereinIOC.Run(action); - return this; - } - - ISereinIOC ISereinIOC.Run(Action action) - { - sereinIOC.Run(action); - return this; - } - - ISereinIOC ISereinIOC.Run(Action action) - { - sereinIOC.Run(action); - return this; - } - - ISereinIOC ISereinIOC.Run(Action action) - { - sereinIOC.Run(action); - return this; - } - - ISereinIOC ISereinIOC.Run(Action action) - { - sereinIOC.Run(action); - return this; - } - - public bool TryGetNodeModel(string nodeGuid, out NodeModelBase nodeModel) - { - throw new NotImplementedException(); - } - #endregion - - - } -} diff --git a/Library/FlowNode/FlowCanvasModel.cs b/Library/FlowNode/FlowCanvasModel.cs new file mode 100644 index 0000000..98d05e6 --- /dev/null +++ b/Library/FlowNode/FlowCanvasModel.cs @@ -0,0 +1,57 @@ +using Serein.Library.Api; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.Library +{ + + [NodeProperty(ValuePath = NodeValuePath.Node)] + public partial class FlowCanvasModel + { + public FlowCanvasModel(IFlowEnvironment env) + { + Env = env; + } + public IFlowEnvironment Env { get; } + + [PropertyInfo(IsProtection = true)] + private string _guid; + + [PropertyInfo(IsNotification = true)] + private string _name; + + [PropertyInfo(IsNotification = true)] + private double _width; + + [PropertyInfo(IsNotification = true)] + private double _height; + + /// + /// 预览位置X + /// + [PropertyInfo] + private double _viewX ; + + /// + /// 预览位置Y + /// + [PropertyInfo] + private double _viewY ; + + /// + /// 缩放比例X + /// + [PropertyInfo] + private double _scaleX ; + + /// + /// 缩放比例Y + /// + [PropertyInfo] + private double _scaleY ; + + } +} diff --git a/Library/FlowNode/NodeModelBaseData.cs b/Library/FlowNode/NodeModelBaseData.cs index aa95c7e..44efedb 100644 --- a/Library/FlowNode/NodeModelBaseData.cs +++ b/Library/FlowNode/NodeModelBaseData.cs @@ -35,6 +35,14 @@ namespace Serein.Library [PropertyInfo(IsProtection = true)] private NodeControlType _controlType; + + /// + /// 所属画布 + /// + [PropertyInfo(IsProtection = true)] + private string _canvasGuid ; + + /// /// 在画布中的位置 /// diff --git a/Library/FlowNode/SereinProjectData.cs b/Library/FlowNode/SereinProjectData.cs index c825c96..778fd3b 100644 --- a/Library/FlowNode/SereinProjectData.cs +++ b/Library/FlowNode/SereinProjectData.cs @@ -87,7 +87,7 @@ namespace Serein.Library /// 画布 /// - public FlowCanvas Canvas { get; set; } + public FlowCanvasInfo Canvas { get; set; } /// /// 版本 @@ -98,8 +98,12 @@ namespace Serein.Library /// /// 画布信息,项目文件相关 /// - public class FlowCanvas + public class FlowCanvasInfo { + public string Guid { get; set; } + + public string Name { get; set; } + /// /// 宽度 /// @@ -178,6 +182,12 @@ namespace Serein.Library /// public class NodeInfo { + /// + /// 所属画布Guid + /// + public string CanvasGuid { get; set; } + + /// /// 节点的GUID /// diff --git a/NodeFlow/Env/EnvMsgTheme.cs b/NodeFlow/Env/EnvMsgTheme.cs index 15bf610..b69cc04 100644 --- a/NodeFlow/Env/EnvMsgTheme.cs +++ b/NodeFlow/Env/EnvMsgTheme.cs @@ -22,6 +22,14 @@ /// public const string ExitFlow = nameof(ExitFlow); /// + /// 尝试新增画布 + /// + public const string CreateCanvas = nameof(CreateCanvas); + /// + /// 尝试移除画布 + /// + public const string RemoveCanvas = nameof(RemoveCanvas); + /// /// 尝试移动某个节点 /// public const string MoveNode = nameof(MoveNode); diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 6a7010f..bb6158a 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -1,5 +1,6 @@ using Serein.Library; using Serein.Library.Api; +using Serein.Library.FlowNode; using Serein.Library.Utils; using Serein.Library.Utils.SereinExpression; using Serein.NodeFlow.Model; @@ -201,6 +202,16 @@ namespace Serein.NodeFlow.Env /// public event EnvOutHandler? OnEnvOut; + /// + /// 本地环境添加了画布 + /// + public event CanvasCreateHandler OnCanvasCreate; + + /// + /// 本地环境移除了画布 + /// + public event CanvasRemoveHandler OnCanvasRemove; + #endregion #region 属性 @@ -271,40 +282,6 @@ namespace Serein.NodeFlow.Env /// private readonly FlowLibraryManagement FlowLibraryManagement; -#if false - - /// - /// Library 与 MethodDetailss的依赖关系 - /// - public ConcurrentDictionary> MethodDetailsOfLibraryInfos { get; } = []; - - - /// - /// 存储已加载的程序集 - /// Key:程序集的FullName - /// Value:构造的方法信息 - /// - public ConcurrentDictionary LibraryInfos { get; } = []; - - /// - /// 存储已加载的方法信息。描述所有DLL中NodeAction特性的方法的原始副本 - /// Key:反射时获取的MethodInfo.MehtodName - /// Value:构造的方法信息 - /// - public ConcurrentDictionary MethodDetailss { get; } = []; - - /// - /// 从dll中加载的类的注册类型 - /// - private Dictionary> AutoRegisterTypes { get; } = []; - - /// - /// 存放所有通过Emit加载的委托 - /// md.Methodname - delegate - /// - private ConcurrentDictionary MethodDelegates { get; } = []; -#endif - /// /// IOC对象容器管理 /// @@ -321,6 +298,11 @@ namespace Serein.NodeFlow.Env /// private Dictionary NodeModels { get; } = []; + /// + /// 运行环境加载的画布集合 + /// + private Dictionary FlowCanvass { get; } = []; + /// /// 存放触发器节点(运行时全部调用) /// @@ -382,7 +364,6 @@ namespace Serein.NodeFlow.Env } - /// /// 异步运行 /// @@ -581,7 +562,7 @@ namespace Serein.NodeFlow.Env _ = Task.Run( async () => { await LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息 - await SetStartNodeAsync(projectData.StartNode); // 设置起始节点 + await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点 }); } @@ -786,7 +767,46 @@ namespace Serein.NodeFlow.Env //} } - + /// + /// 增加画布 + /// + /// 画布名称 + /// 宽度 + /// 高度 + /// + public Task CreateCanvasAsync(string canvasName, int width, int height) + { + var model = new FlowCanvasModel(this) + { + Guid = Guid.NewGuid().ToString(), + Height = height, + Name = canvasName, + Width = height, + }; + var info = model.ToInfo(); + FlowCanvass.Add(model.Guid, model); + OnCanvasCreate.Invoke(new CanvasCreateEventArgs(info)); + return Task.FromResult(info); + } + + /// + /// 删除画布 + /// + /// 画布Guid + /// + public Task RemoteCanvasAsync(string canvasGuid) + { + + if (!FlowCanvass.TryGetValue(canvasGuid, out var model)) + { + return Task.FromResult(false); + } + var count = NodeModels.Values.Count(node => node.CanvasGuid.Equals(canvasGuid)); + return Task.FromResult(count == 0); + } + + + /// /// 从节点信息集合批量加载节点控件 /// @@ -827,7 +847,7 @@ namespace Serein.NodeFlow.Env TryAddNode(nodeModel); // 加载项目时将节点加载到环境中 await UIContextOperation.InvokeAsync(() => - OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); // 添加到UI上 + OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上 } #endregion @@ -853,7 +873,7 @@ namespace Serein.NodeFlow.Env if (result) { await UIContextOperation.InvokeAsync(() => OnNodePlace?.Invoke( - new NodePlaceEventArgs(nodeModel.Guid, containerNode.Guid))); + new NodePlaceEventArgs(nodeInfo.CanvasGuid, nodeModel.Guid, containerNode.Guid))); } @@ -864,6 +884,7 @@ namespace Serein.NodeFlow.Env #region 确定节点之间的方法调用关系 foreach (var nodeInfo in nodeInfos) { + var canvasGuid = nodeInfo.CanvasGuid; if (!TryGetNodeModel(nodeInfo.Guid, out var fromNodeModel)) { return; @@ -886,7 +907,7 @@ namespace Serein.NodeFlow.Env // 防御性代码,加载正常保存的项目文件不会进入这里 continue; }; - var isSuccessful = ConnectInvokeOfNode(fromNodeModel, toNodeModel, item.connectionType); // 加载时确定节点间的连接关系 + var isSuccessful = ConnectInvokeOfNode(canvasGuid, fromNodeModel, toNodeModel, item.connectionType); // 加载时确定节点间的连接关系 } } @@ -916,6 +937,7 @@ namespace Serein.NodeFlow.Env #region 确定节点之间的参数调用关系 foreach (var toNode in NodeModels.Values) { + var canvasGuid = toNode.Guid; if (toNode.MethodDetails.ParameterDetailss == null) { continue; @@ -927,7 +949,7 @@ namespace Serein.NodeFlow.Env && NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode)) { - await ConnectArgSourceOfNodeAsync(fromNode, toNode, pd.ArgDataSourceType, pd.Index); + await ConnectArgSourceOfNodeAsync(canvasGuid, fromNode, toNode, pd.ArgDataSourceType, pd.Index); } } } @@ -945,14 +967,19 @@ namespace Serein.NodeFlow.Env /// /// 流程正在运行时创建节点 /// - /// - /// + /// 所属画布 + /// 所属类型 + /// 所处位置 /// 如果是表达式节点条件节点,该项为null - public Task CreateNodeAsync(NodeControlType nodeControlType, + public Task CreateNodeAsync(string canvasGuid, + NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo? methodDetailsInfo = null) { - + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return Task.FromResult(null); + } NodeModelBase? nodeModel; if (methodDetailsInfo is null) { @@ -976,13 +1003,13 @@ namespace Serein.NodeFlow.Env nodeModel.Position = position; // 通知UI更改 - UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position))); + UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(canvasGuid, nodeModel, position))); // 因为需要UI先布置了元素,才能通知UI变更特效 // 如果不存在流程起始控件,默认设置为流程起始控件 if (StartNode is null) { - SetStartNode(nodeModel); + SetStartNode(canvasGuid, nodeModel); } var nodeInfo = nodeModel.ToInfo(); return Task.FromResult(nodeInfo); @@ -993,8 +1020,13 @@ namespace Serein.NodeFlow.Env /// 将节点放置在容器中 /// /// - public Task PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid) + public Task PlaceNodeToContainerAsync(string canvasGuid, + string nodeGuid, string containerNodeGuid) { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return Task.FromResult(false); + } // 获取目标节点与容器节点 if (!TryGetNodeModel(nodeGuid, out var nodeModel)) { @@ -1017,7 +1049,7 @@ namespace Serein.NodeFlow.Env { _ = UIContextOperation?.InvokeAsync(() => { - OnNodePlace?.Invoke(new NodePlaceEventArgs(nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置 + OnNodePlace?.Invoke(new NodePlaceEventArgs(canvasGuid, nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置 }); } return Task.FromResult(result); @@ -1028,8 +1060,13 @@ namespace Serein.NodeFlow.Env /// 将节点从容器节点中脱离 /// /// - public Task TakeOutNodeToContainerAsync(string nodeGuid) + public Task TakeOutNodeToContainerAsync(string canvasGuid, + string nodeGuid) { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return Task.FromResult(false); + } // 获取目标节点与容器节点 if (!TryGetNodeModel(nodeGuid, out var nodeModel)) { @@ -1044,7 +1081,7 @@ namespace Serein.NodeFlow.Env { _ = UIContextOperation?.InvokeAsync(() => { - OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(nodeGuid)); // 重新放置在画布上 + OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(canvasGuid, nodeGuid)); // 重新放置在画布上 }); } return Task.FromResult(result); @@ -1059,8 +1096,12 @@ namespace Serein.NodeFlow.Env /// /// /// - public async Task RemoveNodeAsync(string nodeGuid) + public async Task RemoveNodeAsync(string canvasGuid, string nodeGuid) { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return false; + } if (!TryGetNodeModel(nodeGuid, out var remoteNode)) { return false; @@ -1083,6 +1124,7 @@ namespace Serein.NodeFlow.Env pNode.SuccessorNodes[pCType].Remove(remoteNode); UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs( + canvasGuid, pNode.Guid, remoteNode.Guid, JunctionOfConnectionType.Invoke, @@ -1100,7 +1142,7 @@ namespace Serein.NodeFlow.Env { NodeModelBase? toNode = snc.Value[i]; - await RemoteConnectAsync(remoteNode, toNode, connectionType); + await RemoteConnectAsync(canvasGuid, remoteNode, toNode, connectionType); } } @@ -1109,7 +1151,7 @@ namespace Serein.NodeFlow.Env // 从集合中移除节点 NodeModels.Remove(nodeGuid); - UIContextOperation?.Invoke(() => OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid))); + UIContextOperation?.Invoke(() => OnNodeRemove?.Invoke(new NodeRemoveEventArgs(canvasGuid, nodeGuid))); return true; } @@ -1121,15 +1163,16 @@ namespace Serein.NodeFlow.Env /// 起始节点控制点 /// 目标节点控制点 /// 连接关系 - public Task ConnectInvokeNodeAsync(string fromNodeGuid, + public Task ConnectInvokeNodeAsync(string canvasGuid, + string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionInvokeType invokeType) { - + // 获取起始节点与目标节点 - if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) { return Task.FromResult(false); } @@ -1150,7 +1193,7 @@ namespace Serein.NodeFlow.Env (fromNode, toNode) = (toNode, fromNode); } // 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点 - state = ConnectInvokeOfNode(fromNode, toNode, invokeType); // 本地环境进行连接 + state = ConnectInvokeOfNode(canvasGuid, fromNode, toNode, invokeType); // 本地环境进行连接 } return Task.FromResult(state); @@ -1192,16 +1235,16 @@ namespace Serein.NodeFlow.Env /// 目标节点Guid /// 连接关系 /// - public async Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) + public async Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) { // 获取起始节点与目标节点 - if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) { return false; } if (fromNode is null || toNode is null) return false; - var result = await RemoteConnectAsync(fromNode, toNode, connectionType); + var result = await RemoteConnectAsync(canvasGuid, fromNode, toNode, connectionType); return result; } @@ -1215,7 +1258,8 @@ namespace Serein.NodeFlow.Env /// 目标节点的第几个参数 /// 调用目标节点对应方法时,对应参数来源类型 /// - public async Task ConnectArgSourceNodeAsync(string fromNodeGuid, + public async Task ConnectArgSourceNodeAsync(string canvasGuid, + string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, @@ -1224,7 +1268,7 @@ namespace Serein.NodeFlow.Env { // 获取起始节点与目标节点 - if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) { return false; } @@ -1246,7 +1290,7 @@ namespace Serein.NodeFlow.Env } // 确定方法入参关系 - state = await ConnectArgSourceOfNodeAsync(fromNode, toNode, connectionArgSourceType, argIndex); // 本地环境进行连接 + state = await ConnectArgSourceOfNodeAsync(canvasGuid, fromNode, toNode, connectionArgSourceType, argIndex); // 本地环境进行连接 } return state; @@ -1259,15 +1303,15 @@ namespace Serein.NodeFlow.Env /// 起始节点Guid /// 目标节点Guid /// 连接到第几个参数 - public async Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex) + public async Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex) { // 获取起始节点与目标节点 - if (!TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(fromNodeGuid, out var fromNode) || !TryGetNodeModel(toNodeGuid, out var toNode)) { return false; } if (fromNode is null || toNode is null) return false; - var result = await RemoteConnectAsync(fromNode, toNode, argIndex); + var result = await RemoteConnectAsync(canvasGuid, fromNode, toNode, argIndex); return result; } @@ -1328,9 +1372,9 @@ namespace Serein.NodeFlow.Env /// /// /// - public void MoveNode(string nodeGuid, double x, double y) + public void MoveNode(string canvasGuid, string nodeGuid, double x, double y) { - if (!TryGetNodeModel(nodeGuid, out var nodeModel)) + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(nodeGuid, out var nodeModel)) { return; } @@ -1344,13 +1388,13 @@ namespace Serein.NodeFlow.Env /// 设置起点控件 /// /// - public Task SetStartNodeAsync(string newNodeGuid) + public Task SetStartNodeAsync(string canvasGuid, string newNodeGuid) { - if (!TryGetNodeModel(newNodeGuid, out var newStartNodeModel)) + if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(newNodeGuid, out var newStartNodeModel)) { return Task.FromResult(StartNode?.Guid ?? string.Empty); } - SetStartNode(newStartNodeModel); + SetStartNode(canvasGuid, newStartNodeModel); return Task.FromResult(StartNode?.Guid ?? string.Empty); } @@ -1483,7 +1527,6 @@ namespace Serein.NodeFlow.Env #endregion - #region 流程依赖类库的接口 @@ -1580,21 +1623,27 @@ namespace Serein.NodeFlow.Env /// 目标节点Model /// 连接关系 /// - private async Task RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType connectionType) + private async Task RemoteConnectAsync(string canvasGuid, NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType connectionType) { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return false; + } fromNode.SuccessorNodes[connectionType].Remove(toNode); toNode.PreviousNodes[connectionType].Remove(fromNode); if (OperatingSystem.IsWindows()) { - await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs( - fromNode.Guid, - toNode.Guid, - JunctionOfConnectionType.Invoke, - connectionType, - NodeConnectChangeEventArgs.ConnectChangeType.Remove))); - } + await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke( + new NodeConnectChangeEventArgs( + canvasGuid, + fromNode.Guid, + toNode.Guid, + JunctionOfConnectionType.Invoke, + connectionType, + NodeConnectChangeEventArgs.ConnectChangeType.Remove))); + } return true; } /// @@ -1604,8 +1653,12 @@ namespace Serein.NodeFlow.Env /// 目标节点Model /// 连接关系 /// - private async Task RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, int argIndex) + private async Task RemoteConnectAsync(string canvasGuid, NodeModelBase fromNode, NodeModelBase toNode, int argIndex) { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return false; + } if (string.IsNullOrEmpty(toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid)) { return false; @@ -1615,14 +1668,16 @@ namespace Serein.NodeFlow.Env if (OperatingSystem.IsWindows()) { - await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs( - fromNode.Guid, - toNode.Guid, - JunctionOfConnectionType.Arg, - argIndex, - ConnectionArgSourceType.GetPreviousNodeData, - NodeConnectChangeEventArgs.ConnectChangeType.Remove))); - } + await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke( + new NodeConnectChangeEventArgs( + canvasGuid, + fromNode.Guid, + toNode.Guid, + JunctionOfConnectionType.Arg, + argIndex, + ConnectionArgSourceType.GetPreviousNodeData, + NodeConnectChangeEventArgs.ConnectChangeType.Remove))); + } return true; } @@ -1721,8 +1776,12 @@ namespace Serein.NodeFlow.Env /// 起始节点 /// 目标节点 /// 连接关系 - private bool ConnectInvokeOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType invokeType) + private bool ConnectInvokeOfNode(string canvasGuid, NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType invokeType) { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return false; + } if (fromNode is null || toNode is null || fromNode == toNode) { return false; @@ -1783,6 +1842,7 @@ namespace Serein.NodeFlow.Env UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke( new NodeConnectChangeEventArgs( + canvasGuid, fromNode.Guid, // 从哪个节点开始 toNode.Guid, // 连接到那个节点 JunctionOfConnectionType.Invoke, @@ -1810,21 +1870,28 @@ namespace Serein.NodeFlow.Env /// /// /// - private async Task ConnectArgSourceOfNodeAsync(NodeModelBase fromNode, + private async Task ConnectArgSourceOfNodeAsync(string canvasGuid, + NodeModelBase fromNode, NodeModelBase toNode, ConnectionArgSourceType connectionArgSourceType, int argIndex) { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return false; + } + var toNodeArgSourceGuid = toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid; if (!string.IsNullOrEmpty(toNodeArgSourceGuid)) { - await RemoteConnectAsync(fromNode, toNode, argIndex); + await RemoteConnectAsync(canvasGuid, fromNode, toNode, argIndex); } toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = fromNode.Guid; toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = connectionArgSourceType; await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke( new NodeConnectChangeEventArgs( + canvasGuid, fromNode.Guid, // 从哪个节点开始 toNode.Guid, // 连接到那个节点 JunctionOfConnectionType.Arg, @@ -1841,11 +1908,15 @@ namespace Serein.NodeFlow.Env /// /// /// - private void SetStartNode(NodeModelBase newStartNode) + private void SetStartNode(string canvasGuid, NodeModelBase newStartNode) { + if (!FlowCanvass.ContainsKey(canvasGuid)) + { + return; + } var oldNodeGuid = StartNode?.Guid; StartNode = newStartNode; - UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid))); + UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(canvasGuid, oldNodeGuid, StartNode.Guid))); //if (OperatingSystem.IsWindows()) //{ diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs index fdc2aa0..c19b175 100644 --- a/NodeFlow/Env/FlowEnvironmentDecorator.cs +++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs @@ -56,6 +56,8 @@ namespace Serein.NodeFlow.Env private int _loadingProjectFlag = 0; // 使用原子自增代替锁 + + /// /// 传入false时,将停止数据通知。传入true时, /// @@ -131,6 +133,18 @@ namespace Serein.NodeFlow.Env remove { currentFlowEnvironmentEvent.OnNodeConnectChange -= value; } } + + public event CanvasCreateHandler OnCanvasCreate + { + add { currentFlowEnvironmentEvent.OnCanvasCreate += value; } + remove { currentFlowEnvironmentEvent.OnCanvasCreate -= value; } + } + public event CanvasRemoveHandler OnCanvasRemove + { + add { currentFlowEnvironmentEvent.OnCanvasRemove += value; } + remove { currentFlowEnvironmentEvent.OnCanvasRemove -= value; } + } + public event NodeCreateHandler OnNodeCreate { add { currentFlowEnvironmentEvent.OnNodeCreate += value; } @@ -217,7 +231,27 @@ namespace Serein.NodeFlow.Env currentFlowEnvironment.ActivateFlipflopNode(nodeGuid); } - + /// + /// 增加画布 + /// + /// 画布名称 + /// 宽度 + /// 高度 + /// + public async Task CreateCanvasAsync(string canvasName, int width, int height) + { + return await currentFlowEnvironment.CreateCanvasAsync(canvasName, width, height); + } + + /// + /// 删除画布 + /// + /// 画布Guid + /// + public async Task RemoteCanvasAsync(string canvasGuid) + { + return await currentFlowEnvironment.RemoteCanvasAsync(canvasGuid); + } /// @@ -228,13 +262,14 @@ namespace Serein.NodeFlow.Env /// 起始节点控制点 /// 目标节点控制点 /// 决定了方法执行后的后继行为 - public async Task ConnectInvokeNodeAsync(string fromNodeGuid, + public async Task ConnectInvokeNodeAsync(string canvasGuid, + string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionInvokeType invokeType) { - return await currentFlowEnvironment.ConnectInvokeNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, invokeType); + return await currentFlowEnvironment.ConnectInvokeNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, invokeType); } @@ -247,14 +282,15 @@ namespace Serein.NodeFlow.Env /// 目标节点控制点 /// 决定了方法参数来源 /// 设置第几个参数 - public async Task ConnectArgSourceNodeAsync(string fromNodeGuid, + public async Task ConnectArgSourceNodeAsync(string canvasGuid, + string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionArgSourceType argSourceType, int argIndex) { - return await currentFlowEnvironment.ConnectArgSourceNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, argSourceType, argIndex); + return await currentFlowEnvironment.ConnectArgSourceNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, argSourceType, argIndex); } @@ -291,10 +327,10 @@ namespace Serein.NodeFlow.Env SetProjectLoadingFlag(true); } - public async Task CreateNodeAsync(NodeControlType nodeBase, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null) + public async Task CreateNodeAsync(string canvasGuid, NodeControlType nodeBase, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null) { SetProjectLoadingFlag(false); - var result = await currentFlowEnvironment.CreateNodeAsync(nodeBase, position, methodDetailsInfo); // 装饰器调用 + var result = await currentFlowEnvironment.CreateNodeAsync(canvasGuid, nodeBase, position, methodDetailsInfo); // 装饰器调用 SetProjectLoadingFlag(true); return result; } @@ -304,10 +340,10 @@ namespace Serein.NodeFlow.Env /// 将节点放置在容器中 /// /// - public async Task PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid) + public async Task PlaceNodeToContainerAsync(string canvasGuid, string nodeGuid, string containerNodeGuid) { SetProjectLoadingFlag(false); - var result = await currentFlowEnvironment.PlaceNodeToContainerAsync(nodeGuid, containerNodeGuid); // 装饰器调用 + var result = await currentFlowEnvironment.PlaceNodeToContainerAsync(canvasGuid, nodeGuid, containerNodeGuid); // 装饰器调用 SetProjectLoadingFlag(true); return result; } @@ -316,10 +352,10 @@ namespace Serein.NodeFlow.Env /// 将节点从容器中脱离 /// /// - public async Task TakeOutNodeToContainerAsync(string nodeGuid) + public async Task TakeOutNodeToContainerAsync(string canvasGuid, string nodeGuid) { SetProjectLoadingFlag(false); - var result = await currentFlowEnvironment.TakeOutNodeToContainerAsync(nodeGuid); // 装饰器调用 + var result = await currentFlowEnvironment.TakeOutNodeToContainerAsync(canvasGuid,nodeGuid); // 装饰器调用 SetProjectLoadingFlag(true); return result; } @@ -374,9 +410,9 @@ namespace Serein.NodeFlow.Env currentFlowEnvironment.MonitorObjectNotification(nodeGuid, monitorData, sourceType); } - public void MoveNode(string nodeGuid, double x, double y) + public void MoveNode(string canvasGuid, string nodeGuid, double x, double y) { - currentFlowEnvironment.MoveNode(nodeGuid, x, y); + currentFlowEnvironment.MoveNode(canvasGuid, nodeGuid, x, y); } public void NodeLocated(string nodeGuid) @@ -408,9 +444,9 @@ namespace Serein.NodeFlow.Env /// /// /// - public async Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) + public async Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) { - return await currentFlowEnvironment.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, connectionType); + return await currentFlowEnvironment.RemoveConnectInvokeAsync(canvasGuid, fromNodeGuid, toNodeGuid, connectionType); } /// @@ -420,14 +456,14 @@ namespace Serein.NodeFlow.Env /// 目标节点Guid /// 连接到第几个参数 /// 参数来源类型 - public async Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex) + public async Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex) { - return await currentFlowEnvironment.RemoveConnectArgSourceAsync(fromNodeGuid, toNodeGuid, argIndex); + return await currentFlowEnvironment.RemoveConnectArgSourceAsync(canvasGuid, fromNodeGuid, toNodeGuid, argIndex); } - public async Task RemoveNodeAsync(string nodeGuid) + public async Task RemoveNodeAsync(string canvasGuid, string nodeGuid) { - return await currentFlowEnvironment.RemoveNodeAsync(nodeGuid); + return await currentFlowEnvironment.RemoveNodeAsync(canvasGuid, nodeGuid); } @@ -477,9 +513,9 @@ namespace Serein.NodeFlow.Env #endif #endregion - public async Task SetStartNodeAsync(string nodeGuid) + public async Task SetStartNodeAsync(string canvasGuid, string nodeGuid) { - return await currentFlowEnvironment.SetStartNodeAsync(nodeGuid); + return await currentFlowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid); } public async Task StartFlowAsync() diff --git a/NodeFlow/Env/MsgControllerOfClient.cs b/NodeFlow/Env/MsgControllerOfClient.cs index 8943f43..767cd8c 100644 --- a/NodeFlow/Env/MsgControllerOfClient.cs +++ b/NodeFlow/Env/MsgControllerOfClient.cs @@ -132,7 +132,6 @@ namespace Serein.NodeFlow.Env - /// /// 从某个节点开始运行 /// @@ -164,6 +163,18 @@ namespace Serein.NodeFlow.Env _ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, null); } + [AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateCanvas, IsReturnValue = false)] + public void CreateCanvas([UseMsgId] string msgId, [UseData] FlowCanvasInfo canvasInfo) + { + _ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, canvasInfo); + } + + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveCanvas, IsReturnValue = false)] + public void RemoveCanvas([UseMsgId] string msgId, [UseData] bool state) + { + _ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state); + } + /// /// 创建节点 diff --git a/NodeFlow/Env/MsgControllerOfServer.cs b/NodeFlow/Env/MsgControllerOfServer.cs index 3487a6a..8504803 100644 --- a/NodeFlow/Env/MsgControllerOfServer.cs +++ b/NodeFlow/Env/MsgControllerOfServer.cs @@ -310,6 +310,22 @@ namespace Serein.NodeFlow.Env return false; } + + [AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateCanvas, IsReturnValue = false)] + public async Task CreateCanvas(string canvasName, int width, int height) + { + var canvasInfo = await environment.CreateCanvasAsync(canvasName, width, height); // 监听到客户端创建节点的请求 + return canvasInfo; + } + + [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveCanvas, IsReturnValue = false)] + public async Task RemoveCanvas([Needful] string canvasGuid) + { + var result = await environment.RemoteCanvasAsync(canvasGuid); // 监听到客户端创建节点的请求 + return new { state = result} ; + } + + /// /// 从远程环境创建节点 /// @@ -317,13 +333,13 @@ namespace Serein.NodeFlow.Env /// /// 如果是表达式节点条件节点,该项为null [AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode,ArgNotNull = false)] - public async Task CreateNode([Needful] string nodeType, [Needful] PositionOfUI position, MethodDetailsInfo? mdInfo = null) + public async Task CreateNode([Needful] string canvasGuid, [Needful] string nodeType, [Needful] PositionOfUI position, MethodDetailsInfo? mdInfo = null) { if (!EnumHelper.TryConvertEnum(nodeType, out var nodeControlType)) { return null; } - var nodeInfo = await environment.CreateNodeAsync(nodeControlType, position, mdInfo); // 监听到客户端创建节点的请求 + var nodeInfo = await environment.CreateNodeAsync(canvasGuid, nodeControlType, position, mdInfo); // 监听到客户端创建节点的请求 return nodeInfo; } @@ -333,9 +349,9 @@ namespace Serein.NodeFlow.Env /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode)] - public async Task RemoveNode(string nodeGuid) + public async Task RemoveNode(string canvasGuid, string nodeGuid) { - var result = await environment.RemoveNodeAsync(nodeGuid); + var result = await environment.RemoveNodeAsync(canvasGuid, nodeGuid); return new { state = result }; } /// @@ -345,20 +361,21 @@ namespace Serein.NodeFlow.Env /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.PlaceNode)] - public async Task PlaceNode(string nodeGuid, string containerNodeGuid) + public async Task PlaceNode( string canvasGuid, string nodeGuid, string containerNodeGuid) { - var result = await environment.PlaceNodeToContainerAsync(nodeGuid, containerNodeGuid); + var result = await environment.PlaceNodeToContainerAsync(canvasGuid,nodeGuid, containerNodeGuid); return new { state = result }; } + /// /// 远程从远程环境移除节点 /// /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.TakeOutNode)] - public async Task TakeOutNode(string nodeGuid) + public async Task TakeOutNode(string canvasGuid, string nodeGuid) { - var result = await environment.TakeOutNodeToContainerAsync(nodeGuid); + var result = await environment.TakeOutNodeToContainerAsync(canvasGuid, nodeGuid); return new { state = result }; } @@ -366,13 +383,15 @@ namespace Serein.NodeFlow.Env /// /// 远程连接节点的方法调用关系 /// + /// 画布 /// 起始节点 /// 目标节点 /// 起始节点控制点 /// 目标节点控制点 /// 连接关系 [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectInvokeNode)] - public async Task ConnectInvokeNode(string fromNodeGuid, + public async Task ConnectInvokeNode(string canvasGuid, + string fromNodeGuid, string toNodeGuid, string fromJunctionType, string toJunctionType, @@ -422,7 +441,7 @@ namespace Serein.NodeFlow.Env SereinEnv.WriteLine(InfoType.INFO, $"目标节点:{toNodeGuid}"); SereinEnv.WriteLine(InfoType.INFO, $"链接请求:{(tmpFromJunctionType, tmpToJunctionType)}"); - var result = await environment.ConnectInvokeNodeAsync(fromNodeGuid, toNodeGuid, tmpFromJunctionType, tmpToJunctionType, tmpConnectionType); + var result = await environment.ConnectInvokeNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, tmpFromJunctionType, tmpToJunctionType, tmpConnectionType); return new { state = result }; } @@ -433,7 +452,7 @@ namespace Serein.NodeFlow.Env /// 目标节点Guid /// 连接关系 [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveInvokeConnect)] - public async Task RemoveInvokeConnect(string fromNodeGuid, string toNodeGuid, string invokeType) + public async Task RemoveInvokeConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, string invokeType) { if (!EnumHelper.TryConvertEnum(invokeType, out var tmpConnectionType)) { @@ -442,10 +461,11 @@ namespace Serein.NodeFlow.Env state = false }; } - var result = await environment.RemoveConnectInvokeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType); + var result = await environment.RemoveConnectInvokeAsync(canvasGuid,fromNodeGuid, toNodeGuid, tmpConnectionType); return new { state = result }; } + /// /// 远程连接节点的参数传递关系 @@ -457,7 +477,8 @@ namespace Serein.NodeFlow.Env /// 入参参数来源类型 /// 第几个参数 [AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectArgSourceNode)] - public async Task ConnectArgSourceNode(string fromNodeGuid, + public async Task ConnectArgSourceNode(string canvasGuid, + string fromNodeGuid, string toNodeGuid, string fromJunctionType, string toJunctionType, @@ -511,7 +532,7 @@ namespace Serein.NodeFlow.Env } // 调用环境接口进行连接 - var result = await environment.ConnectArgSourceNodeAsync(fromNodeGuid, toNodeGuid, tmpFromJunctionType, tmpToJunctionType, tmpArgSourceType, argIndex); + var result = await environment.ConnectArgSourceNodeAsync(canvasGuid, fromNodeGuid, toNodeGuid, tmpFromJunctionType, tmpToJunctionType, tmpArgSourceType, argIndex); return new { state = result }; } @@ -522,11 +543,11 @@ namespace Serein.NodeFlow.Env /// 目标节点Guid /// 目标节点的第几个参数 [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveArgSourceConnect)] - public async Task RemoveArgSourceConnect(string fromNodeGuid, string toNodeGuid, int argIndex) + public async Task RemoveArgSourceConnect(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex) { - var result = await environment.RemoveConnectArgSourceAsync(fromNodeGuid, toNodeGuid, argIndex); + var result = await environment.RemoveConnectArgSourceAsync(canvasGuid,fromNodeGuid, toNodeGuid, argIndex); return new { state = result @@ -540,9 +561,9 @@ namespace Serein.NodeFlow.Env /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.MoveNode)] - public void MoveNode(string nodeGuid, double x, double y) + public void MoveNode(string canvasGuid, string nodeGuid, double x, double y) { - environment.MoveNode(nodeGuid, x, y); + environment.MoveNode(canvasGuid, nodeGuid, x, y); } /// @@ -550,9 +571,9 @@ namespace Serein.NodeFlow.Env /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.SetStartNode)] - public async Task SetStartNode([NotNull]string nodeGuid) + public async Task SetStartNode(string canvasGuid, [NotNull]string nodeGuid) { - return await environment.SetStartNodeAsync(nodeGuid); + return await environment.SetStartNodeAsync(canvasGuid, nodeGuid); } diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs index bddb9c6..a98f42c 100644 --- a/NodeFlow/Env/RemoteFlowEnvironment.cs +++ b/NodeFlow/Env/RemoteFlowEnvironment.cs @@ -45,7 +45,9 @@ namespace Serein.NodeFlow.Env public event LoadDllHandler OnDllLoad; public event ProjectLoadedHandler OnProjectLoaded; - public event ProjectSavingHandler? OnProjectSaving; + public event ProjectSavingHandler OnProjectSaving; + public event CanvasCreateHandler OnCanvasCreate; + public event CanvasRemoveHandler OnCanvasRemove; public event NodeConnectChangeHandler OnNodeConnectChange; public event NodeCreateHandler OnNodeCreate; public event NodeRemoveHandler OnNodeRemove; @@ -165,9 +167,14 @@ namespace Serein.NodeFlow.Env } #endregion + var nodeInfos = flowEnvInfo.Project.Nodes.ToList(); + LoadNodeInfos(nodeInfos); // 加载节点 - LoadNodeInfos(flowEnvInfo.Project.Nodes.ToList()); // 加载节点 - _ = SetStartNodeAsync(flowEnvInfo.Project.StartNode); // 设置流程起点 + var canvasGuid = nodeInfos.FirstOrDefault(item => item.Guid == flowEnvInfo.Project.StartNode)?.CanvasGuid; + if (!string.IsNullOrEmpty(canvasGuid)) + { + _ = SetStartNodeAsync(canvasGuid, flowEnvInfo.Project.StartNode); // 设置流程起点 + } UIContextOperation?.Invoke(() => { OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); // 加载完成 @@ -413,13 +420,67 @@ namespace Serein.NodeFlow.Env return result; } + /// + /// 增加画布 + /// + /// 画布名称 + /// 宽度 + /// 高度 + /// + public async Task CreateCanvasAsync(string canvasName, int width, int height) + { + var info = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.CreateCanvas, new + { + canvasName, + width, + height, + }); + if (info is not null) + { + var model = new FlowCanvasModel(this) + { + Guid = info.Guid, + Height = info.Height, + Name = info.Name, + Width = info.Width, + }; + UIContextOperation?.Invoke(() => + OnCanvasCreate?.Invoke(new CanvasCreateEventArgs(info))); + + return info; + } + + return null; + } + + /// + /// 删除画布 + /// + /// 画布Guid + /// + public async Task RemoteCanvasAsync(string canvasGuid) + { + var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveCanvas, new + { + canvasGuid + }); + if (result) + { + UIContextOperation?.Invoke(() => + OnCanvasRemove?.Invoke(new CanvasRemoveEventArgs(canvasGuid))); + + } + return true; + } + + /// /// 移动节点,通知远程环境也一起移动,保持相对位置一致 /// /// /// /// - public void MoveNode(string nodeGuid, double x, double y) + public void MoveNode(string cavnasGuid, string nodeGuid, double x, double y) { //UIContextOperation?.Invoke(() => //{ @@ -428,6 +489,7 @@ namespace Serein.NodeFlow.Env _ = msgClient.SendAsync(EnvMsgTheme.MoveNode, new { + cavnasGuid, nodeGuid, x, y @@ -446,16 +508,16 @@ namespace Serein.NodeFlow.Env /// /// 尝试设置为起始节点的节点Guid /// 被设置为起始节点的Guid - public async Task SetStartNodeAsync(string nodeGuid) + public async Task SetStartNodeAsync(string canvasGuid, string nodeGuid) { - var newNodeGuid = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.SetStartNode, new { + canvasGuid, nodeGuid }); if (NodeModels.TryGetValue(newNodeGuid, out var nodeModel)) // 存在节点 { - UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid, newNodeGuid))); + UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(canvasGuid, nodeGuid, newNodeGuid))); } return newNodeGuid; } @@ -469,7 +531,8 @@ namespace Serein.NodeFlow.Env /// 起始节点控制点 /// 目标节点控制点 /// 决定了方法执行后的后继行为 - public async Task ConnectInvokeNodeAsync(string fromNodeGuid, + public async Task ConnectInvokeNodeAsync(string canvasGuid, + string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, @@ -505,6 +568,7 @@ namespace Serein.NodeFlow.Env var sendObj = new { + canvasGuid = canvasGuid, fromNodeGuid = fromNodeGuid, toNodeGuid = toNodeGuid, fromJunctionType = fromNodeJunctionType.ToString(), @@ -514,7 +578,8 @@ namespace Serein.NodeFlow.Env var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.ConnectInvokeNode, sendObj); if (result) { - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(canvasGuid, + fromNodeGuid, toNodeGuid, JunctionOfConnectionType.Invoke, invokeType, @@ -532,7 +597,8 @@ namespace Serein.NodeFlow.Env /// 目标节点控制点 /// 决定了方法参数来源 /// 设置第几个参数 - public async Task ConnectArgSourceNodeAsync(string fromNodeGuid, + public async Task ConnectArgSourceNodeAsync(string canvasGuid, + string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, @@ -576,6 +642,7 @@ namespace Serein.NodeFlow.Env var sendObj = new { + canvasGuid = canvasGuid, fromNodeGuid = fromNodeGuid, toNodeGuid = toNodeGuid, fromJunctionType = fromNodeJunctionType.ToString(), @@ -586,7 +653,8 @@ namespace Serein.NodeFlow.Env var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.ConnectArgSourceNode, sendObj); if (result) { - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(canvasGuid, + fromNodeGuid, toNodeGuid, JunctionOfConnectionType.Arg, argIndex, @@ -615,7 +683,7 @@ namespace Serein.NodeFlow.Env /// 起始节点 /// 目标节点 /// 连接类型 - public async Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType invokeType) + public async Task RemoveConnectInvokeAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, ConnectionInvokeType invokeType) { var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveInvokeConnect, new { @@ -627,7 +695,8 @@ namespace Serein.NodeFlow.Env { UIContextOperation.Invoke(() => { - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(canvasGuid, + fromNodeGuid, toNodeGuid, JunctionOfConnectionType.Invoke, invokeType, @@ -643,7 +712,7 @@ namespace Serein.NodeFlow.Env /// 起始节点Guid /// 目标节点Guid /// 连接到第几个参数 - public async Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex) + public async Task RemoveConnectArgSourceAsync(string canvasGuid, string fromNodeGuid, string toNodeGuid, int argIndex) { var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveArgSourceConnect, new { @@ -655,7 +724,8 @@ namespace Serein.NodeFlow.Env { UIContextOperation.Invoke(() => { - OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid, + OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(canvasGuid, + fromNodeGuid, toNodeGuid, JunctionOfConnectionType.Arg, argIndex, @@ -686,6 +756,7 @@ namespace Serein.NodeFlow.Env #region 尝试从节点信息加载节点 foreach (NodeInfo? nodeInfo in nodeInfos) { + var canvasGuid = nodeInfo.CanvasGuid; if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType)) { continue; @@ -701,11 +772,11 @@ namespace Serein.NodeFlow.Env continue; // 有方法名称,但本地没有缓存的相关方法信息,跳过 } // 加载远程环境时尝试获取方法信息 - newNodeInfo = await CreateNodeAsync(controlType, nodeInfo.Position, methodDetails.ToInfo()); + newNodeInfo = await CreateNodeAsync(canvasGuid, controlType, nodeInfo.Position, methodDetails.ToInfo()); } else { - newNodeInfo = await CreateNodeAsync(controlType, nodeInfo.Position); + newNodeInfo = await CreateNodeAsync(canvasGuid, controlType, nodeInfo.Position); } loadSuuccessNodes.Add(nodeInfo); } @@ -738,8 +809,9 @@ namespace Serein.NodeFlow.Env loadFailureNodes.Clear(); foreach (var nodeInfo in needPlaceNodeInfos) { + var canvasGuid = nodeInfo.CanvasGuid; // 通知远程调整节点放置位置 - var isSuuccess = await PlaceNodeToContainerAsync(nodeInfo.Guid, nodeInfo.ParentNodeGuid); + var isSuuccess = await PlaceNodeToContainerAsync(canvasGuid, nodeInfo.Guid, nodeInfo.ParentNodeGuid); if (isSuuccess) { loadSuuccessNodes.Add(nodeInfo); @@ -765,13 +837,15 @@ namespace Serein.NodeFlow.Env /// 节点/区域/基础控件类型 /// 节点在画布上的位置( /// 节点绑定的方法说明 - public async Task CreateNodeAsync(NodeControlType nodeControlType, - PositionOfUI position, - MethodDetailsInfo methodDetailsInfo = null) + public async Task CreateNodeAsync(string canvasGuid, + NodeControlType nodeControlType, + PositionOfUI position, + MethodDetailsInfo methodDetailsInfo = null) { IsLoadingNode = true; var nodeInfo = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.CreateNode, new { + canvasGuid = canvasGuid, nodeType = nodeControlType.ToString(), position = position, mdInfo = methodDetailsInfo, @@ -792,7 +866,7 @@ namespace Serein.NodeFlow.Env // 通知UI更改 UIContextOperation.Invoke(() => { - OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position)); + OnNodeCreate?.Invoke(new NodeCreateEventArgs(canvasGuid, nodeModel, position)); }); return nodeInfo; } @@ -803,10 +877,11 @@ namespace Serein.NodeFlow.Env /// 将节点放置在容器中 /// /// - public async Task PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid) + public async Task PlaceNodeToContainerAsync(string canvasGuid, string nodeGuid, string containerNodeGuid) { var isSuuccess = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.PlaceNode, new { + canvasGuid = canvasGuid, nodeGuid = nodeGuid, containerNodeGuid = containerNodeGuid, }); @@ -822,7 +897,7 @@ namespace Serein.NodeFlow.Env // 通知UI更改 UIContextOperation.Invoke(() => { - OnNodePlace?.Invoke(new NodePlaceEventArgs(nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置 + OnNodePlace?.Invoke(new NodePlaceEventArgs(canvasGuid, nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置 }); } return result; @@ -834,7 +909,7 @@ namespace Serein.NodeFlow.Env /// 将节点从容器中脱离 /// /// - public async Task TakeOutNodeToContainerAsync(string nodeGuid) + public async Task TakeOutNodeToContainerAsync(string canvasGuid, string nodeGuid) { var isSuuccess = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.TakeOutNode, new { @@ -854,7 +929,7 @@ namespace Serein.NodeFlow.Env // 通知UI更改 UIContextOperation.Invoke(() => { - OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(nodeGuid)); // 重新放置在画布上 + OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(canvasGuid, nodeGuid)); // 重新放置在画布上 }); } return result; @@ -869,7 +944,7 @@ namespace Serein.NodeFlow.Env /// /// /// - public async Task RemoveNodeAsync(string nodeGuid) + public async Task RemoveNodeAsync(string canvasGuid, string nodeGuid) { var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.RemoveNode, new { @@ -879,7 +954,7 @@ namespace Serein.NodeFlow.Env { UIContextOperation.Invoke(() => { - OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid)); + OnNodeRemove?.Invoke(new NodeRemoveEventArgs(canvasGuid, nodeGuid)); }); } else @@ -1077,6 +1152,7 @@ namespace Serein.NodeFlow.Env #region 从NodeInfo创建NodeModel foreach (NodeInfo? nodeInfo in nodeInfos) { + var canvasGuid = nodeInfo.CanvasGuid; if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType)) { continue; @@ -1108,7 +1184,7 @@ namespace Serein.NodeFlow.Env TryAddNode(nodeModel); // 加载项目时将节点加载到环境中 UIContextOperation?.Invoke(() => - OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); // 添加到UI上 + OnNodeCreate?.Invoke(new NodeCreateEventArgs(canvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上 } #endregion @@ -1127,10 +1203,11 @@ namespace Serein.NodeFlow.Env if (NodeModels.TryGetValue(nodeInfo.Guid, out var childNode) && NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode)) { + var canvasGuid = nodeInfo.CanvasGuid; childNode.ContainerNode = parentNode; parentNode.ChildrenNode.Add(childNode); UIContextOperation?.Invoke(() => - OnNodePlace?.Invoke(new NodePlaceEventArgs(nodeInfo.Guid, nodeInfo.ParentNodeGuid)) // 通知UI更改节点放置位置 + OnNodePlace?.Invoke(new NodePlaceEventArgs(canvasGuid,nodeInfo.Guid, nodeInfo.ParentNodeGuid)) // 通知UI更改节点放置位置 ); } @@ -1148,6 +1225,7 @@ namespace Serein.NodeFlow.Env // 不存在对应的起始节点 continue; } + var canvasGuid = nodeInfo.CanvasGuid; List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes), @@ -1164,7 +1242,8 @@ namespace Serein.NodeFlow.Env // 遍历当前类型分支的节点(确认连接关系) foreach (var toNode in item.toNodes) { - UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid, + UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(canvasGuid, + fromNode.Guid, toNode.Guid, JunctionOfConnectionType.Invoke, item.connectionType, @@ -1187,10 +1266,11 @@ namespace Serein.NodeFlow.Env if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid) && NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode)) { - + var canvasGuid = toNode.CanvasGuid; UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke( new NodeConnectChangeEventArgs( + canvasGuid, fromNode.Guid, // 从哪个节点开始 toNode.Guid, // 连接到那个节点 JunctionOfConnectionType.Arg, diff --git a/NodeFlow/Model/SingleGlobalDataNode.cs b/NodeFlow/Model/SingleGlobalDataNode.cs index aff7795..be3a5ba 100644 --- a/NodeFlow/Model/SingleGlobalDataNode.cs +++ b/NodeFlow/Model/SingleGlobalDataNode.cs @@ -96,7 +96,7 @@ namespace Serein.NodeFlow.Model { foreach (var nodeModel in ChildrenNode) { - await nodeModel.Env.TakeOutNodeToContainerAsync(nodeModel.Guid); + await nodeModel.Env.TakeOutNodeToContainerAsync(nodeModel.CanvasGuid, nodeModel.Guid); } DataNode = null; } @@ -174,8 +174,11 @@ namespace Serein.NodeFlow.Model /// public override void Remove() { + if (DataNode is null) { + return; + } // 移除数据节点 - _ = this.Env.RemoveNodeAsync(DataNode?.Guid); + _ = this.Env.RemoveNodeAsync(DataNode.CanvasGuid, DataNode.Guid); } } diff --git a/Workbench/App.xaml b/Workbench/App.xaml index 44bc631..99644e4 100644 --- a/Workbench/App.xaml +++ b/Workbench/App.xaml @@ -3,9 +3,10 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:Serein.Workbench" xmlns:view="clr-namespace:Serein.Workbench.Views" - StartupUri="MainWindow.xaml" + StartupUri="Views/FlowWorkbenchView.xaml" Startup="Application_Startup"> + diff --git a/Workbench/App.xaml.cs b/Workbench/App.xaml.cs index 2b6f435..44d9b68 100644 --- a/Workbench/App.xaml.cs +++ b/Workbench/App.xaml.cs @@ -39,7 +39,7 @@ namespace Serein.Workbench { collection.AddSingleton(); // 流程事件管理 collection.AddSingleton(); // 流程事件管理 - collection.AddSingleton(); // 节点操作管理 + collection.AddSingleton(); // 节点操作管理 // collection.AddSingleton(); // 按键事件管理 //collection.AddSingleton(); // 流程节点控件管理 } @@ -90,19 +90,20 @@ namespace Serein.Workbench public App() { - _ = Task.Run(async () => - { - await Task.Delay(500); - await this.LoadLocalProjectAsync(); - }); - return; + var collection = new ServiceCollection(); collection.AddWorkbenchServices(); collection.AddFlowServices(); collection.AddViewModelServices(); var services = collection.BuildServiceProvider(); // 绑定并返回获取实例的服务接口 App.ServiceProvider = services; - + _ = Task.Run(async () => + { + await Task.Delay(500); + await this.LoadLocalProjectAsync(); + App.GetService().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); + + }); } @@ -124,7 +125,6 @@ namespace Serein.Workbench App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;// var dir = Path.GetDirectoryName(filePath); - //App.GetService().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData },App.FileDataPath); } #endif } diff --git a/Workbench/MainWindow.xaml.cs b/Workbench/MainWindow.xaml.cs index 9c63b62..91a0a20 100644 --- a/Workbench/MainWindow.xaml.cs +++ b/Workbench/MainWindow.xaml.cs @@ -356,7 +356,7 @@ namespace Serein.Workbench projectData.Basic = new Basic { - Canvas = new FlowCanvas + Canvas = new FlowCanvasInfo { Height = FlowChartCanvas.Height, Width = FlowChartCanvas.Width, @@ -763,9 +763,11 @@ namespace Serein.Workbench NodeControls.TryAdd(nodeModel.Guid, nodeControl); // 添加到 if (TryPlaceNodeInRegion(nodeControl, position, out var regionControl)) // 判断添加到区域容器 { + var canvasGuid = nodeControl.ViewModel.NodeModel.CanvasGuid; // 通知运行环境调用加载节点子项的方法 - _ = EnvDecorator.PlaceNodeToContainerAsync(nodeControl.ViewModel.NodeModel.Guid, // 待移动的节点 - regionControl.ViewModel.NodeModel.Guid); // 目标的容器节点 + _ = EnvDecorator.PlaceNodeToContainerAsync(canvasGuid, + nodeControl.ViewModel.NodeModel.Guid, // 待移动的节点 + regionControl.ViewModel.NodeModel.Guid); // 目标的容器节点 } else { @@ -1148,7 +1150,7 @@ namespace Serein.Workbench /// private void ConfigureContextMenu(NodeControlBase nodeControl) { - + var canvasGuid = nodeControl.ViewModel.NodeModel.CanvasGuid; var contextMenu = new ContextMenu(); var nodeGuid = nodeControl.ViewModel?.NodeModel?.Guid; #region 触发器节点 @@ -1187,10 +1189,10 @@ namespace Serein.Workbench - contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => EnvDecorator.SetStartNodeAsync(nodeGuid))); + contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => EnvDecorator.SetStartNodeAsync(canvasGuid, nodeGuid))); contextMenu.Items.Add(CreateMenuItem("删除", async (s, e) => { - var result = await EnvDecorator.RemoveNodeAsync(nodeGuid); + var result = await EnvDecorator.RemoveNodeAsync(canvasGuid, nodeGuid); })); #region 右键菜单功能 - 控件对齐 @@ -1407,7 +1409,8 @@ namespace Serein.Workbench { Task.Run(async () => { - await EnvDecorator.CreateNodeAsync(nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象 + + await EnvDecorator.CreateNodeAsync("MainCanvas", nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象 }); } } @@ -1429,7 +1432,7 @@ namespace Serein.Workbench { Task.Run(async () => { - await EnvDecorator.CreateNodeAsync(nodeControlType, position); // 创建基础节点对象 + await EnvDecorator.CreateNodeAsync("MainCanvas", nodeControlType, position); // 创建基础节点对象 }); } } @@ -1580,7 +1583,7 @@ namespace Serein.Workbench var newLeft = oldLeft + deltaX; var newTop = oldTop + deltaY; - this.EnvDecorator.MoveNode(nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点 + this.EnvDecorator.MoveNode("MainCanvas", nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点 // 计算控件实际移动的距离 var actualDeltaX = newLeft - oldLeft; @@ -1593,7 +1596,7 @@ namespace Serein.Workbench { var otherNewLeft = Canvas.GetLeft(nodeControl) + actualDeltaX; var otherNewTop = Canvas.GetTop(nodeControl) + actualDeltaY; - this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点 + this.EnvDecorator.MoveNode("MainCanvas", nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点 } } @@ -1613,7 +1616,7 @@ namespace Serein.Workbench double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量 double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距 double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距 - this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点 + this.EnvDecorator.MoveNode("MainCanvas", nodeControl.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点 nodeControl.UpdateLocationConnections(); } startControlDragPoint = currentPosition; // 更新起始点位置 @@ -1975,7 +1978,9 @@ namespace Serein.Workbench #region 方法调用关系创建 if (myData.Type == JunctionOfConnectionType.Invoke) { - await EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + await EnvDecorator.ConnectInvokeNodeAsync( + "MainCanvas", + myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, myData.StartJunction.JunctionType, myData.CurrentJunction.JunctionType, myData.ConnectionInvokeType); @@ -1995,7 +2000,9 @@ namespace Serein.Workbench argIndex = argJunction2.ArgIndex; } - await EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, + await EnvDecorator.ConnectArgSourceNodeAsync( + "MainCanvas", + myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid, myData.StartJunction.JunctionType, myData.CurrentJunction.JunctionType, myData.ConnectionArgSourceType, @@ -2060,7 +2067,7 @@ namespace Serein.Workbench var guid = node?.ViewModel?.NodeModel?.Guid; if (!string.IsNullOrEmpty(guid)) { - EnvDecorator.RemoveNodeAsync(guid); + EnvDecorator.RemoveNodeAsync("MainCanvas", guid); } } } diff --git a/Workbench/Models/TabModel.cs b/Workbench/Models/TabModel.cs new file mode 100644 index 0000000..3f67122 --- /dev/null +++ b/Workbench/Models/TabModel.cs @@ -0,0 +1,50 @@ +using CommunityToolkit.Mvvm.ComponentModel; +using Newtonsoft.Json.Linq; +using Serein.Workbench.ViewModels; +using Serein.Workbench.Views; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; +using System.Xml.Linq; + +namespace Serein.Workbench.Models +{ + public partial class FlowCanvasModel : ObservableObject + { + public string Name + { + get + { + + var vm = (FlowCanvasViewModel)content.DataContext; + return vm.Name; + } + set + { + var vm = (FlowCanvasViewModel)content.DataContext; + vm.Name = value; + OnPropertyChanged(nameof(Name)); + } + } + + [ObservableProperty] + private bool _isSelected; + [ObservableProperty] + private bool _isEditing; + [ObservableProperty] + private FlowCanvasView content; + + + public FlowCanvasModel() + { + + } + + } + + +} diff --git a/Workbench/Node/View/ConnectionControl.cs b/Workbench/Node/View/ConnectionControl.cs index e41637d..2d130e2 100644 --- a/Workbench/Node/View/ConnectionControl.cs +++ b/Workbench/Node/View/ConnectionControl.cs @@ -237,13 +237,14 @@ namespace Serein.Workbench.Node.View { Canvas.Children.Remove(BezierLine); var env = Start.MyNode.Env; + var canvasGuid = Start.MyNode.CanvasGuid; if (Start.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Invoke) { - env.RemoveConnectInvokeAsync(Start.MyNode.Guid, End.MyNode.Guid, InvokeType); + env.RemoveConnectInvokeAsync(canvasGuid, Start.MyNode.Guid, End.MyNode.Guid, InvokeType); } else if (Start.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Arg) { - env.RemoveConnectArgSourceAsync(Start.MyNode.Guid, End.MyNode.Guid, ArgIndex) ; + env.RemoveConnectArgSourceAsync(canvasGuid,Start.MyNode.Guid, End.MyNode.Guid, ArgIndex) ; } } diff --git a/Workbench/Services/FlowEEForwardingService.cs b/Workbench/Services/FlowEEForwardingService.cs index 280c218..697bacc 100644 --- a/Workbench/Services/FlowEEForwardingService.cs +++ b/Workbench/Services/FlowEEForwardingService.cs @@ -109,6 +109,14 @@ namespace Serein.Workbench.Services /// 运行环境输出事件 /// public event EnvOutHandler? OnEnvOut; + /// + /// 添加画布事件 + /// + public event CanvasCreateHandler OnCanvasCreate; + /// + /// 移除了画布事件 + /// + public event CanvasRemoveHandler OnCanvasRemove; #endregion @@ -119,6 +127,8 @@ namespace Serein.Workbench.Services flowEnvironmentEvent.OnDllLoad += FlowEnvironment_DllLoadEvent; flowEnvironmentEvent.OnProjectSaving += EnvDecorator_OnProjectSaving; flowEnvironmentEvent.OnProjectLoaded += FlowEnvironment_OnProjectLoaded; + flowEnvironmentEvent.OnCanvasCreate += FlowEnvironmentEvent_OnCanvasCreate; + flowEnvironmentEvent.OnCanvasRemove += FlowEnvironmentEvent_OnCanvasRemove; flowEnvironmentEvent.OnStartNodeChange += FlowEnvironment_StartNodeChangeEvent; flowEnvironmentEvent.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt; flowEnvironmentEvent.OnNodeCreate += FlowEnvironment_NodeCreateEvent; @@ -139,6 +149,7 @@ namespace Serein.Workbench.Services flowEnvironmentEvent.OnEnvOut += FlowEnvironment_OnEnvOutEvent; } + private void ResetFlowEnvironmentEvent() { flowEnvironmentEvent.OnDllLoad -= FlowEnvironment_DllLoadEvent; @@ -224,6 +235,28 @@ namespace Serein.Workbench.Services OnNodeConnectChange?.Invoke(eventArgs); } + + /// + /// 添加了画布 + /// + /// + /// + private void FlowEnvironmentEvent_OnCanvasCreate(CanvasCreateEventArgs eventArgs) + { + OnCanvasCreate?.Invoke(eventArgs); + } + + /// + /// 移除了画布 + /// + /// + /// + private void FlowEnvironmentEvent_OnCanvasRemove(CanvasRemoveEventArgs eventArgs) + { + OnCanvasRemove?.Invoke(eventArgs); + + } + /// /// 节点移除事件 /// diff --git a/Workbench/Services/NodeControlService.cs b/Workbench/Services/NodeControlService.cs new file mode 100644 index 0000000..23064ad --- /dev/null +++ b/Workbench/Services/NodeControlService.cs @@ -0,0 +1,63 @@ + +using Newtonsoft.Json; +using Serein.Library; +using Serein.Library.Api; +using Serein.Library.Utils; +using Serein.NodeFlow; +using Serein.NodeFlow.Env; +using Serein.Workbench.Api; +using Serein.Workbench.Avalonia.Api; +using Serein.Workbench.Node; +using Serein.Workbench.Node.View; +using Serein.Workbench.Node.ViewModel; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using System.Windows.Controls; + + + + + +namespace Serein.Workbench.Api +{ + +} + +namespace Serein.Workbench.Services +{ + /// + /// 节点操作相关服务 + /// + internal class NodeControlService + { + + public NodeControlService(IFlowEnvironment flowEnvironment, + IFlowEEForwardingService feefService) + { + /* this.flowEnvironment = flowEnvironment; + this.feefService = feefService; + feefService.OnNodeCreate += FeefService_OnNodeCreate; // 订阅运行环境创建节点事件 + feefService.OnNodeConnectChange += FeefService_OnNodeConnectChange; // 订阅运行环境连接了节点事件 + // 手动加载项目 + _ = Task.Run(async delegate + { + await Task.Delay(1000); + var flowEnvironment = new FlowEnvironment();// App.GetService(); + var filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\project.dnf"; + string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 + var projectData = JsonConvert.DeserializeObject(content); + var projectDfilePath = System.IO.Path.GetDirectoryName(filePath)!; + flowEnvironment.LoadProject(new FlowEnvInfo { Project = projectData }, projectDfilePath); + }, CancellationToken.None);*/ + } + + + + + + } + +} diff --git a/Workbench/Services/NodeOperationService.cs b/Workbench/Services/NodeOperationService.cs deleted file mode 100644 index 2a9117f..0000000 --- a/Workbench/Services/NodeOperationService.cs +++ /dev/null @@ -1,406 +0,0 @@ - -using Newtonsoft.Json; -using Serein.Library; -using Serein.Library.Api; -using Serein.Library.Utils; -using Serein.NodeFlow; -using Serein.NodeFlow.Env; -using Serein.Workbench.Api; -using Serein.Workbench.Avalonia.Api; -using Serein.Workbench.Node; -using Serein.Workbench.Node.View; -using Serein.Workbench.Node.ViewModel; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Controls; - - - - - -namespace Serein.Workbench.Api -{ - - /// - /// 提供节点操作的接口 - /// - internal interface INodeOperationService - { - /// - /// 连接数据 - /// - // ConnectingManage ConnectingManage { get; } - - /// - /// 主画布 - /// - Canvas MainCanvas { get; set; } - - /// - /// 节点创建事件 - /// - - event NodeViewCreateHandle OnNodeViewCreate; - - /// - /// 创建节点控件 - /// - /// 控件类型 - /// 创建坐标 - /// 节点方法信息 - public void CreateNodeView(MethodDetailsInfo methodDetailsInfo, PositionOfUI position); - - /// - /// 尝试从连接控制点创建连接 - /// - /// - //void TryCreateConnectionOnJunction(NodeJunctionView startJunction); - - } - - - - - #region 事件与事件参数 - /// - /// 创建节点控件事件 - /// - /// - - internal delegate bool NodeViewCreateHandle(NodeViewCreateEventArgs eventArgs); - - /// - /// 创建节点控件事件参数 - /// - - - - internal class NodeViewCreateEventArgs : EventArgs - { - internal NodeViewCreateEventArgs(NodeControlBase nodeControl, PositionOfUI position) - { - this.NodeControl = nodeControl; - this.Position = position; - } - public NodeControlBase NodeControl { get; private set; } - public PositionOfUI Position { get; private set; } - } - - - #endregion - - - - - -} - -namespace Serein.Workbench.Services -{ - /// - /// 节点操作相关服务 - /// - internal class NodeOperationService : INodeOperationService - { - - public NodeOperationService(IFlowEnvironment flowEnvironment, - IFlowEEForwardingService feefService) - { - this.flowEnvironment = flowEnvironment; - this.feefService = feefService; - feefService.OnNodeCreate += FeefService_OnNodeCreate; // 订阅运行环境创建节点事件 - feefService.OnNodeConnectChange += FeefService_OnNodeConnectChange; // 订阅运行环境连接了节点事件 - // 手动加载项目 - _ = Task.Run(async delegate - { - await Task.Delay(1000); - var flowEnvironment = new FlowEnvironment();// App.GetService(); - var filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\project.dnf"; - string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 - var projectData = JsonConvert.DeserializeObject(content); - var projectDfilePath = System.IO.Path.GetDirectoryName(filePath)!; - flowEnvironment.LoadProject(new FlowEnvInfo { Project = projectData }, projectDfilePath); - }, CancellationToken.None); - } - - - #region 接口属性 - //public ConnectingManage ConnectingManage { get; private set; } = new ConnectingManage(); - public Canvas MainCanvas { get; set; } - - #endregion - - #region 私有变量 - - /// - /// 存储所有与节点有关的控件 - /// - private Dictionary NodeControls { get; } = []; - - /// - /// 存储所有连接 - /// - //private List Connections { get; } = []; - - - - /// - /// 流程运行环境 - /// - private readonly IFlowEnvironment flowEnvironment; - - /// - /// 流程运行环境事件转发 - /// - private readonly IFlowEEForwardingService feefService; - #endregion - - #region 节点操作事件 - - /// - /// 创建了节点控件 - /// - public event NodeViewCreateHandle OnNodeViewCreate; - - #endregion - - #region 转发事件的处理 - - /// - /// 从工作台事件转发器监听节点创建事件 - /// - /// - private void FeefService_OnNodeCreate(NodeCreateEventArgs eventArgs) - { - var nodeModel = eventArgs.NodeModel; - if (NodeControls.ContainsKey(nodeModel.Guid)) - { - SereinEnv.WriteLine(InfoType.WARN, $"OnNodeCreate 事件意外触发,节点Guid重复 - {nodeModel.Guid}"); - return; - } - if (!flowEnvironment.NodeMVVMManagement.TryGetType(nodeModel.ControlType, out var nodeMVVM)) - { - SereinEnv.WriteLine(InfoType.INFO, $"无法创建{nodeModel.ControlType}节点,节点类型尚未注册。"); - return; - } - if (nodeMVVM.ControlType == null - || nodeMVVM.ViewModelType == null) - { - SereinEnv.WriteLine(InfoType.INFO, $"无法创建{nodeModel.ControlType}节点,UI类型尚未注册(请通过 NodeMVVMManagement.RegisterUI() 方法进行注册)。"); - return; - } - - var isSuccessful = TryCreateNodeView(nodeMVVM.ControlType, // 控件UI类型 - nodeMVVM.ViewModelType, // 控件VIewModel类型 - nodeModel, // 控件数据实体 - out var nodeControl); // 成功创建后传出的节点控件实体 - if (!isSuccessful || nodeControl is null) - { - SereinEnv.WriteLine(InfoType.INFO, $"无法创建{nodeModel.ControlType}节点,节点创建失败。"); - return; - } - - - var e = new NodeViewCreateEventArgs(nodeControl, eventArgs.Position); - if (OnNodeViewCreate?.Invoke(e) == true) - { - // 成功创建 - NodeControls.TryAdd(nodeModel.Guid, nodeControl); // 缓存起来,通知其它地方拿取这个控件 - } - - } - - - /// - /// 运行环境连接了节点事件 - /// - /// - /// - private void FeefService_OnNodeConnectChange(NodeConnectChangeEventArgs eventArgs) - { - string fromNodeGuid = eventArgs.FromNodeGuid; - string toNodeGuid = eventArgs.ToNodeGuid; - if (!TryGetControl(fromNodeGuid, out var fromNodeControl) - || !TryGetControl(toNodeGuid, out var toNodeControl)) - { - return; - } - - - if (eventArgs.JunctionOfConnectionType == JunctionOfConnectionType.Invoke) - { - ConnectionInvokeType connectionType = eventArgs.ConnectionInvokeType; - #region 创建/删除节点之间的调用关系 - #region 创建连接 - if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接 - { - if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction) - { - SereinEnv.WriteLine(InfoType.INFO, "非预期的连接"); - return; - } - var startJunction = IFormJunction.NextStepJunction; - var endJunction = IToJunction.ExecuteJunction; - - // NodeConnectionLineControl nodeConnectionLineControl = new NodeConnectionLineControl(MainCanvas, startJunction, endJunction); - - //startJunction.TransformToVisual(MainCanvas); - - //// 添加连接 - //var shape = new ConnectionLineShape( - // FlowChartCanvas, - // connectionType, - // startJunction, - // endJunction - //); - - - //NodeConnectionLine nodeConnectionLine = new NodeConnectionLine(MainCanvas, shape); - - //if (toNodeControl is FlipflopNodeControl flipflopControl - // && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器 - //{ - // NodeTreeViewer.RemoveGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除 - //} - - //Connections.Add(nodeConnectionLineControl); - //fromNodeControl.AddConnection(nodeConnectionLineControl); - //toNodeControl.AddConnection(nodeConnectionLineControl); - } - #endregion - - - #region 移除连接 - /* else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remove) // 移除连接 - { - // 需要移除连接 - var removeConnections = Connections.Where(c => - c.Start.MyNode.Guid.Equals(fromNodeGuid) - && c.End.MyNode.Guid.Equals(toNodeGuid) - && (c.Start.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Invoke - || c.End.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Invoke)) - .ToList(); - - - foreach (var connection in removeConnections) - { - Connections.Remove(connection); - fromNodeControl.RemoveConnection(connection); // 移除连接 - toNodeControl.RemoveConnection(connection); // 移除连接 - if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control)) - { - JudgmentFlipFlopNode(control); // 连接关系变更时判断 - } - } - }*/ - #endregion - - - #endregion - } - - } - #endregion - - #region 私有方法 - - /// - /// 创建节点控件 - /// - /// 节点控件视图控件类型 - /// 节点控件ViewModel类型 - /// 节点Model实例 - /// 返回的节点对象 - /// 是否创建成功 - /// 无法创建节点控件 - private bool TryCreateNodeView(Type viewType, Type viewModelType, NodeModelBase nodeModel, out NodeControlBase? nodeView) - { - if (string.IsNullOrEmpty(nodeModel.Guid)) - { - nodeModel.Guid = Guid.NewGuid().ToString(); - } - var t_ViewModel = Activator.CreateInstance(viewModelType, nodeModel); - if (t_ViewModel is not NodeControlViewModelBase viewModelBase) - { - nodeView = null; - return false; - } - var controlObj = Activator.CreateInstance(viewType); - if (controlObj is NodeControlBase nodeControl) - { - nodeControl.DataContext = viewModelBase; - nodeView = nodeControl; - return true; - } - else - { - nodeView = null; - return false; - } - - // 在其它地方验证过了,所以注释 - //if ((viewType is null) - // || viewModelType is null - // || nodeModel is null) - //{ - // nodeView = null; - // return false; - //} - //if (typeof(INodeControl).IsSubclassOf(viewType) - // || typeof(NodeViewModelBase).IsSubclassOf(viewModelType)) - //{ - // nodeView = null; - // return false; - //} - } - - private bool TryGetControl(string nodeGuid, out NodeControlBase nodeControl) - { - if (string.IsNullOrEmpty(nodeGuid)) - { - nodeControl = null; - return false; - } - if (!NodeControls.TryGetValue(nodeGuid, out nodeControl)) - { - nodeControl = null; - return false; - } - if (nodeControl is null) - { - return false; - } - return true; - } - - #endregion - - #region 操作接口对外暴露的接口 - - /// - /// 创建节点控件 - /// - /// 控件类型 - /// 创建坐标 - /// 节点方法信息(基础节点传null) - public void CreateNodeView(MethodDetailsInfo methodDetailsInfo, PositionOfUI position) - { - Task.Run(async () => - { - if (EnumHelper.TryConvertEnum(methodDetailsInfo.NodeType, out var nodeType)) - { - await flowEnvironment.CreateNodeAsync(nodeType, position, methodDetailsInfo); - } - }); - } - - - - - #endregion - } - -} diff --git a/Workbench/Tool/Converters/BoolToVisibilityConverter.cs b/Workbench/Tool/Converters/BoolToVisibilityConverter.cs new file mode 100644 index 0000000..ab07dc0 --- /dev/null +++ b/Workbench/Tool/Converters/BoolToVisibilityConverter.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Data; +using System.Windows; + +namespace Serein.Workbench.Tool.Converters +{ + public class BoolToVisibilityConverter : IValueConverter + { + public object Convert(object value, Type targetType, object parameter, CultureInfo culture) + { + if (value is bool b) + { + return b ? Visibility.Visible : Visibility.Collapsed; + } + return Visibility.Collapsed; + } + + public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) + { + return value is Visibility v && v == Visibility.Visible; + } + } +} diff --git a/Workbench/ViewModels/FlowCanvasViewModel.cs b/Workbench/ViewModels/FlowCanvasViewModel.cs index 11058ad..df16e0a 100644 --- a/Workbench/ViewModels/FlowCanvasViewModel.cs +++ b/Workbench/ViewModels/FlowCanvasViewModel.cs @@ -9,11 +9,35 @@ namespace Serein.Workbench.ViewModels { public partial class FlowCanvasViewModel : ObservableObject { - + /// + /// 正在创建节点方法调用关系 + /// [ObservableProperty] private bool _isConnectionInvokeNode; + /// + /// 正在创建节点参数连接关系 + /// [ObservableProperty] private bool _isConnectionArgSourceNode; + + /// + /// 画布显示名称 + /// + [ObservableProperty] + private string _name; + + /// + /// 画布ID + /// + [ObservableProperty] + private string _canvasGuid; + + + + public FlowCanvasViewModel() + { + + } } } diff --git a/Workbench/ViewModels/FlowEditViewModel.cs b/Workbench/ViewModels/FlowEditViewModel.cs index 40f0cb3..24b0609 100644 --- a/Workbench/ViewModels/FlowEditViewModel.cs +++ b/Workbench/ViewModels/FlowEditViewModel.cs @@ -1,9 +1,15 @@ using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; +using Serein.Workbench.Models; +using Serein.Workbench.Views; using System; using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows.Input; namespace Serein.Workbench.ViewModels { @@ -12,5 +18,80 @@ namespace Serein.Workbench.ViewModels /// public partial class FlowEditViewModel : ObservableObject { + public ObservableCollection Tabs { get; set; } + public ICommand AddTabCommand { get; set; } + public ICommand RemoveTabCommand { get; set; } + public ICommand RenameTabCommand { get; set; } + + [ObservableProperty] + private FlowCanvasModel _selectedTab; + + private int _addCount = 0; + + public FlowEditViewModel() + { + Tabs = new ObservableCollection(); + AddTabCommand = new RelayCommand(AddTab); + RemoveTabCommand = new RelayCommand(RemoveTab, CanRemoveTab); + + // 初始化时添加一个默认的Tab + AddTab(); // 添加一个默认选项卡 + } + + private void AddTab() + { + var flowCanvasView = new FlowCanvasView(); // 创建FlowCanvasView实例 + Tabs.Add(new FlowCanvasModel { Content = flowCanvasView ,Name = $"New Tab {_addCount++}"}); + SelectedTab = Tabs[Tabs.Count - 1]; // 选择刚添加的Tab + } + + private void RemoveTab() + { + if (Tabs.Count > 0 && SelectedTab != null) + { + Tabs.Remove(SelectedTab); + SelectedTab = Tabs.Count > 0 ? Tabs[Tabs.Count - 1] : null; + } + } + + private bool CanRemoveTab() + { + return SelectedTab != null; + } + + + + /// + /// 进入编辑模式 + /// + /// + public void StartEditingTab(FlowCanvasModel tab) + { + if (tab != null) + { + tab.IsEditing = true; + OnPropertyChanged(nameof(Tabs)); // 刷新Tabs集合,以便更新UI + } + } + + /// + /// 结束编辑,重命名 + /// + /// + /// + public void EndEditingTab(FlowCanvasModel tab, string newName) + { + if (tab != null) + { + tab.IsEditing = false; + tab.Name = newName; // 设置新名称 + OnPropertyChanged(nameof(Tabs)); // 刷新Tabs集合 + } + } + + + + + } } diff --git a/Workbench/Views/FlowCanvasView.xaml b/Workbench/Views/FlowCanvasView.xaml index cd975dc..152f67a 100644 --- a/Workbench/Views/FlowCanvasView.xaml +++ b/Workbench/Views/FlowCanvasView.xaml @@ -15,17 +15,14 @@ - - - - - + d:DataContext="{d:DesignInstance vm:FlowEditViewModel}" + xmlns:converters="clr-namespace:Serein.Workbench.Tool.Converters" + Background="#E7F0F6"> + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +