diff --git a/.claudiaideconfig b/.claudiaideconfig
new file mode 100644
index 0000000..4c84480
--- /dev/null
+++ b/.claudiaideconfig
@@ -0,0 +1,49 @@
+{
+ "BackgroundImageAbsolutePath": "C:\\Users\\Az\\Pictures\\anime_girl_brunette1.png",
+ "BackgroundImagesDirectoryAbsolutePath": "c:\\users\\az\\appdata\\local\\microsoft\\visualstudio\\17.0_a4315dfe\\extensions\\mtri5ge5.xs4\\Images",
+ "BlurMethod": 0,
+ "BlurRadius": 0,
+ "EditorBackgroundColor": "",
+ "EditorBackgroundColorObject": null,
+ "ExpandToIDE": false,
+ "Extensions": ".png, .jpg, .gif, .bmp",
+ "ImageBackgroundType": 0,
+ "ImageFadeAnimationInterval": "PT5S",
+ "ImageStretch": 0,
+ "IncludeSubdirectories": false,
+ "IsLimitToMainlyEditorWindow": false,
+ "IsTransparentToContentMargin": false,
+ "IsTransparentToStickyScroll": false,
+ "LoopSlideshow": true,
+ "MaxHeight": 0,
+ "MaxWidth": 0,
+ "Opacity": 0.1,
+ "PositionHorizon": 1,
+ "PositionVertical": 1,
+ "ShuffleSlideshow": false,
+ "SoftEdgeX": 0,
+ "SoftEdgeY": 0,
+ "StickyScrollColor": "#00000000",
+ "StickyScrollColorObject": {
+ "A": 0,
+ "B": 0,
+ "G": 0,
+ "R": 0,
+ "ScA": 0,
+ "ScB": 0,
+ "ScG": 0,
+ "ScR": 0
+ },
+ "TileMode": 0,
+ "UpdateImageInterval": "PT1M",
+ "ViewBoxPointX": 0,
+ "ViewBoxPointY": 0,
+ "ViewPortHeight": 1,
+ "ViewPortPointX": 0,
+ "ViewPortPointY": 0,
+ "ViewPortWidth": 1,
+ "WebApiDownloadInterval": "PT5M",
+ "WebApiEndpoint": "",
+ "WebApiJsonKey": "",
+ "WebSingleUrl": ""
+}
\ No newline at end of file
diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs
index 872601e..75e6bb6 100644
--- a/Library/Api/IFlowEnvironment.cs
+++ b/Library/Api/IFlowEnvironment.cs
@@ -146,11 +146,11 @@ namespace Serein.Library.Api
///
/// 是否完成
///
- public bool IsSucceed { get; protected set; } = true;
+ public bool IsSucceed { get;} = true;
///
/// 错误提示
///
- public string ErrorTips { get; protected set; } = string.Empty;
+ public string ErrorTips { get;} = string.Empty;
}
///
@@ -168,9 +168,15 @@ namespace Serein.Library.Api
///
public class ProjectSavingEventArgs : FlowEventArgs
{
- public ProjectSavingEventArgs()
+ public ProjectSavingEventArgs(SereinProjectData projectData)
{
+ ProjectData = projectData;
}
+
+ ///
+ /// 项目数据
+ ///
+ public SereinProjectData ProjectData { get; }
}
///
@@ -186,11 +192,11 @@ namespace Serein.Library.Api
///
/// 已加载了的程序集
///
- public NodeLibraryInfo NodeLibraryInfo { get; protected set; }
+ public NodeLibraryInfo NodeLibraryInfo { get;}
///
/// dll文件中有效的流程方法描述
///
- public List MethodDetailss { get; protected set; }
+ public List MethodDetailss { get;}
}
///
@@ -279,31 +285,31 @@ namespace Serein.Library.Api
///
/// 连接关系中始节点的Guid
///
- public string FromNodeGuid { get; protected set; }
+ public string FromNodeGuid { get;}
///
/// 连接关系中目标节点的Guid
///
- public string ToNodeGuid { get; protected set; }
+ public string ToNodeGuid { get;}
///
/// 连接类型
///
- public ConnectionInvokeType ConnectionInvokeType { get; protected set; }
+ public ConnectionInvokeType ConnectionInvokeType { get;}
///
/// 表示此次需要在两个节点之间创建连接关系,或是移除连接关系
///
- public ConnectChangeType ChangeType { get; protected set; }
+ public ConnectChangeType ChangeType { get;}
///
/// 指示需要创建什么类型的连接线
///
- public JunctionOfConnectionType JunctionOfConnectionType { get; protected set; }
+ public JunctionOfConnectionType JunctionOfConnectionType { get;}
///
/// 节点对应的方法入参所需参数来源
///
- public ConnectionArgSourceType ConnectionArgSourceType { get; protected set; }
+ public ConnectionArgSourceType ConnectionArgSourceType { get;}
///
/// 第几个参数
///
- public int ArgIndex { get; protected set; }
+ public int ArgIndex { get;}
}
@@ -313,12 +319,12 @@ namespace Serein.Library.Api
///
public class CanvasCreateEventArgs : FlowEventArgs
{
- public CanvasCreateEventArgs(FlowCanvasModel model)
+ public CanvasCreateEventArgs(FlowCanvasDetails model)
{
Model = model;
}
- public FlowCanvasModel Model { get; }
+ public FlowCanvasDetails Model { get; }
}
///
@@ -342,6 +348,7 @@ namespace Serein.Library.Api
///
/// 节点添加事件参数
///
+ /// 画布
/// 节点对象
/// 位置
public NodeCreateEventArgs(string canvasGuid, NodeModelBase nodeModel, PositionOfUI position)
@@ -350,15 +357,23 @@ namespace Serein.Library.Api
this.NodeModel = nodeModel;
this.Position = position;
}
-
+ ///
+ /// 所处画布Guid
+ ///
public string CanvasGuid { get; }
///
/// 节点Model对象
///
public NodeModelBase NodeModel { get; private set; }
+ ///
+ /// 在UI上的位置
+ ///
public PositionOfUI Position { get; private set; }
- public string RegeionGuid { get; private set; }
+ ///
+ /// 容器
+ ///
+ //public string RegeionGuid { get; private set; }
}
///
@@ -487,16 +502,16 @@ namespace Serein.Library.Api
///
/// 中断的节点Guid
///
- public string NodeGuid { get; protected set; }
+ public string NodeGuid { get;}
///
/// 监听对象类别
///
- public ObjSourceType ObjSource { get; protected set; }
+ public ObjSourceType ObjSource { get;}
///
/// 新的数据
///
- public object NewData { get; protected set; }
+ public object NewData { get;}
}
///
@@ -514,9 +529,9 @@ namespace Serein.Library.Api
///
/// 中断的节点Guid
///
- public string NodeGuid { get; protected set; }
- public bool IsInterrupt { get; protected set; }
- // public InterruptClass Class { get; protected set; }
+ public string NodeGuid { get;}
+ public bool IsInterrupt { get;}
+ // public InterruptClass Class { get;}
}
///
/// 节点触发了中断事件参数
@@ -549,9 +564,9 @@ namespace Serein.Library.Api
///
/// 中断的节点Guid
///
- public string NodeGuid { get; protected set; }
- public string Expression { get; protected set; }
- public InterruptTriggerType Type { get; protected set; }
+ public string NodeGuid { get;}
+ public string Expression { get;}
+ public InterruptTriggerType Type { get;}
}
@@ -880,7 +895,7 @@ namespace Serein.Library.Api
/// 宽度
/// 高度
///
- Task CreateCanvasAsync(string canvasName, int width , int height);
+ Task CreateCanvasAsync(string canvasName, int width , int height);
///
/// 删除画布
diff --git a/Library/Extension/FlowModelExtension.cs b/Library/Extension/FlowModelExtension.cs
index 9e6e652..df53097 100644
--- a/Library/Extension/FlowModelExtension.cs
+++ b/Library/Extension/FlowModelExtension.cs
@@ -21,9 +21,9 @@ namespace Serein.Library
///
///
///
- public static FlowCanvasInfo ToInfo(this FlowCanvasModel model)
+ public static FlowCanvasDetailsInfo ToInfo(this FlowCanvasDetails model)
{
- return new FlowCanvasInfo
+ return new FlowCanvasDetailsInfo
{
Guid = model.Guid,
Height = model.Height,
@@ -33,6 +33,7 @@ namespace Serein.Library
ScaleY = model.ScaleY,
ViewX = model.ViewX,
ViewY = model.ViewY,
+ StartNode = model.StartNode,
};
}
@@ -41,7 +42,7 @@ namespace Serein.Library
///
///
///
- public static void LoadInfo(this FlowCanvasModel model, FlowCanvasInfo info)
+ public static void LoadInfo(this FlowCanvasDetails model, FlowCanvasDetailsInfo info)
{
model.Guid = info.Guid;
model.Height = info.Height;
@@ -51,6 +52,7 @@ namespace Serein.Library
model.ScaleY = info.ScaleY;
model.ViewX = info.ViewX;
model.ViewY = info.ViewY;
+ model.StartNode = info.StartNode;
}
///
@@ -102,6 +104,7 @@ namespace Serein.Library
NodeInfo nodeInfo = new NodeInfo
{
Guid = nodeModel.Guid,
+ CanvasGuid = nodeModel.CanvasGuid,
AssemblyName = nodeModel.MethodDetails.AssemblyName,
MethodName = nodeModel.MethodDetails?.MethodName,
Label = nodeModel.MethodDetails?.MethodAnotherName,
diff --git a/Library/FlowNode/FlowCanvasModel.cs b/Library/FlowNode/FlowCanvasDetails.cs
similarity index 51%
rename from Library/FlowNode/FlowCanvasModel.cs
rename to Library/FlowNode/FlowCanvasDetails.cs
index 98d05e6..4f56a64 100644
--- a/Library/FlowNode/FlowCanvasModel.cs
+++ b/Library/FlowNode/FlowCanvasDetails.cs
@@ -1,4 +1,5 @@
using Serein.Library.Api;
+using Serein.Library.FlowNode;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,50 +9,86 @@ using System.Threading.Tasks;
namespace Serein.Library
{
+
+
+
+ ///
+ /// 流程画布
+ ///
[NodeProperty(ValuePath = NodeValuePath.Node)]
- public partial class FlowCanvasModel
+ public partial class FlowCanvasDetails
{
- public FlowCanvasModel(IFlowEnvironment env)
+ public FlowCanvasDetails(IFlowEnvironment env)
{
Env = env;
}
+
public IFlowEnvironment Env { get; }
+ ///
+ /// 标识画布ID
+ ///
[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]
+ [PropertyInfo(IsNotification = true)]
private double _viewX ;
///
/// 预览位置Y
///
- [PropertyInfo]
+ [PropertyInfo(IsNotification = true)]
private double _viewY ;
///
/// 缩放比例X
///
- [PropertyInfo]
- private double _scaleX ;
+ [PropertyInfo(IsNotification = true)]
+ private double _scaleX = 1;
///
/// 缩放比例Y
///
- [PropertyInfo]
- private double _scaleY ;
+ [PropertyInfo(IsNotification = true)]
+ private double _scaleY = 1;
+
+
+ ///
+ /// 起始节点私有属性
+ ///
+ private string _startNode;
+
+
}
+
+
+ public partial class FlowCanvasDetails
+ {
+
+
+ }
+
+
}
diff --git a/Library/FlowNode/FlowCanvasDetailsInfo.cs b/Library/FlowNode/FlowCanvasDetailsInfo.cs
new file mode 100644
index 0000000..ee55778
--- /dev/null
+++ b/Library/FlowNode/FlowCanvasDetailsInfo.cs
@@ -0,0 +1,60 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Library
+{
+
+ ///
+ /// 画布信息
+ ///
+ public class FlowCanvasDetailsInfo
+ {
+ ///
+ /// 起始节点Guid
+ ///
+ public string StartNode;
+
+ ///
+ /// 标识画布ID
+ ///
+ public string Guid;
+
+ ///
+ /// 画布名称
+ ///
+ public string Name;
+
+ ///
+ /// 画布宽度
+ ///
+ public double Width;
+
+ ///
+ /// 画布高度
+ ///
+ public double Height;
+
+ ///
+ /// 预览位置X
+ ///
+ public double ViewX;
+
+ ///
+ /// 预览位置Y
+ ///
+ public double ViewY;
+
+ ///
+ /// 缩放比例X
+ ///
+ public double ScaleX;
+
+ ///
+ /// 缩放比例Y
+ ///
+ public double ScaleY;
+ }
+}
diff --git a/Library/FlowNode/SereinProjectData.cs b/Library/FlowNode/SereinProjectData.cs
index 778fd3b..8ccc9fa 100644
--- a/Library/FlowNode/SereinProjectData.cs
+++ b/Library/FlowNode/SereinProjectData.cs
@@ -48,7 +48,7 @@ namespace Serein.Library
///
- /// 项目保存文件
+ /// 项目数据
///
public class SereinProjectData
{
@@ -64,16 +64,20 @@ namespace Serein.Library
public NodeLibraryInfo[] Librarys { get; set; }
- ///
- /// 起始节点GUID
- ///
+ /////
+ ///// 起始节点GUID
+ /////
- public string StartNode { get; set; }
+ //public string StartNode { get; set; }
+
+ ///
+ /// 画布集合
+ ///
+ public FlowCanvasDetailsInfo[] Canvass { get; set; }
///
/// 节点集合
///
-
public NodeInfo[] Nodes { get; set; }
}
@@ -83,11 +87,10 @@ namespace Serein.Library
///
public class Basic
{
- ///
- /// 画布
- ///
-
- public FlowCanvasInfo Canvas { get; set; }
+ /////
+ ///// 画布
+ /////
+ //public FlowCanvasInfo Canvas { get; set; }
///
/// 版本
@@ -95,7 +98,9 @@ namespace Serein.Library
public string Versions { get; set; }
}
- ///
+
+
+ /* ///
/// 画布信息,项目文件相关
///
public class FlowCanvasInfo
@@ -132,7 +137,7 @@ namespace Serein.Library
/// 缩放比例Y
///
public double ScaleY { get; set; }
- }
+ }*/
///
/// 项目依赖的程序集,项目文件相关
@@ -187,7 +192,6 @@ namespace Serein.Library
///
public string CanvasGuid { get; set; }
-
///
/// 节点的GUID
///
diff --git a/Library/Utils/UIContextOperation.cs b/Library/Utils/UIContextOperation.cs
index 53d4b28..d377792 100644
--- a/Library/Utils/UIContextOperation.cs
+++ b/Library/Utils/UIContextOperation.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
@@ -91,6 +92,7 @@ namespace Serein.Library.Utils
catch (Exception ex)
{
tcs.SetException(ex);
+ Debug.WriteLine(ex);
}
}, null);
diff --git a/NodeFlow/Env/EnvMsgTheme.cs b/NodeFlow/Env/EnvMsgTheme.cs
index b69cc04..146645f 100644
--- a/NodeFlow/Env/EnvMsgTheme.cs
+++ b/NodeFlow/Env/EnvMsgTheme.cs
@@ -5,6 +5,10 @@
///
public static class EnvMsgTheme
{
+ ///
+ /// 尝试保存项目
+ ///
+ public const string SaveProject = nameof(SaveProject);
///
/// 获取远程环境信息
///
diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs
index 7caa234..79265f3 100644
--- a/NodeFlow/Env/FlowEnvironment.cs
+++ b/NodeFlow/Env/FlowEnvironment.cs
@@ -301,7 +301,7 @@ namespace Serein.NodeFlow.Env
///
/// 运行环境加载的画布集合
///
- private Dictionary FlowCanvass { get; } = [];
+ private Dictionary FlowCanvass { get; } = [];
///
/// 存放触发器节点(运行时全部调用)
@@ -309,35 +309,35 @@ namespace Serein.NodeFlow.Env
private List FlipflopNodes { get; } = [];
+ /*
+ ///
+ /// 起始节点私有属性
+ ///
+ private NodeModelBase? _startNode = null;
- ///
- /// 起始节点私有属性
- ///
- private NodeModelBase? _startNode = null;
-
- ///
- /// 起始节点
- ///
- private NodeModelBase? StartNode
- {
- get
- {
- return _startNode;
- }
- set
- {
- if (value is null)
- {
- return;
- }
- if (_startNode is not null)
- {
- _startNode.IsStart = false;
- }
- value.IsStart = true;
- _startNode = value;
- }
- }
+ ///
+ /// 起始节点
+ ///
+ private NodeModelBase? StartNode
+ {
+ get
+ {
+ return _startNode;
+ }
+ set
+ {
+ if (value is null)
+ {
+ return;
+ }
+ if (_startNode is not null)
+ {
+ _startNode.IsStart = false;
+ }
+ value.IsStart = true;
+ _startNode = value;
+ }
+ }*/
///
/// 流程任务管理
@@ -373,12 +373,11 @@ namespace Serein.NodeFlow.Env
IOC.Reset();
IOC.Register(); // 注册脚本接口
- var flowTaskOptions = new FlowWorkLibrary
+ var flowTaskOptions = new FlowWorkOptions
{
-
Environment = this,
FlowContextPool = new ObjectPool(() => new DynamicContext(this)),
- Nodes = NodeModels.Values.ToList(),
+ //Nodes = NodeModels.Values.ToList(),
AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(),
InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init),
LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading),
@@ -534,7 +533,8 @@ namespace Serein.NodeFlow.Env
///
public void SaveProject()
{
- OnProjectSaving?.Invoke(new ProjectSavingEventArgs());
+ var project = GetProjectInfoAsync().GetAwaiter().GetResult();
+ OnProjectSaving?.Invoke(new ProjectSavingEventArgs(project));
}
///
@@ -562,7 +562,7 @@ namespace Serein.NodeFlow.Env
_ = Task.Run( async () =>
{
await LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息
- await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点
+ //await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点
});
}
@@ -615,16 +615,17 @@ namespace Serein.NodeFlow.Env
/// 序列化当前项目的依赖信息、节点信息
///
///
- public Task GetProjectInfoAsync()
+ public async Task GetProjectInfoAsync()
{
var projectData = new SereinProjectData()
{
Librarys = this.FlowLibraryManagement.GetAllLibraryInfo().ToArray(),
Nodes = NodeModels.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
- StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid,
+ Canvass = FlowCanvass.Values.Select(canvas => canvas.ToInfo()).ToArray(),
+ //StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid,
};
- return Task.FromResult(projectData);
+ return projectData;
}
@@ -776,22 +777,22 @@ namespace Serein.NodeFlow.Env
/// 宽度
/// 高度
///
- public Task CreateCanvasAsync(string canvasName, int width, int height)
+ public async Task CreateCanvasAsync(string canvasName, int width, int height)
{
- var model = new FlowCanvasModel(this)
+ var model = new FlowCanvasDetails(this)
{
Guid = Guid.NewGuid().ToString(),
Height = height,
- Name = !string.IsNullOrWhiteSpace(canvasName) ? canvasName : $"流程图 {_addCanvasCount++}",
- Width = height,
+ Width = width,
+ Name = !string.IsNullOrWhiteSpace(canvasName) ? canvasName : $"流程图{_addCanvasCount++}",
};
FlowCanvass.Add(model.Guid, model);
- UIContextOperation.Invoke(() =>
+ await UIContextOperation.InvokeAsync(() =>
{
OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model));
});
var info = model.ToInfo();
- return Task.FromResult(info);
+ return info;
}
///
@@ -799,15 +800,29 @@ namespace Serein.NodeFlow.Env
///
/// 画布Guid
///
- public Task RemoveCanvasAsync(string canvasGuid)
+ public async Task RemoveCanvasAsync(string canvasGuid)
{
if (!FlowCanvass.TryGetValue(canvasGuid, out var model))
{
- return Task.FromResult(false);
+ return false;
}
var count = NodeModels.Values.Count(node => node.CanvasGuid.Equals(canvasGuid));
- return Task.FromResult(count == 0);
+ if(count > 0)
+ {
+ SereinEnv.WriteLine(InfoType.WARN, "无法删除具有节点的画布");
+ return false;
+ }
+ if (FlowCanvass.Remove(canvasGuid))
+ {
+ await UIContextOperation.InvokeAsync(() =>
+ {
+ OnCanvasRemove.Invoke(new CanvasRemoveEventArgs(canvasGuid));
+ });
+ return true;
+ }
+
+ return false;
}
@@ -981,7 +996,7 @@ namespace Serein.NodeFlow.Env
PositionOfUI position,
MethodDetailsInfo? methodDetailsInfo = null)
{
- if (!FlowCanvass.ContainsKey(canvasGuid))
+ if (!TryGetCanvasModel(canvasGuid,out var cavnasModel))
{
return Task.FromResult(null);
}
@@ -1005,16 +1020,17 @@ namespace Serein.NodeFlow.Env
}
TryAddNode(nodeModel);
- nodeModel.Position = position;
+ nodeModel.CanvasGuid = canvasGuid; // 设置所属于的画布
+ nodeModel.Position = position; // 设置位置
// 通知UI更改
UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(canvasGuid, nodeModel, position)));
// 因为需要UI先布置了元素,才能通知UI变更特效
// 如果不存在流程起始控件,默认设置为流程起始控件
- if (StartNode is null)
+ if (cavnasModel.StartNode is null)
{
- SetStartNode(canvasGuid, nodeModel);
+ SetStartNode(cavnasModel, nodeModel);
}
var nodeInfo = nodeModel.ToInfo();
return Task.FromResult(nodeInfo);
@@ -1392,15 +1408,16 @@ namespace Serein.NodeFlow.Env
///
/// 设置起点控件
///
- ///
+ /// 画布
+ /// 节点Guid
public Task SetStartNodeAsync(string canvasGuid, string newNodeGuid)
{
- if (!FlowCanvass.ContainsKey(canvasGuid) || !TryGetNodeModel(newNodeGuid, out var newStartNodeModel))
+ if (!TryGetCanvasModel(canvasGuid, out var canvasModel) || !TryGetNodeModel(newNodeGuid, out var newStartNodeModel))
{
- return Task.FromResult(StartNode?.Guid ?? string.Empty);
+ return Task.FromResult(canvasModel.StartNode ?? string.Empty);
}
- SetStartNode(canvasGuid, newStartNodeModel);
- return Task.FromResult(StartNode?.Guid ?? string.Empty);
+ SetStartNode(canvasModel, newStartNodeModel);
+ return Task.FromResult(canvasModel.StartNode ?? string.Empty);
}
///
@@ -1512,9 +1529,25 @@ namespace Serein.NodeFlow.Env
}
+ ///
+ /// 从Guid获取画布
+ ///
+ /// 节点Guid
+ /// 节点Model
+ /// 无法获取节点、Guid/节点为null时报错
+ public bool TryGetCanvasModel(string nodeGuid, out FlowCanvasDetails canvasDetails)
+ {
+ if (string.IsNullOrEmpty(nodeGuid))
+ {
+ canvasDetails = null;
+ return false;
+ }
+ return FlowCanvass.TryGetValue(nodeGuid, out canvasDetails) && canvasDetails is not null;
+
+ }
///
- /// Guid 转 NodeModel
+ /// 从Guid获取节点
///
/// 节点Guid
/// 节点Model
@@ -1913,15 +1946,11 @@ namespace Serein.NodeFlow.Env
///
///
///
- private void SetStartNode(string canvasGuid, NodeModelBase newStartNode)
+ private void SetStartNode(FlowCanvasDetails cavnasModel, NodeModelBase newStartNode)
{
- if (!FlowCanvass.ContainsKey(canvasGuid))
- {
- return;
- }
- var oldNodeGuid = StartNode?.Guid;
- StartNode = newStartNode;
- UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(canvasGuid, oldNodeGuid, StartNode.Guid)));
+ var oldNodeGuid = cavnasModel.StartNode;
+ cavnasModel.StartNode = newStartNode.Guid;
+ UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode)));
//if (OperatingSystem.IsWindows())
//{
diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs
index 8d310a0..5456d7c 100644
--- a/NodeFlow/Env/FlowEnvironmentDecorator.cs
+++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs
@@ -238,7 +238,7 @@ namespace Serein.NodeFlow.Env
/// 宽度
/// 高度
///
- public async Task CreateCanvasAsync(string canvasName, int width, int height)
+ public async Task CreateCanvasAsync(string canvasName, int width, int height)
{
return await currentFlowEnvironment.CreateCanvasAsync(canvasName, width, height);
}
diff --git a/NodeFlow/Env/MsgControllerOfClient.cs b/NodeFlow/Env/MsgControllerOfClient.cs
index 767cd8c..030136c 100644
--- a/NodeFlow/Env/MsgControllerOfClient.cs
+++ b/NodeFlow/Env/MsgControllerOfClient.cs
@@ -164,7 +164,7 @@ namespace Serein.NodeFlow.Env
}
[AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateCanvas, IsReturnValue = false)]
- public void CreateCanvas([UseMsgId] string msgId, [UseData] FlowCanvasInfo canvasInfo)
+ public void CreateCanvas([UseMsgId] string msgId, [UseData] FlowCanvasDetailsInfo canvasInfo)
{
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, canvasInfo);
}
diff --git a/NodeFlow/Env/MsgControllerOfServer.cs b/NodeFlow/Env/MsgControllerOfServer.cs
index b896383..5902b98 100644
--- a/NodeFlow/Env/MsgControllerOfServer.cs
+++ b/NodeFlow/Env/MsgControllerOfServer.cs
@@ -312,7 +312,7 @@ namespace Serein.NodeFlow.Env
[AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateCanvas, IsReturnValue = false)]
- public async Task CreateCanvas(string canvasName, int width, int height)
+ public async Task CreateCanvas(string canvasName, int width, int height)
{
var canvasInfo = await environment.CreateCanvasAsync(canvasName, width, height); // 监听到客户端创建节点的请求
return canvasInfo;
diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs
index 95ad133..c4cd54b 100644
--- a/NodeFlow/Env/RemoteFlowEnvironment.cs
+++ b/NodeFlow/Env/RemoteFlowEnvironment.cs
@@ -131,11 +131,28 @@ namespace Serein.NodeFlow.Env
}
///
- /// 保存项目
+ /// 远程环境下保存项目
///
public void SaveProject()
{
- OnProjectSaving?.Invoke(new ProjectSavingEventArgs());
+ _ = Task.Run(async () =>
+ {
+ // 保存项目
+ var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.SaveProject);
+ if (result is not null)
+ {
+ OnProjectSaving?.Invoke(new ProjectSavingEventArgs(result));
+ }
+ });
+
+ // 获取远程环境
+ //var projectData = new SereinProjectData()
+ //{
+ // Librarys = this.FlowLibraryManagement.GetAllLibraryInfo().ToArray(),
+ // Nodes = NodeModels.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
+ // StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid,
+ //};
+
}
@@ -167,14 +184,26 @@ namespace Serein.NodeFlow.Env
}
#endregion
- var nodeInfos = flowEnvInfo.Project.Nodes.ToList();
- LoadNodeInfos(nodeInfos); // 加载节点
-
- var canvasGuid = nodeInfos.FirstOrDefault(item => item.Guid == flowEnvInfo.Project.StartNode)?.CanvasGuid;
- if (!string.IsNullOrEmpty(canvasGuid))
+ // 加载画布
+ foreach (var info in flowEnvInfo.Project.Canvass)
{
- _ = SetStartNodeAsync(canvasGuid, flowEnvInfo.Project.StartNode); // 设置流程起点
+ var canvasModel = new FlowCanvasDetails(this);
+ canvasModel.LoadInfo(info);
+ var e = new CanvasCreateEventArgs(canvasModel);
+ OnCanvasCreate?.Invoke(e);
}
+
+ // 加载节点
+ var nodeInfos = flowEnvInfo.Project.Nodes.ToList();
+ LoadNodeInfos(nodeInfos);
+
+ // 设置每个画布的起始节点
+ foreach (var info in flowEnvInfo.Project.Canvass)
+ {
+ _ = SetStartNodeAsync(info.Guid, info.StartNode); // 设置流程起点
+
+ }
+
UIContextOperation?.Invoke(() =>
{
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); // 加载完成
@@ -427,9 +456,9 @@ namespace Serein.NodeFlow.Env
/// 宽度
/// 高度
///
- public async Task CreateCanvasAsync(string canvasName, int width, int height)
+ public async Task CreateCanvasAsync(string canvasName, int width, int height)
{
- var info = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.CreateCanvas, new
+ var info = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.CreateCanvas, new
{
canvasName,
width,
@@ -437,7 +466,7 @@ namespace Serein.NodeFlow.Env
});
if (info is not null)
{
- var model = new FlowCanvasModel(this)
+ var model = new FlowCanvasDetails(this)
{
Guid = info.Guid,
Height = info.Height,
@@ -506,6 +535,7 @@ namespace Serein.NodeFlow.Env
///
/// 设置远程环境的流程起点节点
///
+ /// 节点画布
/// 尝试设置为起始节点的节点Guid
/// 被设置为起始节点的Guid
public async Task SetStartNodeAsync(string canvasGuid, string nodeGuid)
diff --git a/NodeFlow/FlowWorkManagement.cs b/NodeFlow/FlowWorkManagement.cs
index 3d697ed..9b4a4d1 100644
--- a/NodeFlow/FlowWorkManagement.cs
+++ b/NodeFlow/FlowWorkManagement.cs
@@ -6,6 +6,7 @@ using Serein.NodeFlow.Model;
using Serein.NodeFlow.Tool;
using System;
using System.Collections.Concurrent;
+using System.Threading.Tasks.Dataflow;
using System.Xml.Linq;
namespace Serein.NodeFlow
@@ -20,25 +21,24 @@ namespace Serein.NodeFlow
///
private ConcurrentDictionary dictGlobalFlipflop = [];
-
-
///
/// 结束运行时需要执行的方法
///
private Func? ExitAction { get; set; }
+
///
/// 初始化选项
///
- public FlowWorkLibrary WorkLibrary { get; }
+ public FlowWorkOptions WorkOptions { get; }
///
/// 流程任务管理
///
- ///
- public FlowWorkManagement(FlowWorkLibrary library)
+ ///
+ public FlowWorkManagement(FlowWorkOptions options)
{
- WorkLibrary = library;
+ WorkOptions = options;
}
@@ -48,43 +48,83 @@ namespace Serein.NodeFlow
///
public async Task RunAsync(CancellationToken token)
{
- NodeModelBase? startNode = WorkLibrary.Nodes.FirstOrDefault(node => node.IsStart);
- if (startNode is null)
+ #region 注册所有节点所属的类的类型,如果注册失败则退出
+ List nodes = new List();
+ foreach (var item in WorkOptions.Flows.Values)
+ {
+ var temp = item.GetNodes();
+ nodes.AddRange(temp);
+ }
+ if (!RegisterAllType(nodes))
{
return false;
}
+ #endregion
+
+ #region 调用所有流程类的Init、Load事件
- if (!RegisterAllType())
- {
- return false;
- };
var initState = await TryInit();
if (!initState)
{
return false;
- };
+ }
+ ;
var loadState = await TryLoadAsync();
if (!loadState)
{
return false;
- };
- var task = CallFlipflopNode();
- await CallStartNode(startNode);
- await task;
+ }
+ ;
+ #endregion
+
+ // 开始调用流程
+ foreach (var kvp in WorkOptions.Flows)
+ {
+ var guid = kvp.Key;
+ var flow = kvp.Value;
+ var flowNodes = flow.GetNodes();
+
+ // 找到流程的起始节点,开始运行
+ NodeModelBase? startNode = flowNodes.FirstOrDefault(node => node.IsStart);
+ if (startNode is null)
+ {
+ return false;
+ }
+ // 是否后台运行当前画布流程
+ if (flow.IsTaskAsync)
+ {
+ _ = Task.Run(async () => await CallStartNode(startNode));
+ }
+ else
+ {
+ await CallStartNode(startNode);
+ }
+ var flipflopTasks = CallFlipflopNode(flow); // 获取所有触发器异步任务
+ _ = Task.Run(async () => await flipflopTasks); // 后台调用流程中的触发器
+ }
+
+ // 等待流程运行完成
await CallExit();
return true;
}
#region 初始化
- private bool RegisterAllType()
+ ///
+ /// 初始化节点所需的所有类型
+ ///
+ ///
+ private bool RegisterAllType(List nodes)
{
- var env = WorkLibrary.Environment;
- var nodeMds = WorkLibrary.Nodes.Select(item => item.MethodDetails).ToList(); // 获取环境中所有节点的方法信息
+ var env = WorkOptions.Environment;
+
+
+
+ var nodeMds = nodes.Select(item => item.MethodDetails).ToList(); // 获取环境中所有节点的方法信息
var allMds = new List();
allMds.AddRange(nodeMds.Where(md => md?.ActingInstanceType is not null));
- allMds.AddRange(WorkLibrary.InitMds.Where(md => md?.ActingInstanceType is not null));
- allMds.AddRange(WorkLibrary.LoadMds.Where(md => md?.ActingInstanceType is not null));
- allMds.AddRange(WorkLibrary.ExitMds.Where(md => md?.ActingInstanceType is not null));
+ allMds.AddRange(WorkOptions.InitMds.Where(md => md?.ActingInstanceType is not null));
+ allMds.AddRange(WorkOptions.LoadMds.Where(md => md?.ActingInstanceType is not null));
+ allMds.AddRange(WorkOptions.ExitMds.Where(md => md?.ActingInstanceType is not null));
var isSuccessful = true;
foreach (var md in allMds)
{
@@ -114,10 +154,10 @@ namespace Serein.NodeFlow
private async Task TryInit()
{
- var env = WorkLibrary.Environment;
- var initMds = WorkLibrary.InitMds;
- var pool = WorkLibrary.FlowContextPool;
- var ioc = WorkLibrary.Environment.IOC;
+ var env = WorkOptions.Environment;
+ var initMds = WorkOptions.InitMds;
+ var pool = WorkOptions.FlowContextPool;
+ var ioc = WorkOptions.Environment.IOC;
foreach (var md in initMds) // 初始化
{
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
@@ -136,10 +176,10 @@ namespace Serein.NodeFlow
}
private async Task TryLoadAsync()
{
- var env = WorkLibrary.Environment;
- var loadMds = WorkLibrary.LoadMds;
- var pool = WorkLibrary.FlowContextPool;
- var ioc = WorkLibrary.Environment.IOC;
+ var env = WorkOptions.Environment;
+ var loadMds = WorkOptions.LoadMds;
+ var pool = WorkOptions.FlowContextPool;
+ var ioc = WorkOptions.Environment.IOC;
foreach (var md in loadMds) // 加载时
{
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行初始化
@@ -159,10 +199,10 @@ namespace Serein.NodeFlow
}
private async Task CallExit()
{
- var env = WorkLibrary.Environment;
- var mds = WorkLibrary.ExitMds;
- var pool = WorkLibrary.FlowContextPool;
- var ioc = WorkLibrary.Environment.IOC;
+ var env = WorkOptions.Environment;
+ var mds = WorkOptions.ExitMds;
+ var pool = WorkOptions.FlowContextPool;
+ var ioc = WorkOptions.Environment.IOC;
ioc.Run(fit => fit.CancelAllTrigger());// 取消所有中断
foreach (var md in mds) // 结束时
@@ -186,10 +226,10 @@ namespace Serein.NodeFlow
return isSuccessful;
}
- private Task CallFlipflopNode()
+ private Task CallFlipflopNode(FlowTask flow)
{
- var env = WorkLibrary.Environment;
- var flipflopNodes = WorkLibrary.Nodes.Where(item => item is SingleFlipflopNode node
+ var env = WorkOptions.Environment;
+ var flipflopNodes = flow.GetNodes().Where(item => item is SingleFlipflopNode node
&& !node.IsStart
&& node.DebugSetting.IsEnable
&& node.NotExitPreviousNode())
@@ -206,10 +246,16 @@ namespace Serein.NodeFlow
}
return Task.CompletedTask;
}
+
+ ///
+ /// 从某一个节点开始执行
+ ///
+ ///
+ ///
private async Task CallStartNode(NodeModelBase startNode)
{
- var pool = WorkLibrary.FlowContextPool;
- var token = WorkLibrary.CancellationTokenSource.Token;
+ var pool = WorkOptions.FlowContextPool;
+ var token = WorkOptions.CancellationTokenSource.Token;
var context = pool.Allocate();
await startNode.StartFlowAsync(context, token);
context.Exit();
@@ -227,9 +273,9 @@ namespace Serein.NodeFlow
///
public async Task StartFlowInSelectNodeAsync(IFlowEnvironment env, NodeModelBase startNode)
{
- var pool = WorkLibrary.FlowContextPool;
+ var pool = WorkOptions.FlowContextPool;
var context = pool.Allocate();
- var token = WorkLibrary.CancellationTokenSource.Token;
+ var token = WorkOptions.CancellationTokenSource.Token;
await startNode.StartFlowAsync(context, token); // 开始运行时从选定节点开始运行
context.Reset();
pool.Free(context);
@@ -294,7 +340,7 @@ namespace Serein.NodeFlow
CancellationToken singleToken)
{
- var pool = WorkLibrary.FlowContextPool;
+ var pool = WorkOptions.FlowContextPool;
while (!singleToken.IsCancellationRequested && !singleToken.IsCancellationRequested)
{
try
diff --git a/NodeFlow/FlowWorkLibrary.cs b/NodeFlow/FlowWorkOptions.cs
similarity index 68%
rename from NodeFlow/FlowWorkLibrary.cs
rename to NodeFlow/FlowWorkOptions.cs
index 2e0ec1f..bce319a 100644
--- a/NodeFlow/FlowWorkLibrary.cs
+++ b/NodeFlow/FlowWorkOptions.cs
@@ -9,10 +9,29 @@ using System.Threading.Tasks;
namespace Serein.NodeFlow
{
+
+ public class FlowTask
+ {
+ ///
+ /// 是否异步启动流程
+ ///
+ public bool IsTaskAsync { get; set; }
+
+ ///
+ /// 流程起始节点
+ ///
+ public Func GetStartNode { get; set; }
+
+ ///
+ /// 获取当前画布流程的所有节点
+ ///
+ public Func> GetNodes { get; set; }
+ }
+
///
/// 节点任务执行依赖
///
- public class FlowWorkLibrary()
+ public class FlowWorkOptions()
{
///
/// 流程运行环境
@@ -29,14 +48,21 @@ namespace Serein.NodeFlow
///
public Serein.Library.Utils.ObjectPool FlowContextPool { get; set; }
+ ///
+ /// 每个画布需要启用的节点
+ ///
+ public Dictionary Flows { get; set; }
+
///
/// 当前任务加载的所有节点
///
- public List Nodes { get; set; }// = nodes;
+ //public List Nodes { get; set; }// = nodes;
+
///
/// 需要注册的类型
///
public Dictionary> AutoRegisterTypes { get; set; } //= autoRegisterTypes;
+
///
/// 初始化时需要的方法
///
diff --git a/Serein.Library.MyGenerator/Attribute.cs b/Serein.Library.MyGenerator/Attribute.cs
index bca2463..ba8c22a 100644
--- a/Serein.Library.MyGenerator/Attribute.cs
+++ b/Serein.Library.MyGenerator/Attribute.cs
@@ -59,10 +59,12 @@ namespace Serein.Library
/// 是否通知UI
///
public bool IsNotification = false;
+
///
/// 是否使用Console.WriteLine打印
///
public bool IsPrint = false;
+
///
/// 是否禁止参数进行修改(初始化后不能再通过 Setter 修改)
///
diff --git a/Workbench/Api/IFlowCanvas.cs b/Workbench/Api/IFlowCanvas.cs
new file mode 100644
index 0000000..bd965cb
--- /dev/null
+++ b/Workbench/Api/IFlowCanvas.cs
@@ -0,0 +1,68 @@
+using Serein.Library;
+using Serein.Workbench.Node.View;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Api
+{
+ ///
+ /// 流程画布
+ ///
+ public interface IFlowCanvas
+ {
+ ///
+ /// 画布标识
+ ///
+ string Guid { get; }
+
+ ///
+ /// 画布名称
+ ///
+ string Name { get; }
+
+ ///
+ /// 移除节点
+ ///
+ void Remove(NodeControlBase nodeControl);
+
+ ///
+ /// 添加节点
+ ///
+ void Add(NodeControlBase nodeControl);
+
+ ///
+ /// 创建节点之间方法调用关系
+ ///
+ /// 调用顺序中的起始节点
+ /// 下一节点
+ /// 调用类型
+ void CreateInvokeConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, ConnectionInvokeType type);
+
+ ///
+ /// 移除节点之间的调用关系
+ ///
+ /// 调用顺序中的起始节点
+ /// 下一节点
+ void RemoveInvokeConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl);
+
+ ///
+ /// 创建节点之间的参数传递关系
+ ///
+ /// 参数来源节点
+ /// 获取参数的节点
+ /// 指示参数是如何获取的
+ /// 作用在节点的第几个入参
+ void CreateArgConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, ConnectionArgSourceType type, int index);
+
+ ///
+ /// 移除节点之间的参数传递关系
+ ///
+ /// 参数来源节点
+ /// 获取参数的节点
+ /// 移除节点第几个入参
+ void RemoveArgConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, int index);
+ }
+}
diff --git a/Workbench/App.xaml.cs b/Workbench/App.xaml.cs
index b200c20..d13581f 100644
--- a/Workbench/App.xaml.cs
+++ b/Workbench/App.xaml.cs
@@ -32,7 +32,7 @@ namespace Serein.Workbench
collection.AddSingleton();
collection.AddSingleton();
- collection.AddTransient(); // 依赖信息
+ collection.AddTransient(); // 画布
}
public static void AddWorkbenchServices(this IServiceCollection collection)
@@ -40,8 +40,8 @@ namespace Serein.Workbench
collection.AddSingleton(); // 流程事件管理
collection.AddSingleton(); // 流程事件管理
collection.AddSingleton(); // 节点操作管理
- // collection.AddSingleton(); // 按键事件管理
- //collection.AddSingleton(); // 流程节点控件管理
+ // collection.AddSingleton(); // 按键事件管理
+ //collection.AddSingleton(); // 流程节点控件管理
}
@@ -97,21 +97,17 @@ namespace Serein.Workbench
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);
-
- });
+#if DEBUG
+ _ = this.LoadLocalProjectAsync();
+#endif
}
private async Task LoadLocalProjectAsync()
{
-
+ await Task.Delay(500);
#if DEBUG
- if (1 == 1)
+ if (1 == 10)
{
// 这里是测试代码,可以删除
string filePath;
@@ -124,7 +120,7 @@ namespace Serein.Workbench
App.FlowProjectData = JsonConvert.DeserializeObject(content);
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 91a0a20..765ac80 100644
--- a/Workbench/MainWindow.xaml.cs
+++ b/Workbench/MainWindow.xaml.cs
@@ -7,6 +7,7 @@ using Serein.Library.Utils;
using Serein.NodeFlow;
using Serein.NodeFlow.Env;
using Serein.NodeFlow.Tool;
+using Serein.Workbench.Api;
using Serein.Workbench.Extension;
using Serein.Workbench.Node;
using Serein.Workbench.Node.View;
@@ -292,14 +293,14 @@ namespace Serein.Workbench
{
return;
}
- InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Height);// 设置画布大小
+ //InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Height);// 设置画布大小
//foreach (var connection in Connections)
//{
// connection.RefreshLine(); // 窗体完成加载后试图刷新所有连接线
//}
SereinEnv.WriteLine(InfoType.INFO, $"运行环境当前工作目录:{System.IO.Directory.GetCurrentDirectory()}");
- var canvasData = project.Basic.Canvas;
+ /*var canvasData = project.Basic.Canvas;
if (canvasData is not null)
{
scaleTransform.ScaleX = 1;
@@ -312,7 +313,7 @@ namespace Serein.Workbench
translateTransform.Y += canvasData.ViewY;
// 应用变换组
FlowChartCanvas.RenderTransform = canvasTransformGroup;
- }
+ }*/
}
@@ -356,15 +357,15 @@ namespace Serein.Workbench
projectData.Basic = new Basic
{
- Canvas = new FlowCanvasInfo
- {
- Height = FlowChartCanvas.Height,
- Width = FlowChartCanvas.Width,
- ViewX = translateTransform.X,
- ViewY = translateTransform.Y,
- ScaleX = scaleTransform.ScaleX,
- ScaleY = scaleTransform.ScaleY,
- },
+ //Canvas = new FlowCanvasInfo
+ //{
+ // Height = FlowChartCanvas.Height,
+ // Width = FlowChartCanvas.Width,
+ // ViewX = translateTransform.X,
+ // ViewY = translateTransform.Y,
+ // ScaleX = scaleTransform.ScaleX,
+ // ScaleY = scaleTransform.ScaleY,
+ //},
Versions = "1",
};
@@ -527,7 +528,7 @@ namespace Serein.Workbench
/// 节点连接关系变更
///
///
- private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs)
+ private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs)
{
string fromNodeGuid = eventArgs.FromNodeGuid;
string toNodeGuid = eventArgs.ToNodeGuid;
@@ -2444,7 +2445,7 @@ namespace Serein.Workbench
var controlObj = Activator.CreateInstance(controlType, [viewModel]);
if (controlObj is NodeControlBase nodeControl)
{
- nodeControl.NodeCanvas = nodeCanvas;
+ nodeControl.FlowCanvas = (Api.IFlowCanvas)nodeCanvas;
return nodeControl;
}
else
@@ -2731,6 +2732,7 @@ public class FlowLibrary
}
}
#endregion
+
#region 顶部菜单栏 - 远程管理
private async void ButtonStartRemoteServer_Click(object sender, RoutedEventArgs e)
{
diff --git a/Workbench/Models/TabModel.cs b/Workbench/Models/TabModel.cs
index 3f67122..c1fad2b 100644
--- a/Workbench/Models/TabModel.cs
+++ b/Workbench/Models/TabModel.cs
@@ -6,6 +6,7 @@ using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
+using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
@@ -13,35 +14,50 @@ using System.Xml.Linq;
namespace Serein.Workbench.Models
{
- public partial class FlowCanvasModel : ObservableObject
+ public partial class FlowEditorTabModel : ObservableObject
{
+ ///
+ /// tab 名称
+ ///
public string Name
{
get
{
- var vm = (FlowCanvasViewModel)content.DataContext;
- return vm.Name;
+ var vm = (FlowCanvasViewModel)Content.DataContext;
+ return vm.Model.Name ?? "null";
}
set
{
- var vm = (FlowCanvasViewModel)content.DataContext;
- vm.Name = value;
+ var vm = (FlowCanvasViewModel)Content.DataContext;
+ vm.Model.Name = value;
OnPropertyChanged(nameof(Name));
}
}
+
+ ///
+ /// 正在选中
+ ///
[ObservableProperty]
private bool _isSelected;
+
+ ///
+ /// 正在编辑标题
+ ///
[ObservableProperty]
private bool _isEditing;
+
+ ///
+ /// tab对应的控件
+ ///
[ObservableProperty]
private FlowCanvasView content;
- public FlowCanvasModel()
+ public FlowEditorTabModel(FlowCanvasView content)
{
-
+ this.Content = content;
}
}
diff --git a/Workbench/Node/NodeControlBase.cs b/Workbench/Node/NodeControlBase.cs
index 2487cfb..93950d6 100644
--- a/Workbench/Node/NodeControlBase.cs
+++ b/Workbench/Node/NodeControlBase.cs
@@ -1,6 +1,8 @@
using Serein.Library;
using Serein.Library.Api;
+using Serein.Workbench.Api;
using Serein.Workbench.Node.ViewModel;
+using Serein.Workbench.Views;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
@@ -18,13 +20,14 @@ namespace Serein.Workbench.Node.View
///
/// 节点所在的画布(以后需要将画布封装出来,实现多画布的功能)
///
- public Canvas NodeCanvas { get; set; }
-
- private INodeContainerControl nodeContainerControl;
+ public IFlowCanvas FlowCanvas { get; set; }
+
///
/// 如果该节点放置在了某个容器节点,就会记录这个容器节点
///
- private INodeContainerControl NodeContainerControl { get; }
+
+ private INodeContainerControl nodeContainerControl;
+
///
/// 记录与该节点控件有关的所有连接
@@ -53,11 +56,11 @@ namespace Serein.Workbench.Node.View
public void PlaceToContainer(INodeContainerControl nodeContainerControl)
{
this.nodeContainerControl = nodeContainerControl;
- NodeCanvas.Children.Remove(this); // 临时从画布上移除
+ FlowCanvas.Remove(this); // 临时从画布上移除
var result = nodeContainerControl.PlaceNode(this);
if (!result) // 检查是否放置成功,如果不成功,需要重新添加回来
{
- NodeCanvas.Children.Add(this); // 从画布上移除
+ FlowCanvas.Add(this); // 从画布上移除
}
}
@@ -70,7 +73,7 @@ namespace Serein.Workbench.Node.View
var result = nodeContainerControl.TakeOutNode(this); // 从控件取出
if (result) // 移除成功时才添加到画布上
{
- NodeCanvas.Children.Add(this); // 重新添加到画布上
+ FlowCanvas.Add(this); // 重新添加到画布上
if (nodeContainerControl is NodeControlBase containerControl)
{
this.ViewModel.NodeModel.Position.X = containerControl.ViewModel.NodeModel.Position.X + containerControl.Width + 10;
@@ -128,20 +131,12 @@ namespace Serein.Workbench.Node.View
///
public void SetBinding()
{
- // 绑定 Canvas.Left
- Binding leftBinding = new Binding("X")
- {
- Source = ViewModel.NodeModel.Position, // 如果 X 属性在当前 DataContext 中
- Mode = BindingMode.TwoWay
- };
+ var p = ViewModel.NodeModel.Position;
+
+ Binding leftBinding = new(nameof(p.X)) { Source = p, Mode = BindingMode.TwoWay };
BindingOperations.SetBinding(this, Canvas.LeftProperty, leftBinding);
- // 绑定 Canvas.Top
- Binding topBinding = new Binding("Y")
- {
- Source = ViewModel.NodeModel.Position, // 如果 Y 属性在当前 DataContext 中
- Mode = BindingMode.TwoWay
- };
+ Binding topBinding = new(nameof(p.Y)) { Source = p, Mode = BindingMode.TwoWay };
BindingOperations.SetBinding(this, Canvas.TopProperty, topBinding);
}
diff --git a/Workbench/Node/View/GlobalDataControl.xaml.cs b/Workbench/Node/View/GlobalDataControl.xaml.cs
index 08f4abf..070066b 100644
--- a/Workbench/Node/View/GlobalDataControl.xaml.cs
+++ b/Workbench/Node/View/GlobalDataControl.xaml.cs
@@ -1,4 +1,5 @@
using Serein.NodeFlow.Model;
+using Serein.Workbench.Api;
using Serein.Workbench.Node.ViewModel;
namespace Serein.Workbench.Node.View
diff --git a/Workbench/Serein.WorkBench.csproj b/Workbench/Serein.WorkBench.csproj
index 644a844..16613c4 100644
--- a/Workbench/Serein.WorkBench.csproj
+++ b/Workbench/Serein.WorkBench.csproj
@@ -25,9 +25,11 @@
+
+
diff --git a/Workbench/Services/FlowEEForwardingService.cs b/Workbench/Services/FlowEEForwardingService.cs
index 697bacc..efcebc2 100644
--- a/Workbench/Services/FlowEEForwardingService.cs
+++ b/Workbench/Services/FlowEEForwardingService.cs
@@ -31,7 +31,7 @@ namespace Serein.Workbench.Services
/// 转发流程运行环境各个事件的实现类
///
///
- ///
+ ///
public FlowEEForwardingService(IFlowEnvironment flowEnvironment,
IFlowEnvironmentEvent flowEnvironmentEvent)
{
diff --git a/Workbench/Services/FlowNodeService.cs b/Workbench/Services/FlowNodeService.cs
index 1c32e62..cdcb120 100644
--- a/Workbench/Services/FlowNodeService.cs
+++ b/Workbench/Services/FlowNodeService.cs
@@ -1,9 +1,12 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Workbench.Api;
+using Serein.Workbench.Node;
using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel;
+using Serein.Workbench.ViewModels;
using Serein.Workbench.Views;
+using System.Windows.Controls;
namespace Serein.Workbench.Services
{
@@ -12,9 +15,23 @@ namespace Serein.Workbench.Services
///
public class FlowNodeService
{
+
+
#region 流程节点操作的相关事件
- public Action OnCreateFlowCanvasView { get; set; }
- public Action OnRemoveFlowCanvasView { get; set; }
+ ///
+ /// 添加了画布
+ ///
+ public Action OnCreateFlowCanvasView { get; set; }
+ ///
+ /// 移除了画布
+ ///
+ public Action OnRemoveFlowCanvasView { get; set; }
+
+ ///
+ /// 添加了节点
+ ///
+ public Action OnCreateNode { get; set; }
+
#endregion
@@ -32,7 +49,7 @@ namespace Serein.Workbench.Services
///
/// 当前需要创建的节点类型
///
- public NodeControlType? CurrentNodeControlType { get; set; }
+ public NodeControlType CurrentNodeControlType { get; set; } = NodeControlType.None;
///
@@ -55,23 +72,27 @@ namespace Serein.Workbench.Services
///
public NodeControlBase? ConnectionEndNode { get; set; }
-
- /*
-
-*/
-
///
/// 记录流程画布
///
- private readonly Dictionary FlowCanvasViews = [];
+ private readonly Dictionary Canvass = [];
+
///
/// 记录加载的节点
///
- private readonly Dictionary NodeControls = [];
+ private readonly Dictionary NodeControls = [];
+ ///
+ /// 运行环境接口
+ ///
private readonly IFlowEnvironment flowEnvironment;
+
+ ///
+ /// 运行环境事件转发器
+ ///
private readonly IFlowEEForwardingService flowEEForwardingService;
+
#region 初始化
public FlowNodeService(IFlowEnvironment flowEnvironment,
IFlowEEForwardingService flowEEForwardingService)
@@ -79,44 +100,292 @@ namespace Serein.Workbench.Services
this.flowEnvironment = flowEnvironment;
this.flowEEForwardingService = flowEEForwardingService;
InitFlowEvent();
+ InitNodeType();
}
- public void InitFlowEvent()
+
+ ///
+ /// 注册节点类型
+ ///
+ private void InitNodeType()
{
- flowEEForwardingService.OnCanvasCreate += FlowEEForwardingService_OnCanvasCreate;
- flowEEForwardingService.OnCanvasRemove += FlowEEForwardingService_OnCanvasRemove;
- flowEEForwardingService.OnNodeCreate += FlowEEForwardingService_OnNodeCreate;
- flowEEForwardingService.OnNodeRemove += FlowEEForwardingService_OnNodeRemove;
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.UI, typeof(UINodeControl), typeof(UINodeControlViewModel));
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.Action, typeof(ActionNodeControl), typeof(ActionNodeControlViewModel));
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.Flipflop, typeof(FlipflopNodeControl), typeof(FlipflopNodeControlViewModel));
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.ExpOp, typeof(ExpOpNodeControl), typeof(ExpOpNodeControlViewModel));
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.ExpCondition, typeof(ConditionNodeControl), typeof(ConditionNodeControlViewModel));
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.ConditionRegion, typeof(ConditionRegionControl), typeof(ConditionRegionNodeControlViewModel));
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.GlobalData, typeof(GlobalDataControl), typeof(GlobalDataNodeControlViewModel));
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.Script, typeof(ScriptNodeControl), typeof(ScriptNodeControlViewModel));
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.NetScript, typeof(NetScriptNodeControl), typeof(NetScriptNodeControlViewModel));
}
+ ///
+ /// 注册节点事件
+ ///
+ private void InitFlowEvent()
+ {
+ flowEEForwardingService.OnCanvasCreate += FlowEEForwardingService_OnCanvasCreate; // 创建了画布
+ flowEEForwardingService.OnCanvasRemove += FlowEEForwardingService_OnCanvasRemove; // 移除了画布
+ flowEEForwardingService.OnNodeCreate += FlowEEForwardingService_OnNodeCreate; // 创建了节点
+ flowEEForwardingService.OnNodeRemove += FlowEEForwardingService_OnNodeRemove; // 移除了节点
+
+ flowEEForwardingService.OnNodePlace += FlowEEForwardingService_OnNodePlace; // 节点放置在容器中
+ flowEEForwardingService.OnNodeTakeOut += FlowEEForwardingService_OnNodeTakeOut; ; // 节点从容器中取出
+
+ flowEEForwardingService.OnNodeConnectChange += FlowEEForwardingService_OnNodeConnectChange; // 节点连接状态改变事件
+
+ }
+
+ private void FlowEEForwardingService_OnNodeConnectChange(NodeConnectChangeEventArgs e)
+ {
+ var canvasGuid = e.CanvasGuid;
+ string fromNodeGuid = e.FromNodeGuid;
+ string toNodeGuid = e.ToNodeGuid;
+ if (!TryGetCanvas(canvasGuid, out var flowCanvas)
+ || flowCanvas is not IFlowCanvas flow
+ || !TryGetControl(fromNodeGuid, out var fromNode)
+ || !TryGetControl(toNodeGuid, out var toNode))
+ {
+ return;
+ }
+
+ Action? action = (e.JunctionOfConnectionType, e.ChangeType) switch
+ {
+ (JunctionOfConnectionType.Invoke, NodeConnectChangeEventArgs.ConnectChangeType.Create) => () => flow.CreateInvokeConnection(fromNode, toNode, e.ConnectionInvokeType), // 创建节点之间的调用关系
+ (JunctionOfConnectionType.Invoke, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => () => flow.RemoveInvokeConnection(fromNode, toNode), // 移除节点之间的调用关系
+ (JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Create) => () => flow.CreateArgConnection(fromNode, toNode, e.ConnectionArgSourceType, e.ArgIndex), // 创建节点之间的参数传递关系
+ (JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => () => flow.RemoveArgConnection(fromNode, toNode, e.ArgIndex), // 移除节点之间的参数传递关系
+ _ => null
+ };
+ action?.Invoke();
+ return;
+
+ }
+
+ private void FlowEEForwardingService_OnNodeTakeOut(NodeTakeOutEventArgs eventArgs)
+ {
+ string nodeGuid = eventArgs.NodeGuid;
+ if (!TryGetControl(nodeGuid, out var nodeControl))
+ {
+ return;
+ }
+ nodeControl.TakeOutContainer(); // 从容器节点中取出
+ }
+
+ private void FlowEEForwardingService_OnNodePlace(NodePlaceEventArgs eventArgs)
+ {
+ string nodeGuid = eventArgs.NodeGuid;
+ string containerNodeGuid = eventArgs.ContainerNodeGuid;
+ if (!TryGetControl(nodeGuid, out var nodeControl)
+ || !TryGetControl(containerNodeGuid, out var containerNodeControl))
+ {
+ return;
+ }
+ if (containerNodeControl is not INodeContainerControl containerControl)
+ {
+ SereinEnv.WriteLine(InfoType.WARN,
+ $"节点[{nodeGuid}]无法放置于节点[{containerNodeGuid}]," +
+ $"因为后者并不实现 INodeContainerControl 接口");
+ return;
+ }
+ nodeControl.PlaceToContainer(containerControl); // 放置在容器节点中
+ }
+ #endregion
+
+ #region 节点、画布相关的事件
+
+ ///
+ /// 节点移除
+ ///
+ ///
private void FlowEEForwardingService_OnNodeRemove(NodeRemoveEventArgs eventArgs)
{
- throw new NotImplementedException();
+ if (!TryGetCanvas(eventArgs.CanvasGuid, out var nodeCanvas) || nodeCanvas is not IFlowCanvas api)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"无法移除节点,画布不存在。");
+ return;
+ }
+ if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl))
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"无法移除节点,节点不存在。");
+ return;
+ }
+ api.Remove(nodeControl);
+
}
+ ///
+ /// 节点创建
+ ///
+ ///
+
private void FlowEEForwardingService_OnNodeCreate(NodeCreateEventArgs eventArgs)
{
- throw new NotImplementedException();
+ #region 校验事件传入值
+ var position = eventArgs.Position;
+ var cavnasGuid = eventArgs.CanvasGuid;
+ var nodeModel = eventArgs.NodeModel;
+ if (NodeControls.ContainsKey(nodeModel.Guid))
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"创建节点时发生意外:节点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;
+ }
+
+ if (!TryGetCanvas(cavnasGuid, out var nodeCanvas))
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"无法创建{nodeModel.ControlType}节点,不存在画布【{cavnasGuid}】。");
+ return;
+ }
+ #endregion
+
+ #region 创建控件
+
+ NodeControlBase nodeControl;
+ try
+ {
+ nodeControl = CreateNodeControl(nodeMVVM.ControlType, // 控件UI类型
+ nodeMVVM.ViewModelType, // 控件VIewModel类型
+ nodeModel, // 控件数据实体
+ nodeCanvas); // 所在画布
+ OnCreateNode.Invoke(nodeControl); // 创建节点
+ }
+ catch (Exception ex)
+ {
+ SereinEnv.WriteLine(ex);
+ return;
+ }
+
+ NodeControls.TryAdd(nodeControl.ViewModel.NodeModel.Guid, nodeControl); // 记录创建了的节点控件
+
+ #endregion
+
}
+ ///
+ /// 画布移除
+ ///
+ ///
private void FlowEEForwardingService_OnCanvasRemove(CanvasRemoveEventArgs eventArgs)
{
- OnRemoveFlowCanvasView.Invoke(eventArgs.CanvasGuid);
+ if (!TryGetCanvas(eventArgs.CanvasGuid, out var nodeCanvas))
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"无法移除画布,画布不存在。");
+ return;
+ }
+ OnRemoveFlowCanvasView.Invoke(nodeCanvas);
}
+ ///
+ /// 画布创建
+ ///
+ ///
private void FlowEEForwardingService_OnCanvasCreate(CanvasCreateEventArgs eventArgs)
{
- var info = eventArgs.Model;
var model = eventArgs.Model;
- FlowCanvasView canvasView = new FlowCanvasView();
- canvasView.ViewModel.CanvasGuid = info.Guid;
- canvasView.ViewModel.Model = model;
- FlowCanvasViews.Add(info.Guid, canvasView);
+ var guid = model.Guid;
+ if (Canvass.ContainsKey(guid))
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"创建画布时发生意外:节点Guid重复 - {guid}");
+ return;
+ }
+
+ FlowCanvasView canvasView = new FlowCanvasView(model);
+ //canvasView.ViewModel.Model = model;
+ //canvasView.ViewModel.CanvasGuid = model.Guid;
+ //canvasView.ViewModel.Name = model.Name;
+ //canvasView.SetBinding(model);
+ Canvass.Add(model.Guid, canvasView);
OnCreateFlowCanvasView.Invoke(canvasView); // 传递给订阅者
}
+
+
#endregion
+ ///
+ /// 创建节点控件
+ ///
+ /// 节点控件视图控件类型
+ /// 节点控件ViewModel类型
+ /// 节点Model实例
+ /// 节点所在画布
+ ///
+ /// 无法创建节点控件
+ private static NodeControlBase CreateNodeControl(Type controlType, Type viewModelType, NodeModelBase model, IFlowCanvas nodeCanvas)
+ {
+ if ((controlType is null)
+ || viewModelType is null
+ || model is null)
+ {
+ throw new Exception("无法创建节点控件");
+ }
+ if (typeof(NodeControlBase).IsSubclassOf(controlType) || typeof(NodeControlViewModelBase).IsSubclassOf(viewModelType))
+ {
+ throw new Exception("无法创建节点控件");
+ }
+
+ if (string.IsNullOrEmpty(model.Guid))
+ {
+ model.Guid = Guid.NewGuid().ToString();
+ }
+
+ var viewModel = Activator.CreateInstance(viewModelType, [model]);
+ var controlObj = Activator.CreateInstance(controlType, [viewModel]);
+ if (controlObj is NodeControlBase nodeControl)
+ {
+ nodeControl.FlowCanvas = nodeCanvas;
+ return nodeControl;
+ }
+ else
+ {
+ throw new Exception("无法创建节点控件");
+ }
+ }
+
+ ///
+ /// 从Guid获取节点控件
+ ///
+ ///
+ ///
+ ///
+ private bool TryGetControl(string nodeGuid, out NodeControlBase nodeControl)
+ {
+ nodeControl = null;
+ if (string.IsNullOrEmpty(nodeGuid))
+ {
+ return false;
+ }
+ return NodeControls.TryGetValue(nodeGuid, out nodeControl);
+ }
+
+
+ ///
+ /// 从Guid获取画布视图
+ ///
+ ///
+ ///
+ ///
+ private bool TryGetCanvas(string nodeGuid, out FlowCanvasView flowCanvas)
+ {
+ flowCanvas = null;
+ if (string.IsNullOrEmpty(nodeGuid))
+ {
+ return false;
+ }
+ return Canvass.TryGetValue(nodeGuid, out flowCanvas);
+ }
@@ -128,8 +397,8 @@ namespace Serein.Workbench.Services
///
public void CreateFlowCanvas()
{
- int height = 1000;
- int width = 600;
+ int width = 1200;
+ int height = 780;
_ = Task.Run(async () =>
{
var result = await flowEnvironment.CreateCanvasAsync("", width, height);
@@ -146,7 +415,8 @@ namespace Serein.Workbench.Services
{
return;
}
- _ = flowEnvironment.RemoveCanvasAsync(CurrentSelectCanvas.ViewModel.CanvasGuid);
+ var model = ((FlowCanvasViewModel)CurrentSelectCanvas.DataContext).Model;
+ _ = flowEnvironment.RemoveCanvasAsync(model.Guid);
}
///
@@ -154,32 +424,31 @@ namespace Serein.Workbench.Services
///
public void CreateNode()
{
- string canvasGuid = CurrentSelectCanvas.ViewModel.CanvasGuid;
- NodeControlType? nodeType = CurrentNodeControlType;
+ var model = ((FlowCanvasViewModel)CurrentSelectCanvas.DataContext).Model;
+
+ string canvasGuid = model.Guid;
+ NodeControlType nodeType = CurrentNodeControlType;
PositionOfUI? position = CurrentMouseLocation;
MethodDetailsInfo? methodDetailsInfo = CurrentDragMdInfo;
- if (nodeType is null)
- {
- return;
- }
+
if (position is null)
{
return;
}
- _ = flowEnvironment.CreateNodeAsync(canvasGuid, (NodeControlType)nodeType, position, methodDetailsInfo);
+ _ = flowEnvironment.CreateNodeAsync(canvasGuid, nodeType, position, methodDetailsInfo);
}
///
/// 向运行环境发出请求:移除节点
///
- public void RemoteNode()
+ public void RemoteNode(NodeControlBase nodeControl)
{
- NodeControlBase? node = CurrentSelectNodeControl;
- if (node is null)
+ //NodeControlBase? node = CurrentSelectNodeControl;
+ if (nodeControl is null)
{
return;
}
- var model = node.ViewModel.NodeModel;
+ var model = nodeControl.ViewModel.NodeModel;
if (model is null)
{
return;
diff --git a/Workbench/Services/KeyEventService.cs b/Workbench/Services/KeyEventService.cs
index ac6069f..a21718c 100644
--- a/Workbench/Services/KeyEventService.cs
+++ b/Workbench/Services/KeyEventService.cs
@@ -1,10 +1,11 @@
-/*
+
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Windows.Input;
namespace Serein.Workbench.Services
{
@@ -38,7 +39,6 @@ namespace Serein.Workbench.Services
///
internal class KeyEventService : IKeyEventService
{
-
///
/// 按键按下
///
@@ -77,4 +77,3 @@ namespace Serein.Workbench.Services
}
}
}
-*/
\ No newline at end of file
diff --git a/Workbench/Services/NodeControlService.cs b/Workbench/Services/NodeControlService.cs
index 23064ad..7654e57 100644
--- a/Workbench/Services/NodeControlService.cs
+++ b/Workbench/Services/NodeControlService.cs
@@ -18,14 +18,6 @@ using System.Threading.Tasks;
using System.Windows.Controls;
-
-
-
-namespace Serein.Workbench.Api
-{
-
-}
-
namespace Serein.Workbench.Services
{
///
diff --git a/Workbench/Services/WorkbenchEventService.cs b/Workbench/Services/WorkbenchEventService.cs
index 78ede6b..4469620 100644
--- a/Workbench/Services/WorkbenchEventService.cs
+++ b/Workbench/Services/WorkbenchEventService.cs
@@ -1,7 +1,11 @@
-using Serein.Library;
+using Microsoft.Win32;
+using Newtonsoft.Json.Linq;
+using Serein.Library;
using Serein.Library.Api;
+using Serein.Workbench.Api;
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
@@ -50,25 +54,32 @@ namespace Serein.Workbench.Services
{
private readonly IFlowEnvironment flowEnvironment;
+ private readonly IFlowEEForwardingService flowEEForwardingService;
+
///
/// 管理工作台的事件
///
///
- public WorkbenchEventService(IFlowEnvironment flowEnvironment)
+ ///
+ public WorkbenchEventService(IFlowEnvironment flowEnvironment, IFlowEEForwardingService flowEEForwardingService)
{
this.flowEnvironment = flowEnvironment;
-
+ this.flowEEForwardingService = flowEEForwardingService;
+ InitEvents();
}
- private void SubscribeEvents()
+ private void InitEvents()
{
-
+ flowEEForwardingService.OnProjectSaving += SaveProjectToLocalFile;
}
+
+
///
/// 预览了某个方法信息(待创建)
///
public event PreviewlMethodInfoHandler? OnPreviewlMethodInfo;
+
///
/// 预览依赖方法信息
///
@@ -84,6 +95,96 @@ namespace Serein.Workbench.Services
{
}
+
+ ///
+ /// 保存项目数据到本地文件
+ ///
+ ///
+ private void SaveProjectToLocalFile(ProjectSavingEventArgs e)
+ {
+ var project = e.ProjectData;
+ // 创建一个新的保存文件对话框
+ SaveFileDialog saveFileDialog = new()
+ {
+ Filter = "DynamicNodeFlow Files (*.dnf)|*.dnf",
+ DefaultExt = "dnf",
+ FileName = "project.dnf"
+ // FileName = System.IO.Path.GetFileName(App.FileDataPath)
+ };
+
+ // 显示保存文件对话框
+ bool? result = saveFileDialog.ShowDialog();
+ // 如果用户选择了文件并点击了保存按钮
+ if (result == false)
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, "取消保存文件");
+ return;
+ }
+
+ var savePath = saveFileDialog.FileName;
+ string? librarySavePath = System.IO.Path.GetDirectoryName(savePath);
+ if (string.IsNullOrEmpty(librarySavePath))
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, "保存项目DLL时返回了意外的文件保存路径");
+ return;
+ }
+
+
+ Uri saveProjectFileUri = new Uri(savePath);
+ SereinEnv.WriteLine(InfoType.INFO, "项目文件保存路径:" + savePath);
+ for (int index = 0; index < project.Librarys.Length; index++)
+ {
+ NodeLibraryInfo? library = project.Librarys[index];
+ string sourceFilePath = new Uri(library.FilePath).LocalPath; // 源文件夹
+ string targetDir = System.IO.Path.Combine(librarySavePath, library.AssemblyName); // 目标文件夹
+ if (!Path.Exists(targetDir))
+ {
+ Directory.CreateDirectory(targetDir);
+ }
+ string targetFilePath = System.IO.Path.Combine(targetDir, library.FileName); // 目标文件夹
+
+ try
+ {
+ if (File.Exists(sourceFilePath))
+ {
+ if (!File.Exists(targetFilePath))
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"源文件路径 : {sourceFilePath}");
+ SereinEnv.WriteLine(InfoType.INFO, $"目标路径 : {targetFilePath}");
+ File.Copy(sourceFilePath, targetFilePath, true);
+
+ }
+ else
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"目标路径已有类库文件: {targetFilePath}");
+ }
+ }
+ else
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"源文件不存在 : {targetFilePath}");
+ }
+ }
+ catch (IOException ex)
+ {
+
+ SereinEnv.WriteLine(InfoType.ERROR, ex.Message);
+ }
+ var dirName = System.IO.Path.GetDirectoryName(targetFilePath);
+ if (!string.IsNullOrEmpty(dirName))
+ {
+ var tmpUri2 = new Uri(targetFilePath);
+ var relativePath = saveProjectFileUri.MakeRelativeUri(tmpUri2).ToString(); // 转为类库的相对文件路径
+
+ //string relativePath = System.IO.Path.GetRelativePath(savePath, targetPath);
+ project.Librarys[index].FilePath = relativePath;
+ }
+
+ }
+
+ JObject projectJsonData = JObject.FromObject(project);
+ File.WriteAllText(savePath, projectJsonData.ToString());
+
+ }
}
}
diff --git a/Workbench/ViewModels/FlowCanvasViewModel.cs b/Workbench/ViewModels/FlowCanvasViewModel.cs
index fb9ea9f..b8dc077 100644
--- a/Workbench/ViewModels/FlowCanvasViewModel.cs
+++ b/Workbench/ViewModels/FlowCanvasViewModel.cs
@@ -1,21 +1,27 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library;
+using Serein.Library.Api;
+using Serein.Workbench.Api;
using Serein.Workbench.Node.View;
+using Serein.Workbench.Services;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
+using System.Windows.Input;
+using System.Windows;
+using System.Windows.Controls;
namespace Serein.Workbench.ViewModels
{
public partial class FlowCanvasViewModel : ObservableObject
{
+
///
/// 画布当前选中的节点
///
- public NodeControlBase CurrentSelectNodeControl { get; set; }
-
+ public NodeControlBase CurrentSelectNode { get; set; }
///
/// 正在创建节点方法调用关系
@@ -29,29 +35,13 @@ namespace Serein.Workbench.ViewModels
[ObservableProperty]
private bool _isConnectionArgSourceNode;
- ///
- /// 画布显示名称
- ///
- [ObservableProperty]
- private string _name;
-
- ///
- /// 画布ID
- ///
- [ObservableProperty]
- private string _canvasGuid;
-
///
/// 画布数据实体
///
[ObservableProperty]
- private FlowCanvasModel _model;
+ private FlowCanvasDetails _model;
- public FlowCanvasViewModel()
- {
-
- }
}
}
diff --git a/Workbench/ViewModels/FlowEditViewModel.cs b/Workbench/ViewModels/FlowEditViewModel.cs
index 45fab07..029b23e 100644
--- a/Workbench/ViewModels/FlowEditViewModel.cs
+++ b/Workbench/ViewModels/FlowEditViewModel.cs
@@ -1,6 +1,8 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
+using Serein.Workbench.Api;
using Serein.Workbench.Models;
+using Serein.Workbench.Node.View;
using Serein.Workbench.Services;
using Serein.Workbench.Views;
using System;
@@ -20,13 +22,17 @@ namespace Serein.Workbench.ViewModels
///
public partial class FlowEditViewModel : ObservableObject
{
- public ObservableCollection Tabs { get; set; } = [];
+ public ObservableCollection CanvasTabs { get; set; } = [];
public ICommand AddTabCommand { get; set; }
public ICommand RemoveTabCommand { get; set; }
public ICommand RenameTabCommand { get; set; }
+
+ ///
+ /// 当前选择的画布
+ ///
[ObservableProperty]
- private FlowCanvasModel _selectedTab;
+ private FlowEditorTabModel _selectedTab;
private readonly FlowNodeService flowNodeService;
@@ -34,39 +40,42 @@ namespace Serein.Workbench.ViewModels
{
this.flowNodeService = flowNodeService;
AddTabCommand = new RelayCommand(AddTab);
- RemoveTabCommand = new RelayCommand(RemoveTab, CanRemoveTab);
+ RemoveTabCommand = new RelayCommand(RemoveTab);
- flowNodeService.OnCreateFlowCanvasView += OnCreateFlowCanvasView; // 运行环境创建了画布
- flowNodeService.OnRemoveFlowCanvasView += OnRemoveFlowCanvasView; // 运行环境移除了画布
+ flowNodeService.OnCreateFlowCanvasView += OnCreateFlowCanvasView; // 创建了画布
+ flowNodeService.OnRemoveFlowCanvasView += OnRemoveFlowCanvasView; // 移除了画布
this.PropertyChanged += OnPropertyChanged;
+
+
}
+
+
private void OnPropertyChanged(object? value, PropertyChangedEventArgs e)
{
- if (nameof(SelectedTab).Equals(e.PropertyName) && value is FlowCanvasModel model)
- {
- flowNodeService.CurrentSelectCanvas = model.Content; // 选中的视图发生改变
- }
+ if (this.SelectedTab is null) return;
+ flowNodeService.CurrentSelectCanvas = this.SelectedTab.Content;
}
#region 响应环境事件
- private void OnCreateFlowCanvasView(FlowCanvasView FlowCanvasView)
+ private void OnCreateFlowCanvasView(FlowCanvasView canvas)
{
- var model = new FlowCanvasModel { Content = FlowCanvasView, Name = FlowCanvasView.ViewModel.Name };
- Tabs.Add(model);
+ var model = new FlowEditorTabModel(canvas);
+ CanvasTabs.Add(model);
+ SelectedTab = model;
}
- private void OnRemoveFlowCanvasView(string canvasGuid)
+ private void OnRemoveFlowCanvasView(FlowCanvasView canvas)
{
- var tab = Tabs.FirstOrDefault(t => t.Content.ViewModel.Model.Guid.Equals(canvasGuid, StringComparison.OrdinalIgnoreCase));
+ var tab = CanvasTabs.FirstOrDefault(c => c.Content.Guid.Equals(canvas.Guid));
if (tab is null)
{
return;
}
- Tabs.Remove(tab);
- Tabs.Remove(SelectedTab);
- if(Tabs.Count > 0 && Tabs[^1] is FlowCanvasModel view )
+ CanvasTabs.Remove(tab);
+
+ if (CanvasTabs.Count > 0 && CanvasTabs[^1] is FlowEditorTabModel c)
{
- SelectedTab = view;
+ SelectedTab = c;
}
}
#endregion
@@ -75,10 +84,9 @@ namespace Serein.Workbench.ViewModels
private void AddTab() => flowNodeService.CreateFlowCanvas();
private void RemoveTab()
{
- if (Tabs.Count > 0 && SelectedTab != null) flowNodeService.RemoveFlowCanvas();
+ if (CanvasTabs.Count > 0 && SelectedTab != null) flowNodeService.RemoveFlowCanvas();
}
- private bool CanRemoveTab() => SelectedTab != null;
@@ -86,12 +94,12 @@ namespace Serein.Workbench.ViewModels
/// 进入编辑模式
///
///
- public void StartEditingTab(FlowCanvasModel tab)
+ public void StartEditingTab(FlowEditorTabModel tab)
{
if (tab != null)
{
tab.IsEditing = true;
- OnPropertyChanged(nameof(Tabs)); // 刷新Tabs集合,以便更新UI
+ OnPropertyChanged(nameof(CanvasTabs)); // 刷新Tabs集合,以便更新UI
}
}
@@ -100,13 +108,13 @@ namespace Serein.Workbench.ViewModels
///
///
///
- public void EndEditingTab(FlowCanvasModel tab, string newName)
+ public void EndEditingTab(FlowEditorTabModel tab, string? newName = null)
{
if (tab != null)
{
tab.IsEditing = false;
- tab.Name = newName; // 设置新名称
- OnPropertyChanged(nameof(Tabs)); // 刷新Tabs集合
+ if(tab.Name != newName && !string.IsNullOrWhiteSpace(newName)) tab.Name = newName; // 名称合法时设置新名称
+ OnPropertyChanged(nameof(CanvasTabs)); // 刷新Tabs集合
}
}
diff --git a/Workbench/ViewModels/FlowWorkbenchViewModel.cs b/Workbench/ViewModels/FlowWorkbenchViewModel.cs
index 387e870..47fb4dd 100644
--- a/Workbench/ViewModels/FlowWorkbenchViewModel.cs
+++ b/Workbench/ViewModels/FlowWorkbenchViewModel.cs
@@ -1,6 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Workbench.Api;
using Serein.Workbench.Models;
+using Serein.Workbench.Services;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
@@ -13,10 +14,12 @@ namespace Serein.Workbench.ViewModels
internal partial class FlowWorkbenchViewModel : ObservableObject
{
private readonly IFlowEEForwardingService flowEEForwardingService;
+ private readonly IWorkbenchEventService workbenchEventService;
- public FlowWorkbenchViewModel(IFlowEEForwardingService flowEEForwardingService)
+ public FlowWorkbenchViewModel(IFlowEEForwardingService flowEEForwardingService, IWorkbenchEventService workbenchEventService)
{
this.flowEEForwardingService = flowEEForwardingService;
+ this.workbenchEventService = workbenchEventService;
//flowEEForwardingService.OnDllLoad += FlowEEForwardingService_OnDllLoad;
}
}
diff --git a/Workbench/ViewModels/MainMenuBarViewModel.cs b/Workbench/ViewModels/MainMenuBarViewModel.cs
index 05f1d0e..6bf28ff 100644
--- a/Workbench/ViewModels/MainMenuBarViewModel.cs
+++ b/Workbench/ViewModels/MainMenuBarViewModel.cs
@@ -21,23 +21,33 @@ namespace Serein.Workbench.ViewModels
/// 加载远程项目
///
public ICommand LoadRemoteProjectCommand { get; private set; }
-
+
///
/// 增加流程图
///
public ICommand CreateFlowCanvasCommand { get; private set; }
-
///
- /// 增加流程图
+ /// 移除流程图
///
public ICommand RemoteFlowCanvasCommand { get; private set; }
-
+ ///
+ /// 运行当前画布流程
+ ///
+ public ICommand StartCurrentCanvasFlowCommand { get; private set; }
+
+ ///
+ /// 停止当前画布流程
+ ///
+ public ICommand StopCurrentCanvasFlowCommand { get; private set; }
+
+
+
///
/// 打开环境输出窗口
///
public ICommand OpenEnvOutWindowCommand { get; private set; }
-
+
///
/// 打开动态编译窗口
///
@@ -49,13 +59,33 @@ namespace Serein.Workbench.ViewModels
{
this.environment = environment;
- SaveProjectCommand = new RelayCommand(SaveProject);
+ SaveProjectCommand = new RelayCommand(SaveProject); // 保存项目
+ LoadLocalProjectCommand = new RelayCommand(LoadLocalProject); // 加载本地项目
+ LoadRemoteProjectCommand = new RelayCommand(LoadRemoteProject); // 加载远程项目
+
+ CreateFlowCanvasCommand = new RelayCommand(CreateFlowCanvas); // 增加画布
+ RemoteFlowCanvasCommand = new RelayCommand(RemoteFlowCanvas); // 移除画布
+
+ StartCurrentCanvasFlowCommand = new RelayCommand(StartCurrentCanvasFlow); // 运行当前流程
+ StopCurrentCanvasFlowCommand = new RelayCommand(StopCurrentCanvasFlow); // 停止当前流程
+
+ OpenEnvOutWindowCommand = new RelayCommand(OpenEnvOutWindow); // 打开运行输出窗口
+ OpenDynamicCompilerCommand = new RelayCommand(OpenDynamicCompiler); // 打开动态编译仓库窗口
}
- public void SaveProject()
- {
-
+ private void SaveProject() {
+ environment.SaveProject(); // 保存项目
}
+ private void LoadLocalProject() {
+ //environment.LoadProject(); // 加载项目
+ }
+ private void LoadRemoteProject() { }
+ private void CreateFlowCanvas() { }
+ private void RemoteFlowCanvas() { }
+ private void StartCurrentCanvasFlow() { }
+ private void StopCurrentCanvasFlow() { }
+ private void OpenDynamicCompiler() { }
+ private void OpenEnvOutWindow() { }
}
}
diff --git a/Workbench/Views/FlowCanvasView.xaml b/Workbench/Views/FlowCanvasView.xaml
index 152f67a..f92bd40 100644
--- a/Workbench/Views/FlowCanvasView.xaml
+++ b/Workbench/Views/FlowCanvasView.xaml
@@ -5,8 +5,11 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.Workbench.Views"
xmlns:tool="clr-namespace:Serein.Workbench.Tool.Converters"
+ xmlns:vm="clr-namespace:Serein.Workbench.ViewModels"
mc:Ignorable="d"
- d:DesignHeight="450" d:DesignWidth="800">
+ d:DesignHeight="450"
+ d:DesignWidth="800"
+ d:DataContext="{d:DesignInstance vm:FlowCanvasViewModel}">
@@ -16,22 +19,23 @@
+ ClipToBounds="True">
+