diff --git a/Library/Extension/FlowModelExtension.cs b/Library/Extension/FlowModelExtension.cs
index 9648f51..84b22f6 100644
--- a/Library/Extension/FlowModelExtension.cs
+++ b/Library/Extension/FlowModelExtension.cs
@@ -33,26 +33,29 @@ namespace Serein.Library
ScaleY = model.ScaleY,
ViewX = model.ViewX,
ViewY = model.ViewY,
- StartNode = model.StartNode,
+ StartNode = model.StartNode?.Guid,
};
}
///
/// 从画布信息加载
///
- ///
- ///
- public static void LoadInfo(this FlowCanvasDetails model, FlowCanvasDetailsInfo info)
+ ///
+ ///
+ public static void LoadInfo(this FlowCanvasDetails canvasModel, FlowCanvasDetailsInfo canvasInfo)
{
- 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;
- model.StartNode = info.StartNode;
+ canvasModel.Guid = canvasInfo.Guid;
+ canvasModel.Height = canvasInfo.Height;
+ canvasModel.Width = canvasInfo.Width;
+ canvasModel.Name = canvasInfo.Name;
+ canvasModel.ScaleX = canvasInfo.ScaleX;
+ canvasModel.ScaleY = canvasInfo.ScaleY;
+ canvasModel.ViewX = canvasInfo.ViewX;
+ canvasModel.ViewY = canvasInfo.ViewY;
+ if(canvasModel.Env.TryGetNodeModel(canvasInfo.StartNode,out var nodeModel))
+ {
+ canvasModel.StartNode = nodeModel;
+ }
}
///
@@ -61,7 +64,7 @@ namespace Serein.Library
///
public static ParameterData[] SaveParameterInfo(this NodeModelBase nodeModel)
{
- if (nodeModel.MethodDetails.ParameterDetailss == null)
+ if (nodeModel.MethodDetails is null || nodeModel.MethodDetails.ParameterDetailss == null)
{
return new ParameterData[0];
}
diff --git a/Library/FlowNode/FlowCanvasDetails.cs b/Library/FlowNode/FlowCanvasDetails.cs
index c8d6e49..6725cfa 100644
--- a/Library/FlowNode/FlowCanvasDetails.cs
+++ b/Library/FlowNode/FlowCanvasDetails.cs
@@ -24,7 +24,6 @@ namespace Serein.Library
Env = env;
}
-
public IFlowEnvironment Env { get; }
///
@@ -87,19 +86,17 @@ namespace Serein.Library
[PropertyInfo(IsNotification = true)]
private double _scaleY = 1;
-
///
- /// 起始节点私有属性
+ /// 起始节点
///
- private string _startNode;
+ [PropertyInfo]
+ private NodeModelBase _startNode;
}
public partial class FlowCanvasDetails
{
-
-
}
diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs
index 9434293..d864a62 100644
--- a/Library/FlowNode/ParameterDetails.cs
+++ b/Library/FlowNode/ParameterDetails.cs
@@ -248,7 +248,8 @@ namespace Serein.Library
}
else
{
- inputParameter = context.GetFlowData(previousNode).Value; // 当前传递的数据
+ var flowData = context.GetFlowData(previousNode);
+ inputParameter = flowData.Value; // 当前传递的数据
}
}
else
@@ -259,7 +260,16 @@ namespace Serein.Library
}
if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeData)
{
- inputParameter = context.GetFlowData(argSourceNodeModel).Value;
+ var flowData = context.GetFlowData(argSourceNodeModel);
+ if(flowData is null)
+ {
+ inputParameter = null;
+ }
+ else
+ {
+
+ inputParameter = flowData.Value;
+ }
}
else if (ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
{
diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs
index cd7fd46..260eb95 100644
--- a/NodeFlow/Env/FlowEnvironment.cs
+++ b/NodeFlow/Env/FlowEnvironment.cs
@@ -8,6 +8,7 @@ using Serein.NodeFlow.Tool;
using System;
using System.Collections.Specialized;
using System.Diagnostics;
+using System.Net.Http.Headers;
using System.Net.Mime;
using System.Reactive;
using System.Reflection;
@@ -228,7 +229,7 @@ namespace Serein.NodeFlow.Env
///
/// UI线程操作类
///
- public UIContextOperation UIContextOperation { get; set; }
+ public UIContextOperation UIContextOperation { get; private set; }
///
/// 节点视图模型管理类
@@ -389,13 +390,12 @@ namespace Serein.NodeFlow.Env
}
var ft = new FlowTask();
ft.GetNodes = () => NodeModels.Values.Where(node => node.CanvasDetails.Guid.Equals(guid)).ToList();
- var startNodeModel = NodeModels.GetValueOrDefault(canvasModel.StartNode);
- if(startNodeModel is null)
+ if (canvasModel.StartNode.Guid is null)
{
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在起始节点,将停止运行。{guid}");
return false;
}
- ft.GetStartNode = () => startNodeModel;
+ ft.GetStartNode = () => canvasModel.StartNode;
flowTasks.Add(guid, ft);
}
#endregion
@@ -841,7 +841,7 @@ namespace Serein.NodeFlow.Env
var model = new FlowCanvasDetails(this);
model.LoadInfo(info);
FlowCanvass.Add(model.Guid, model);
- UIContextOperation.Invoke(() =>
+ UIContextOperation?.Invoke(() =>
{
OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model));
});
@@ -868,7 +868,7 @@ namespace Serein.NodeFlow.Env
}
if (FlowCanvass.Remove(canvasGuid))
{
- UIContextOperation.Invoke(() =>
+ UIContextOperation?.Invoke(() =>
{
OnCanvasRemove.Invoke(new CanvasRemoveEventArgs(canvasGuid));
});
@@ -878,6 +878,60 @@ namespace Serein.NodeFlow.Env
return false;
}
+ ///
+ /// 从节点信息创建节点,并返回状态指示是否创建成功
+ ///
+ ///
+ ///
+ private bool CreateNodeFromNodeInfo(NodeInfo nodeInfo)
+ {
+ if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType))
+ {
+ return false;
+ }
+
+ #region 获取方法描述
+ MethodDetails? methodDetails;
+ if (controlType.IsBaseNode())
+ {
+ // 加载基础节点
+ methodDetails = new MethodDetails();
+ }
+ else
+ {
+ if (string.IsNullOrEmpty(nodeInfo.MethodName)) return false;
+ // 加载方法节点
+ FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息
+ }
+ #endregion
+
+ var nodeModel = FlowNodeExtension.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
+ if (nodeModel is null)
+ {
+ nodeInfo.Guid = string.Empty;
+ return false;
+ }
+ if (FlowCanvass.TryGetValue(nodeInfo.CanvasGuid, out var canvasModel))
+ {
+
+ // 节点与画布互相绑定
+ // 需要在UI线程上进行添加,否则会报 “不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改”异常
+ nodeModel.CanvasDetails = canvasModel;
+ UIContextOperation?.Invoke(() => canvasModel.Nodes.Add(nodeModel));
+
+ nodeModel.LoadInfo(nodeInfo); // 创建节点model
+ TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
+ }
+ else
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, $"加载节点[{nodeInfo.Guid}]时发生异常,画布[{nodeInfo.CanvasGuid}]不存在");
+ return false;
+ }
+
+ UIContextOperation?.Invoke(() =>
+ OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上
+ return true;
+ }
///
@@ -885,57 +939,37 @@ namespace Serein.NodeFlow.Env
///
/// 节点信息
///
+ ///
public async Task LoadNodeInfosAsync(List nodeInfos)
{
#region 从NodeInfo创建NodeModel
+ // 流程接口节点最后才创建
+
+ List flowCallNodeInfos = [];
foreach (NodeInfo? nodeInfo in nodeInfos)
{
-
- if (!EnumHelper.TryConvertEnum(nodeInfo.Type, out var controlType))
+ if(nodeInfo.Type == nameof(NodeControlType.FlowCall))
{
- continue;
- }
-
- #region 获取方法描述
- MethodDetails? methodDetails;
- if (controlType.IsBaseNode())
- {
- // 加载基础节点
- methodDetails = new MethodDetails();
+ flowCallNodeInfos.Add(nodeInfo);
}
else
{
- if (string.IsNullOrEmpty(nodeInfo.MethodName)) continue;
- // 加载方法节点
- FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName, nodeInfo.MethodName, out methodDetails); // 加载项目时尝试获取方法信息
- }
- #endregion
+ if (!CreateNodeFromNodeInfo(nodeInfo))
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}");
+ continue;
+ }
+ }
+ }
- var nodeModel = FlowNodeExtension.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
- if (nodeModel is null)
+ // 创建流程接口节点
+ foreach (NodeInfo? nodeInfo in flowCallNodeInfos)
+ {
+ if (!CreateNodeFromNodeInfo(nodeInfo))
{
- nodeInfo.Guid = string.Empty;
+ SereinEnv.WriteLine(InfoType.WARN, $"节点创建失败。{Environment.NewLine}{nodeInfo}");
continue;
}
- if(FlowCanvass.TryGetValue(nodeInfo.CanvasGuid, out var canvasModel))
- {
-
- // 节点与画布互相绑定
- // 需要在UI线程上进行添加,否则会报 “不支持从调度程序线程以外的线程对其 SourceCollection 进行的更改”异常
- nodeModel.CanvasDetails = canvasModel;
- UIContextOperation.Invoke(() => canvasModel.Nodes.Add(nodeModel));
-
- nodeModel.LoadInfo(nodeInfo); // 创建节点model
- TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
- }
- else
- {
- SereinEnv.WriteLine(InfoType.ERROR, $"加载节点[{nodeInfo.Guid}]时发生异常,画布[{nodeInfo.CanvasGuid}]不存在");
- return;
- }
-
- UIContextOperation.Invoke(() =>
- OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeInfo.CanvasGuid, nodeModel, nodeInfo.Position))); // 添加到UI上
}
#endregion
@@ -960,7 +994,7 @@ namespace Serein.NodeFlow.Env
var result = nodeContainer.PlaceNode(nodeModel);
if (result)
{
- UIContextOperation.Invoke(() => OnNodePlace?.Invoke(
+ UIContextOperation?.Invoke(() => OnNodePlace?.Invoke(
new NodePlaceEventArgs(nodeInfo.CanvasGuid, nodeModel.Guid, containerNode.Guid)));
}
@@ -1044,7 +1078,7 @@ namespace Serein.NodeFlow.Env
#endregion
- UIContextOperation.Invoke(() =>
+ UIContextOperation?.Invoke(() =>
{
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs());
});
@@ -1136,7 +1170,7 @@ namespace Serein.NodeFlow.Env
var result = nodeContainer.PlaceNode(nodeModel); // 放置在容器节点
if (result)
{
- UIContextOperation.Invoke(() =>
+ UIContextOperation?.Invoke(() =>
{
OnNodePlace?.Invoke(new NodePlaceEventArgs(canvasGuid, nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置
});
@@ -1168,7 +1202,7 @@ namespace Serein.NodeFlow.Env
var result = nodeContainer.TakeOutNode(nodeModel); // 从容器节点取出
if (result)
{
- UIContextOperation.Invoke(() =>
+ UIContextOperation?.Invoke(() =>
{
OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(canvasGuid, nodeGuid)); // 重新放置在画布上
});
@@ -1223,17 +1257,26 @@ namespace Serein.NodeFlow.Env
}
}
- // 遍历所有后继节点,从那些后继节点中的前置节点集合移除该节点
- foreach (var snc in remoteNode.SuccessorNodes)
+
+ if(remoteNode.ControlType == NodeControlType.FlowCall)
{
- var connectionType = snc.Key; // 连接类型
- for (int i = 0; i < snc.Value.Count; i++)
+
+ }
+ else
+ {
+ // 遍历所有后继节点,从那些后继节点中的前置节点集合移除该节点
+ foreach (var snc in remoteNode.SuccessorNodes)
{
- NodeModelBase? toNode = snc.Value[i];
+ var connectionType = snc.Key; // 连接类型
+ for (int i = 0; i < snc.Value.Count; i++)
+ {
+ NodeModelBase? toNode = snc.Value[i];
- await RemoteConnectAsync(canvasGuid, remoteNode, toNode, connectionType);
+ await RemoteConnectAsync(canvasGuid, remoteNode, toNode, connectionType);
+ }
}
+
}
@@ -1484,10 +1527,10 @@ namespace Serein.NodeFlow.Env
{
if (!TryGetCanvasModel(canvasGuid, out var canvasModel) || !TryGetNodeModel(newNodeGuid, out var newStartNodeModel))
{
- return Task.FromResult(canvasModel.StartNode ?? string.Empty);
+ return Task.FromResult(string.Empty);
}
SetStartNode(canvasModel, newStartNodeModel);
- return Task.FromResult(canvasModel.StartNode ?? string.Empty);
+ return Task.FromResult(canvasModel.StartNode.Guid ?? string.Empty);
}
///
@@ -1743,7 +1786,7 @@ namespace Serein.NodeFlow.Env
if (OperatingSystem.IsWindows())
{
- UIContextOperation.Invoke(() => OnNodeConnectChange?.Invoke(
+ UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(
new NodeConnectChangeEventArgs(
canvasGuid,
fromNode.Guid,
@@ -1776,7 +1819,7 @@ namespace Serein.NodeFlow.Env
if (OperatingSystem.IsWindows())
{
- UIContextOperation.Invoke(() => OnNodeConnectChange?.Invoke(
+ UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(
new NodeConnectChangeEventArgs(
canvasGuid,
fromNode.Guid,
@@ -1887,6 +1930,12 @@ namespace Serein.NodeFlow.Env
/// 连接关系
private bool ConnectInvokeOfNode(string canvasGuid, NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType invokeType)
{
+ if (fromNode.ControlType == NodeControlType.FlowCall)
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, $"流程接口节点不可调用下一个节点。" +
+ $"{Environment.NewLine}流程节点:{fromNode.Guid}");
+ return false;
+ }
if (!FlowCanvass.ContainsKey(canvasGuid))
{
return false;
@@ -1907,7 +1956,8 @@ namespace Serein.NodeFlow.Env
{
flowTaskManagement?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被连接的是全局触发器,尝试移除
}
-
+ var isOverwriting = false;
+ ConnectionInvokeType overwritingCt = ConnectionInvokeType.None;
var isPass = false;
foreach (ConnectionInvokeType ctType in ct)
{
@@ -1917,21 +1967,33 @@ namespace Serein.NodeFlow.Env
FromExistInTo = ToOnF.Length > 0;
if (ToExistOnFrom && FromExistInTo)
{
- SereinEnv.WriteLine(InfoType.WARN, "起始节点已与目标节点存在连接");
- isPass = false;
+ if(ctType == invokeType)
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"起始节点已与目标节点存在连接。" +
+ $"{Environment.NewLine}起始节点:{fromNode.Guid}" +
+ $"{Environment.NewLine}目标节点:{toNode.Guid}");
+ return false;
+ }
+ isOverwriting = true;
+ overwritingCt = ctType;
}
else
{
// 检查是否可能存在异常
if (!ToExistOnFrom && FromExistInTo)
{
- SereinEnv.WriteLine(InfoType.WARN, "目标节点不是起始节点的子节点,起始节点却是目标节点的父节点");
+ SereinEnv.WriteLine(InfoType.ERROR, $"起始节点不是目标节点的父节点,目标节点却是起始节点的子节点。" +
+ $"{Environment.NewLine}起始节点:{fromNode.Guid}" +
+ $"{Environment.NewLine}目标节点:{toNode.Guid}");
isPass = false;
}
else if (ToExistOnFrom && !FromExistInTo)
{
//
- SereinEnv.WriteLine(InfoType.WARN, " 起始节点不是目标节点的父节点,目标节点却是起始节点的子节点");
+ SereinEnv.WriteLine(InfoType.ERROR, $"起始节点不是目标节点的父节点,目标节点却是起始节点的子节点。" +
+ $"{Environment.NewLine}起始节点:{fromNode.Guid}" +
+ $"{Environment.NewLine}目标节点:{toNode.Guid}" +
+ $"");
isPass = false;
}
else
@@ -1942,7 +2004,11 @@ namespace Serein.NodeFlow.Env
}
if (isPass)
{
-
+ if (isOverwriting) // 需要替换
+ {
+ fromNode.SuccessorNodes[overwritingCt].Remove(toNode); // 从起始节点子分支中移除
+ toNode.PreviousNodes[overwritingCt].Remove(fromNode); // 从目标节点父分支中移除
+ }
fromNode.SuccessorNodes[invokeType].Add(toNode); // 添加到起始节点的子分支
toNode.PreviousNodes[invokeType].Add(fromNode); // 添加到目标节点的父分支
if (OperatingSystem.IsWindows())
@@ -1991,13 +2057,38 @@ namespace Serein.NodeFlow.Env
}
var toNodeArgSourceGuid = toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid;
- if (!string.IsNullOrEmpty(toNodeArgSourceGuid))
+ var toNodeArgSourceType = toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType;
+ if(fromNode.Guid == toNodeArgSourceGuid && toNodeArgSourceType == connectionArgSourceType)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"节点之间已建立过连接关系,此次操作将不会执行" +
+ $"起始节点:{fromNode.Guid}" +
+ $"目标节点:{toNode.Guid}" +
+ $"参数索引:{argIndex}" +
+ $"参数类型:{connectionArgSourceType}");
+ UIContextOperation?.Invoke(() =>
+ OnNodeConnectChange?.Invoke(
+ new NodeConnectChangeEventArgs(
+ canvasGuid,
+ fromNode.Guid, // 从哪个节点开始
+ toNode.Guid, // 连接到那个节点
+ JunctionOfConnectionType.Arg,
+ argIndex, // 连接线的样式类型
+ connectionArgSourceType,
+ NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
+ ))); // 通知UI
+
+ return true;
+ }
+
+ if (!string.IsNullOrEmpty(toNodeArgSourceGuid) )
{
await RemoteConnectAsync(canvasGuid, fromNode, toNode, argIndex);
}
+
toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = fromNode.Guid;
toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = connectionArgSourceType;
- UIContextOperation.Invoke(() =>
+
+ UIContextOperation?.Invoke(() =>
OnNodeConnectChange?.Invoke(
new NodeConnectChangeEventArgs(
canvasGuid,
@@ -2019,15 +2110,14 @@ namespace Serein.NodeFlow.Env
/// 起始节点
private void SetStartNode(FlowCanvasDetails cavnasModel, NodeModelBase newStartNode)
{
- var oldNodeGuid = cavnasModel.StartNode;
+ var oldNodeGuid = cavnasModel.StartNode?.Guid;
/*if(TryGetNodeModel(oldNodeGuid, out var newStartNodeModel))
{
newStartNode.IsStart = false;
}*/
- cavnasModel.StartNode = newStartNode.Guid;
+ cavnasModel.StartNode = newStartNode;
//newStartNode.IsStart = true;
-
- UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode)));
+ UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode.Guid)));
}
diff --git a/NodeFlow/Model/SingleFlowCallNode.cs b/NodeFlow/Model/SingleFlowCallNode.cs
index a3435da..dcf5618 100644
--- a/NodeFlow/Model/SingleFlowCallNode.cs
+++ b/NodeFlow/Model/SingleFlowCallNode.cs
@@ -16,14 +16,17 @@ namespace Serein.NodeFlow.Model
[NodeProperty(ValuePath = NodeValuePath.Node)]
public partial class SingleFlowCallNode
{
-
-
+ ///
+ /// 目标公开节点
+ ///
+ [PropertyInfo(IsNotification = true)]
+ private string targetNodeGuid;
///
/// 使用目标节点的参数(如果为true,则使用目标节点的入参,如果为false,则使用节点自定义入参)
///
[PropertyInfo(IsNotification = true)]
- private bool _isShareParam = true ;
+ private bool _isShareParam ;
}
@@ -38,9 +41,13 @@ namespace Serein.NodeFlow.Model
///
private NodeModelBase targetNode;
///
+ /// 缓存的方法信息
+ ///
+ public MethodDetails CacheMethodDetails { get; private set; }
+ ///
/// 接口节点Guid
///
- public string? TargetNodeGuid => targetNode?.Guid;
+ //public string? TargetNodeGuid => targetNode?.Guid;
public SingleFlowCallNode(IFlowEnvironment environment) : base(environment)
@@ -48,43 +55,70 @@ namespace Serein.NodeFlow.Model
}
+
///
/// 重置接口节点
///
public void ResetTargetNode()
{
- if(targetNode is not null)
+ if (targetNode is not null)
{
// 取消接口
- targetNode.PropertyChanged -= TargetNode_PropertyChanged;
- this.MethodDetails = null;
- foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
- {
- this.SuccessorNodes[ctType] = new List();
- }
+ TargetNodeGuid = string.Empty;
}
-
-
}
///
- /// 设置接口节点(如果传入null,则视为取消设置)
+ /// 设置接口节点
///
- ///
- public void SetTargetNode(NodeModelBase? value)
+ ///
+ public void SetTargetNode(string? nodeGuid)
{
- if( value is null)
+ if (nodeGuid is null || !Env.TryGetNodeModel(nodeGuid, out _))
{
return;
}
- this.targetNode = value;
- tmpMethodDetails = targetNode.MethodDetails.CloneOfNode(this); // 从目标节点复制一份
- targetNode.PropertyChanged += TargetNode_PropertyChanged;
- this.MethodDetails = tmpMethodDetails;
- this.SuccessorNodes = targetNode.SuccessorNodes;
+ TargetNodeGuid = nodeGuid;
+ }
+
+ partial void OnTargetNodeGuidChanged(string value)
+ {
+ if (string.IsNullOrEmpty(value) || !Env.TryGetNodeModel(value, out targetNode))
+ {
+ // 取消设置接口节点
+ targetNode.PropertyChanged -= TargetNode_PropertyChanged;
+ this.MethodDetails = new MethodDetails();
+ /*foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
+ {
+ this.SuccessorNodes[ctType] = new List();
+ }*/
+ }
+ else
+ {
+ //if (this.MethodDetails.ActingInstanceType.FullName.Equals())
+
+ if(!this.IsShareParam
+ && CacheMethodDetails is not null
+ && targetNode.MethodDetails.AssemblyName.Equals(CacheMethodDetails.AssemblyName)
+ && targetNode.MethodDetails.MethodName.Equals(CacheMethodDetails.MethodName))
+ {
+ this.MethodDetails = CacheMethodDetails;
+ }
+ else
+ {
+ CacheMethodDetails = targetNode.MethodDetails.CloneOfNode(this); // 从目标节点复制一份
+ targetNode.PropertyChanged += TargetNode_PropertyChanged;
+ this.MethodDetails = CacheMethodDetails;
+ /*foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
+ {
+ this.SuccessorNodes[ctType] = targetNode.SuccessorNodes[ctType];
+ }*/
+ }
+
+ }
+ OnPropertyChanged(nameof(MethodDetails));
}
- private MethodDetails tmpMethodDetails;
partial void OnIsShareParamChanged(bool value)
{
if (targetNode is null)
@@ -93,40 +127,26 @@ namespace Serein.NodeFlow.Model
}
if (value)
{
- tmpMethodDetails = this.MethodDetails;
+ CacheMethodDetails = this.MethodDetails;
this.MethodDetails = targetNode.MethodDetails;
}
else
{
- this.MethodDetails = tmpMethodDetails;
- OnPropertyChanged(nameof(MethodDetails));
+ this.MethodDetails = CacheMethodDetails;
}
+
+ OnPropertyChanged(nameof(MethodDetails));
}
- /* partial void OnTargetNodeGuidChanged(string value)
- {
- var guid = value;
- if (string.IsNullOrEmpty(guid))
- {
- targetNode = null;
- return;
- }
- if (!Env.TryGetNodeModel(guid, out targetNode))
- {
- SereinEnv.WriteLine(InfoType.ERROR, $"流程接口找不到节点{guid}");
- return;
- }
- SetTargetNode(targetNode);
- //OnIsShareParamChanged(IsShareParam); // 更新参数状态
- }*/
-
-
private void TargetNode_PropertyChanged(object? sender, System.ComponentModel.PropertyChangedEventArgs e)
{
// 如果不再公开
if (sender is NodeModelBase node && !node.IsPublic)
{
- this.SuccessorNodes = [];
+ foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
+ {
+ this.SuccessorNodes[ctType] = [];
+ }
targetNode.PropertyChanged -= TargetNode_PropertyChanged;
}
}
@@ -168,7 +188,12 @@ namespace Serein.NodeFlow.Model
{
throw new ArgumentNullException();
}
- return await base.ExecutingAsync(context, token);
+ if (IsShareParam)
+ {
+ this.MethodDetails = targetNode.MethodDetails;
+ }
+ this.SuccessorNodes = targetNode.SuccessorNodes;
+ return await base.ExecutingAsync(context, token);
}
@@ -192,19 +217,30 @@ namespace Serein.NodeFlow.Model
///
public override void LoadCustomData(NodeInfo nodeInfo)
{
+ CacheMethodDetails = this.MethodDetails; // 缓存
string targetNodeGuid = nodeInfo.CustomData?.TargetNodeGuid ?? "";
this.IsShareParam = nodeInfo.CustomData?.IsShareParam;
if (Env.TryGetNodeModel(targetNodeGuid, out var targetNode))
{
+ TargetNodeGuid = targetNode.Guid;
this.targetNode = targetNode;
}
else
{
SereinEnv.WriteLine(InfoType.ERROR, $"流程接口节点[{this.Guid}]无法找到对应的节点:{targetNodeGuid}");
}
+
}
+ public override void Remove()
+ {
+ var tmp = this;
+ targetNode = null;
+ CacheMethodDetails = null;
+
+ }
+
}
}
\ No newline at end of file
diff --git a/Workbench/App.xaml.cs b/Workbench/App.xaml.cs
index 4bbcf63..8f9ab04 100644
--- a/Workbench/App.xaml.cs
+++ b/Workbench/App.xaml.cs
@@ -61,6 +61,7 @@ namespace Serein.Workbench
{
getSyncContext = () => uiContext;
}
+
});
UIContextOperation? uIContextOperation = null;
@@ -84,6 +85,12 @@ namespace Serein.Workbench
public partial class App : Application
{
private static IServiceProvider? ServiceProvider;
+
+ ///
+ /// UI线程
+ ///
+ public static UIContextOperation UIContextOperation => App.GetService() ?? throw new NullReferenceException();
+
public static T GetService() where T : class
{
return ServiceProvider?.GetService() ?? throw new NullReferenceException();
@@ -111,7 +118,7 @@ namespace Serein.Workbench
{
await Task.Delay(500);
#if DEBUG
- if (1 ==1)
+ if (1 == 1)
{
// 这里是测试代码,可以删除
string filePath;
diff --git a/Workbench/Node/Junction/ConnectionLineShape.cs b/Workbench/Node/Junction/ConnectionLineShape.cs
index 122adaf..c573dbe 100644
--- a/Workbench/Node/Junction/ConnectionLineShape.cs
+++ b/Workbench/Node/Junction/ConnectionLineShape.cs
@@ -60,7 +60,15 @@ namespace Serein.Workbench.Node.View
endPoint = end;
this.strokeThickness = 4;
InitElementPoint(isDotted, isTop);
- InvalidateVisual(); // 触发重绘
+
+ _ = Task.Run(async () =>
+ {
+ await App.UIContextOperation.InvokeAsync(() =>
+ {
+ InvalidateVisual(); // 触发重绘
+ });
+ });
+
}
diff --git a/Workbench/Node/NodeControlBase.cs b/Workbench/Node/NodeControlBase.cs
index a4b77fd..f72b9a9 100644
--- a/Workbench/Node/NodeControlBase.cs
+++ b/Workbench/Node/NodeControlBase.cs
@@ -1,7 +1,9 @@
-using Serein.Library;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Serein.Library;
using Serein.Library.Api;
using Serein.Workbench.Api;
using Serein.Workbench.Node.ViewModel;
+using Serein.Workbench.Themes;
using Serein.Workbench.Views;
using System.Windows;
using System.Windows.Controls;
@@ -146,7 +148,7 @@ namespace Serein.Workbench.Node.View
///
///
///
- protected T FindVisualChild(DependencyObject parent) where T : DependencyObject
+ protected static T FindVisualChild(DependencyObject parent) where T : DependencyObject
{
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
{
@@ -165,13 +167,58 @@ namespace Serein.Workbench.Node.View
return null;
}
+ protected static JunctionControlBase[] GetArgJunction(NodeControlBase nodeControl, MethodDetailsControl methodDetailsControl)
+ {
+ // 获取 MethodDetailsControl 实例
+ try
+ {
+ var itemsControl = FindVisualChild(methodDetailsControl); // 查找 ItemsControl
+ if (itemsControl != null)
+ {
+ var md = nodeControl.ViewModel.NodeModel.MethodDetails;
+ if (md is null)
+ {
+ return [];
+ }
+ if(md.ParameterDetailss is null)
+ {
+ return [];
+ }
+ var argDataJunction = new JunctionControlBase[md.ParameterDetailss.Length];
+ var controls = new List();
+
+ for (int i = 0; i < itemsControl.Items.Count; i++)
+ {
+ var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
+ if (container != null)
+ {
+ var argControl = FindVisualChild(container);
+ if (argControl != null)
+ {
+ controls.Add(argControl); // 收集 ArgJunctionControl 实例
+ }
+ }
+ }
+ return argDataJunction = controls.ToArray();
+ }
+ return [];
+ }
+ catch (Exception ex)
+ {
+
+ SereinEnv.WriteLine(InfoType.ERROR,$"节点获取入参控制点时发生异常{Environment.NewLine}节点:{nodeControl.ViewModel.NodeModel.Guid}");
+ SereinEnv.WriteLine(ex);
+ return [];
+ }
+ }
+
}
-
+
//public class FLowNodeObObservableCollection : ObservableCollection
//{
diff --git a/Workbench/Node/View/ActionNodeControl.xaml.cs b/Workbench/Node/View/ActionNodeControl.xaml.cs
index ec69362..5cfa4f0 100644
--- a/Workbench/Node/View/ActionNodeControl.xaml.cs
+++ b/Workbench/Node/View/ActionNodeControl.xaml.cs
@@ -1,5 +1,7 @@
-using Serein.NodeFlow.Model;
+using Serein.Library;
+using Serein.NodeFlow.Model;
using Serein.Workbench.Node.ViewModel;
+using Serein.Workbench.Themes;
using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Controls;
@@ -42,35 +44,7 @@ namespace Serein.Workbench.Node.View
///
/// 方法入参控制点(可能有,可能没)
///
- JunctionControlBase[] INodeJunction.ArgDataJunction => GetArgJunction();
-
- private JunctionControlBase[] GetArgJunction()
- {
- // 获取 MethodDetailsControl 实例
- var methodDetailsControl = this.MethodDetailsControl;
- var itemsControl = FindVisualChild(methodDetailsControl); // 查找 ItemsControl
- if (itemsControl != null)
- {
- var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
- var controls = new List();
-
- for (int i = 0; i < itemsControl.Items.Count; i++)
- {
- var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
- if (container != null)
- {
- var argControl = FindVisualChild(container);
- if (argControl != null)
- {
- controls.Add(argControl); // 收集 ArgJunctionControl 实例
- }
- }
- }
- return argDataJunction = controls.ToArray();
- }
- return [];
- }
-
+ JunctionControlBase[] INodeJunction.ArgDataJunction => GetArgJunction(this, MethodDetailsControl);
}
}
diff --git a/Workbench/Node/View/ConnectionControl.cs b/Workbench/Node/View/ConnectionControl.cs
index 5eacfb9..cda5603 100644
--- a/Workbench/Node/View/ConnectionControl.cs
+++ b/Workbench/Node/View/ConnectionControl.cs
@@ -196,7 +196,7 @@ namespace Serein.Workbench.Node.View
{
leftCenterOfEndLocation = Start.MyCenterPoint;
rightCenterOfStartLocation = End.MyCenterPoint;
-
+
(Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End);
var connectionType = Start.JunctionType.ToConnectyionType();
bool isDotted;
@@ -214,7 +214,7 @@ namespace Serein.Workbench.Node.View
BezierLine = new ConnectionLineShape(LineType, startPoint, endPoint, brush, isDotted);
Grid.SetZIndex(BezierLine, -9999999); // 置底
Canvas.Children.Add(BezierLine);
-
+
ConfigureLineContextMenu(); //配置右键菜单
}
diff --git a/Workbench/Node/View/ExpOpNodeControl.xaml.cs b/Workbench/Node/View/ExpOpNodeControl.xaml.cs
index 4a96d69..bfd7369 100644
--- a/Workbench/Node/View/ExpOpNodeControl.xaml.cs
+++ b/Workbench/Node/View/ExpOpNodeControl.xaml.cs
@@ -43,6 +43,7 @@ namespace Serein.Workbench.Node.View
/// 方法入参控制点(可能有,可能没)
///
private JunctionControlBase[] argDataJunction;
+
///
/// 方法入参控制点(可能有,可能没)
///
diff --git a/Workbench/Node/View/FlipflopNodeControl.xaml.cs b/Workbench/Node/View/FlipflopNodeControl.xaml.cs
index b2d68e8..f9c1e73 100644
--- a/Workbench/Node/View/FlipflopNodeControl.xaml.cs
+++ b/Workbench/Node/View/FlipflopNodeControl.xaml.cs
@@ -34,34 +34,7 @@ namespace Serein.Workbench.Node.View
///
/// 方法入参控制点(可能有,可能没)
///
- JunctionControlBase[] INodeJunction.ArgDataJunction => GetArgJunction();
-
- private JunctionControlBase[] GetArgJunction()
- {
- // 获取 MethodDetailsControl 实例
- var methodDetailsControl = this.MethodDetailsControl;
- var itemsControl = FindVisualChild(methodDetailsControl); // 查找 ItemsControl
- if (itemsControl != null)
- {
- var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
- var controls = new List();
-
- for (int i = 0; i < itemsControl.Items.Count; i++)
- {
- var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
- if (container != null)
- {
- var argControl = FindVisualChild(container);
- if (argControl != null)
- {
- controls.Add(argControl); // 收集 ArgJunctionControl 实例
- }
- }
- }
- return argDataJunction = controls.ToArray();
- }
- return [];
- }
+ JunctionControlBase[] INodeJunction.ArgDataJunction => GetArgJunction(this, MethodDetailsControl);
}
}
diff --git a/Workbench/Node/View/FlowCallNodeControl.xaml.cs b/Workbench/Node/View/FlowCallNodeControl.xaml.cs
index 33c1923..bf726f2 100644
--- a/Workbench/Node/View/FlowCallNodeControl.xaml.cs
+++ b/Workbench/Node/View/FlowCallNodeControl.xaml.cs
@@ -55,36 +55,9 @@ namespace Serein.Workbench.Node.View
///
/// 方法入参控制点(可能有,可能没)
///
- JunctionControlBase[] INodeJunction.ArgDataJunction => GetArgJunction();
-
- private JunctionControlBase[] GetArgJunction()
- {
- // 获取 MethodDetailsControl 实例
- //var methodDetailsControl = ViewModel.NodeModel.IsShareParam ? this.SelectMethodDetailsControl : this.MyMethodDetailsControl;
- var methodDetailsControl = this.MethodDetailsControl;
- var itemsControl = FindVisualChild(methodDetailsControl); // 查找 ItemsControl
- if (itemsControl != null)
- {
- var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
- var controls = new List();
-
- for (int i = 0; i < itemsControl.Items.Count; i++)
- {
- var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
- if (container != null)
- {
- var argControl = FindVisualChild(container);
- if (argControl != null)
- {
- controls.Add(argControl); // 收集 ArgJunctionControl 实例
- }
- }
- }
- return argDataJunction = controls.ToArray();
- }
- return [];
- }
+ JunctionControlBase[] INodeJunction.ArgDataJunction => GetArgJunction(this,MethodDetailsControl);
+
}
}
diff --git a/Workbench/Node/View/UINodeControl.xaml b/Workbench/Node/View/UINodeControl.xaml
index b7b8318..3c3621a 100644
--- a/Workbench/Node/View/UINodeControl.xaml
+++ b/Workbench/Node/View/UINodeControl.xaml
@@ -32,8 +32,9 @@
-
+
+
+
diff --git a/Workbench/Node/View/UINodeControl.xaml.cs b/Workbench/Node/View/UINodeControl.xaml.cs
index 4d7beac..a7fa9e1 100644
--- a/Workbench/Node/View/UINodeControl.xaml.cs
+++ b/Workbench/Node/View/UINodeControl.xaml.cs
@@ -22,6 +22,7 @@ namespace Serein.Workbench.Node.View
///
public partial class UINodeControl : NodeControlBase, INodeJunction
{
+ private new UINodeControlViewModel ViewModel { get; }
public UINodeControl()
{
base.ViewModel.IsEnabledOnView = true;
@@ -30,6 +31,7 @@ namespace Serein.Workbench.Node.View
public UINodeControl(UINodeControlViewModel viewModel) : base(viewModel)
{
+ ViewModel = viewModel;
DataContext = viewModel;
InitializeComponent();
@@ -49,8 +51,8 @@ namespace Serein.Workbench.Node.View
private void NodeControlBase_Loaded(object sender, RoutedEventArgs e)
{
- UINodeControlViewModel vm = (UINodeControlViewModel)DataContext;
- vm.InitAdapter(userControl => {
+ //ViewModel.InitAdapter();
+ ViewModel.InitAdapter(userControl => {
EmbedContainer.Child = userControl;
});
diff --git a/Workbench/Node/ViewModel/FlowCallNodeControlViewModel.cs b/Workbench/Node/ViewModel/FlowCallNodeControlViewModel.cs
index c3c783e..cbb74da 100644
--- a/Workbench/Node/ViewModel/FlowCallNodeControlViewModel.cs
+++ b/Workbench/Node/ViewModel/FlowCallNodeControlViewModel.cs
@@ -68,10 +68,9 @@ namespace Serein.Workbench.Node.ViewModel
{
return;
}
- if (targetNodeControl.FlowCanvas is FlowCanvasView view
- && view.DataContext is FlowCanvasViewModel viewModel)
+ if (targetNodeControl.FlowCanvas is FlowCanvasView view )
{
- SelectCanvas = viewModel;
+ SelectCanvas = view.ViewModel;
SelectNode = targetNodeControl.ViewModel.NodeModel;
}
}
@@ -80,8 +79,10 @@ namespace Serein.Workbench.Node.ViewModel
{
flowEEForwardingService.OnCanvasCreate += (e) => RershCanvass(); // 画布创建了
flowEEForwardingService.OnCanvasRemove += (e) => RershCanvass(); // 画布移除了
+
}
+
partial void OnSelectCanvasChanged(FlowCanvasViewModel value)
{
FlowCallNode.ResetTargetNode();
@@ -89,7 +90,12 @@ namespace Serein.Workbench.Node.ViewModel
partial void OnSelectNodeChanged(NodeModelBase value)
{
- FlowCallNode.SetTargetNode(value);
+ if(value is null)
+ {
+ FlowCallNode.ResetTargetNode();
+ return;
+ }
+ FlowCallNode.SetTargetNode(value.Guid);
}
private void RershCanvass()
diff --git a/Workbench/Node/ViewModel/UINodeControlViewModel.cs b/Workbench/Node/ViewModel/UINodeControlViewModel.cs
index e7a9b3b..81f7890 100644
--- a/Workbench/Node/ViewModel/UINodeControlViewModel.cs
+++ b/Workbench/Node/ViewModel/UINodeControlViewModel.cs
@@ -1,4 +1,5 @@
-using Serein.Library;
+using CommunityToolkit.Mvvm.ComponentModel;
+using Serein.Library;
using Serein.Library.Api;
using Serein.NodeFlow.Model;
using System;
@@ -10,17 +11,24 @@ using System.Windows.Controls;
namespace Serein.Workbench.Node.ViewModel
{
- public class UINodeControlViewModel : NodeControlViewModelBase
+ public partial class UINodeControlViewModel : NodeControlViewModelBase
{
private SingleUINode NodeModel => (SingleUINode)base.NodeModel;
//public IEmbeddedContent Adapter => NodeModel.Adapter;
+ ///
+ /// 节点UI的对应内容
+ ///
+ [ObservableProperty]
+ private UserControl _nodeUIContent;
+
+
public UINodeControlViewModel(NodeModelBase nodeModel) : base(nodeModel)
{
- //NodeModel.Adapter.GetWindowHandle();
+
}
- public void InitAdapter(Action setUIDisplayHandle)
+ public void InitAdapter()
{
Task.Factory.StartNew(async () =>
{
@@ -32,11 +40,32 @@ namespace Serein.Workbench.Node.ViewModel
&& NodeModel.Adapter.GetUserControl() is UserControl userControl)
{
NodeModel.Env.UIContextOperation.Invoke(() =>
+ {
+ NodeUIContent = userControl;
+ });
+ }
+ });
+ }
+
+
+ public void InitAdapter(Action setUIDisplayHandle)
+ {
+ Task.Factory.StartNew(async () =>
+ {
+ var context = new DynamicContext(NodeModel.Env);
+ var cts = new CancellationTokenSource();
+ var result = await NodeModel.ExecutingAsync(context, cts.Token);
+ cts?.Dispose();
+ if (context.NextOrientation == ConnectionInvokeType.IsSucceed
+ && NodeModel.Adapter.GetUserControl() is UserControl userControl)
+ {
+ NodeModel.Env.UIContextOperation.Invoke(() =>
{
setUIDisplayHandle.Invoke(userControl);
});
}
});
}
+
}
}
diff --git a/Workbench/Services/FlowEEForwardingService.cs b/Workbench/Services/FlowEEForwardingService.cs
index 13d6118..2bfe3f5 100644
--- a/Workbench/Services/FlowEEForwardingService.cs
+++ b/Workbench/Services/FlowEEForwardingService.cs
@@ -13,6 +13,7 @@ using Serein.Library;
using Serein.Library.Utils;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Api;
+using System.Diagnostics;
namespace Serein.Workbench.Services
{
diff --git a/Workbench/Services/FlowNodeService.cs b/Workbench/Services/FlowNodeService.cs
index c3c25dd..362abe6 100644
--- a/Workbench/Services/FlowNodeService.cs
+++ b/Workbench/Services/FlowNodeService.cs
@@ -8,7 +8,9 @@ using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel;
using Serein.Workbench.ViewModels;
using Serein.Workbench.Views;
+using System;
using System.Text;
+using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
@@ -210,7 +212,9 @@ namespace Serein.Workbench.Services
(JunctionOfConnectionType.Arg, NodeConnectChangeEventArgs.ConnectChangeType.Remove) => () => flow.RemoveArgConnection(fromNode, toNode, e.ArgIndex), // 移除节点之间的参数传递关系
_ => null
};
+
action?.Invoke();
+
return;
}
diff --git a/Workbench/ViewModels/CanvasNodeTreeViewModel.cs b/Workbench/ViewModels/CanvasNodeTreeViewModel.cs
index 26186a1..0155c38 100644
--- a/Workbench/ViewModels/CanvasNodeTreeViewModel.cs
+++ b/Workbench/ViewModels/CanvasNodeTreeViewModel.cs
@@ -32,10 +32,7 @@ namespace Serein.Workbench.ViewModels
///
private void OnViewCanvasChanged(FlowCanvasView flowCanvas)
{
- if (flowCanvas.DataContext is FlowCanvasViewModel vm)
- {
- Model = vm.Model;
- }
+ Model = flowCanvas.ViewModel.Model;
}
}
}
diff --git a/Workbench/ViewModels/FlowCanvasViewModel.cs b/Workbench/ViewModels/FlowCanvasViewModel.cs
index be49fe7..bcaedb4 100644
--- a/Workbench/ViewModels/FlowCanvasViewModel.cs
+++ b/Workbench/ViewModels/FlowCanvasViewModel.cs
@@ -17,7 +17,6 @@ namespace Serein.Workbench.ViewModels
{
public partial class FlowCanvasViewModel : ObservableObject
{
-
///
/// 画布当前的节点
///
diff --git a/Workbench/ViewModels/FlowEditViewModel.cs b/Workbench/ViewModels/FlowEditViewModel.cs
index f993307..502ea71 100644
--- a/Workbench/ViewModels/FlowEditViewModel.cs
+++ b/Workbench/ViewModels/FlowEditViewModel.cs
@@ -43,16 +43,16 @@ namespace Serein.Workbench.ViewModels
flowNodeService.OnCreateFlowCanvasView += OnCreateFlowCanvasView; // 创建了画布
flowNodeService.OnRemoveFlowCanvasView += OnRemoveFlowCanvasView; // 移除了画布
- this.PropertyChanged += OnPropertyChanged;
+ //this.PropertyChanged += OnPropertyChanged;
}
- private void OnPropertyChanged(object? value, PropertyChangedEventArgs e)
+ partial void OnSelectedTabChanged(FlowEditorTabModel value)
{
- if (this.SelectedTab is null) return;
- flowNodeService.CurrentSelectCanvas = this.SelectedTab.Content;
+ flowNodeService.CurrentSelectCanvas = value.Content;
}
+
#region 响应环境事件
private void OnCreateFlowCanvasView(FlowCanvasView canvas)
{
diff --git a/Workbench/Views/FlowCanvasView.xaml.cs b/Workbench/Views/FlowCanvasView.xaml.cs
index d692678..7deaf60 100644
--- a/Workbench/Views/FlowCanvasView.xaml.cs
+++ b/Workbench/Views/FlowCanvasView.xaml.cs
@@ -52,187 +52,17 @@ namespace Serein.Workbench.Views
private readonly IFlowEnvironment flowEnvironment;
private readonly IKeyEventService keyEventService;
private readonly FlowNodeService flowNodeService;
+ private readonly IFlowEEForwardingService flowEEForwardingService;
///
/// 存储所有的连接。考虑集成在运行环境中。
///
private List Connections { get; } = [];
+ ///
+ /// 画布模型
+ ///
+ public FlowCanvasViewModel ViewModel => this.DataContext as FlowCanvasViewModel ?? throw new ArgumentNullException();
- private FlowCanvasViewModel ViewModel => this.DataContext as FlowCanvasViewModel ?? throw new ArgumentNullException();
-
- #region 画布接口实现
- private IFlowCanvas Api => this;
-
- public string Guid
- {
- get
- {
- return ViewModel.Model.Guid;
- }
- }
-
- public string Name => ViewModel.Model.Name;
- FlowCanvasDetails IFlowCanvas.Model => ViewModel.Model;
- void IFlowCanvas.Remove(NodeControlBase nodeControl)
- {
- ViewModel.NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid);
- FlowChartCanvas.Dispatcher.Invoke(() =>
- {
- FlowChartCanvas.Children.Remove(nodeControl);
- });
- }
- void IFlowCanvas.Add(NodeControlBase nodeControl)
- {
- ViewModel.NodeControls.TryAdd(nodeControl.ViewModel.NodeModel.Guid, nodeControl);
- FlowChartCanvas.Dispatcher.Invoke(() =>
- {
- FlowChartCanvas.Children.Add(nodeControl);
- if(nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.UI)
- {
- // 需要切换到对应画布,尽可能让UI线程获取到适配器
- var edit = App.GetService().FlowEditViewModel;
- var tab = edit.CanvasTabs.First(tab => tab.Content == this);
- App.GetService().FlowEditViewModel.SelectedTab = tab;
-
- }
- });
-
- ConfigureNodeEvents(nodeControl); // 配置相关事件
- ConfigureContextMenu(nodeControl); // 添加右键菜单
-
- }
-
- void IFlowCanvas.CreateInvokeConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, ConnectionInvokeType type)
- {
- if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
- {
- SereinEnv.WriteLine(InfoType.INFO, "非预期的连接");
- return;
- }
- JunctionControlBase startJunction = IFormJunction.NextStepJunction;
- JunctionControlBase endJunction = IToJunction.ExecuteJunction;
- var connection = new ConnectionControl(
- FlowChartCanvas,
- type,
- startJunction,
- endJunction
- );
-
- //if (toNodeControl is FlipflopNodeControl flipflopControl
- // && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
- //{
- // NodeTreeViewer.RemoveGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
- //}
- Connections.Add(connection);
- fromNodeControl.AddCnnection(connection);
- toNodeControl.AddCnnection(connection);
- EndConnection(); // 环境触发了创建节点连接事件
-
-
- }
- void IFlowCanvas.RemoveInvokeConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl)
- {
- if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
- {
- SereinEnv.WriteLine(InfoType.INFO, "非预期的连接");
- return;
- }
- JunctionControlBase startJunction = IFormJunction.NextStepJunction;
- JunctionControlBase endJunction = IToJunction.ExecuteJunction;
-
- var removeConnections = Connections.Where(c =>
- c.Start.Equals(startJunction)
- && c.End.Equals(endJunction)
- && (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); // 连接关系变更时判断
- //}
- }
- }
- void IFlowCanvas.CreateArgConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl,ConnectionArgSourceType type, int index)
- {
- if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
- {
- SereinEnv.WriteLine(InfoType.INFO, "非预期的情况");
- return;
- }
-
- JunctionControlBase startJunction = type switch
- {
- ConnectionArgSourceType.GetPreviousNodeData => IFormJunction.ReturnDataJunction, // 自身节点
- ConnectionArgSourceType.GetOtherNodeData => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
- ConnectionArgSourceType.GetOtherNodeDataOfInvoke => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
- _ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发
- };
-
- if (IToJunction.ArgDataJunction.Length <= index)
- {
- _ = Task.Run(async () =>
- {
- await Task.Delay(500);
- Api.CreateArgConnection(fromNodeControl, toNodeControl, type, index);
- });
- return; // // 尝试重新连接
- }
- JunctionControlBase endJunction = IToJunction.ArgDataJunction[index];
- LineType lineType = LineType.Bezier;
- // 添加连接
- var connection = new ConnectionControl(
- lineType,
- FlowChartCanvas,
- index,
- type,
- startJunction,
- endJunction,
- IToJunction
- );
- Connections.Add(connection);
- fromNodeControl.AddCnnection(connection);
- toNodeControl.AddCnnection(connection);
- EndConnection(); // 环境触发了创建节点连接事件
- }
- void IFlowCanvas.RemoveArgConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, int index)
- {
- if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
- {
- SereinEnv.WriteLine(InfoType.INFO, "非预期的连接");
- return;
- }
- JunctionControlBase startJunction = IFormJunction.NextStepJunction;
- JunctionControlBase endJunction = IToJunction.ExecuteJunction;
-
- var removeConnections = Connections.Where(c =>
- c.Start.Equals(startJunction)
- && c.End.Equals(endJunction)
- && (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
#region 与画布相关的字段
@@ -301,8 +131,10 @@ namespace Serein.Workbench.Views
flowEnvironment = App.GetService();
flowNodeService = App.GetService();
keyEventService = App.GetService();
+ flowEEForwardingService = App.GetService();
flowNodeService.OnCreateNode += OnCreateNode;
- keyEventService.OnKeyDown += KeyEventService_OnKeyDown;
+ keyEventService.OnKeyDown += KeyEventService_OnKeyDown;
+ //flowEEForwardingService.OnProjectLoaded += FlowEEForwardingService_OnProjectLoaded;
// 缩放平移容器
canvasTransformGroup = new TransformGroup();
@@ -313,6 +145,13 @@ namespace Serein.Workbench.Views
FlowChartCanvas.RenderTransform = canvasTransformGroup;
SetBinding(model);
+
+
+ }
+
+ private void FlowEEForwardingService_OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
+ {
+ RefreshAllLine();
}
@@ -336,16 +175,18 @@ namespace Serein.Workbench.Views
}
+
///
/// 当前画布创建了节点
///
///
private void OnCreateNode(NodeControlBase nodeControl)
{
- if (!nodeControl.FlowCanvas.Guid.Equals(Guid))
- {
- return;
- }
+ if (!nodeControl.FlowCanvas.Guid.Equals(Guid))
+ {
+ // 防止事件传播到其它画布
+ return;
+ }
var p = nodeControl.ViewModel.NodeModel.Position;
PositionOfUI position = new PositionOfUI(p.X, p.Y);
if (TryPlaceNodeInRegion(nodeControl, position, out var regionControl)) // 判断添加到区域容器
@@ -409,6 +250,188 @@ namespace Serein.Workbench.Views
#endregion
+ #region 画布接口实现
+ private IFlowCanvas Api => this;
+
+ public string Guid
+ {
+ get
+ {
+ return ViewModel.Model.Guid;
+ }
+ }
+
+ public string Name => ViewModel.Model.Name;
+ FlowCanvasDetails IFlowCanvas.Model => ViewModel.Model;
+ void IFlowCanvas.Remove(NodeControlBase nodeControl)
+ {
+ ViewModel.NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid);
+ FlowChartCanvas.Dispatcher.Invoke(() =>
+ {
+ FlowChartCanvas.Children.Remove(nodeControl);
+ });
+ }
+ void IFlowCanvas.Add(NodeControlBase nodeControl)
+ {
+
+ ViewModel.NodeControls.TryAdd(nodeControl.ViewModel.NodeModel.Guid, nodeControl);
+
+ FlowChartCanvas.Dispatcher.Invoke(() =>
+ {
+
+ FlowChartCanvas.Children.Add(nodeControl);
+
+ if (nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.UI)
+ {
+ // 需要切换到对应画布,尽可能让UI线程获取到适配器
+ var edit = App.GetService().FlowEditViewModel;
+ var tab = edit.CanvasTabs.First(tab => tab.Content == this);
+ App.GetService().FlowEditViewModel.SelectedTab = tab;
+
+ }
+ });
+ ConfigureNodeEvents(nodeControl); // 配置相关事件
+ ConfigureContextMenu(nodeControl); // 添加右键菜单
+
+ }
+
+ void IFlowCanvas.CreateInvokeConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, ConnectionInvokeType type)
+ {
+ if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, "非预期的连接");
+ return;
+ }
+ JunctionControlBase startJunction = IFormJunction.NextStepJunction;
+ JunctionControlBase endJunction = IToJunction.ExecuteJunction;
+
+
+ var connection = new ConnectionControl(
+ FlowChartCanvas,
+ type,
+ startJunction,
+ endJunction
+ );
+
+ //if (toNodeControl is FlipflopNodeControl flipflopControl
+ // && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
+ //{
+ // NodeTreeViewer.RemoveGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
+ //}
+
+ Connections.Add(connection);
+ fromNodeControl.AddCnnection(connection);
+ toNodeControl.AddCnnection(connection);
+ EndConnection(); // 环境触发了创建节点连接事件
+
+
+ }
+ void IFlowCanvas.RemoveInvokeConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl)
+ {
+ if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, "非预期的连接");
+ return;
+ }
+ JunctionControlBase startJunction = IFormJunction.NextStepJunction;
+ JunctionControlBase endJunction = IToJunction.ExecuteJunction;
+
+ var removeConnections = Connections.Where(c =>
+ c.Start.Equals(startJunction)
+ && c.End.Equals(endJunction)
+ && (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); // 连接关系变更时判断
+ //}
+ }
+ }
+ void IFlowCanvas.CreateArgConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl,ConnectionArgSourceType type, int index)
+ {
+ if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, "非预期的情况");
+ return;
+ }
+
+ JunctionControlBase startJunction = type switch
+ {
+ ConnectionArgSourceType.GetPreviousNodeData => IFormJunction.ReturnDataJunction, // 自身节点
+ ConnectionArgSourceType.GetOtherNodeData => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
+ ConnectionArgSourceType.GetOtherNodeDataOfInvoke => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
+ _ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发
+ };
+
+ if (IToJunction.ArgDataJunction.Length <= index)
+ {
+ _ = Task.Run(async () =>
+ {
+ await Task.Delay(100);
+ await App.UIContextOperation.InvokeAsync(() => {
+ Api.CreateArgConnection(fromNodeControl, toNodeControl, type, index);
+ });
+ });
+ return; // // 尝试重新连接
+ }
+ JunctionControlBase endJunction = IToJunction.ArgDataJunction[index];
+ LineType lineType = LineType.Bezier;
+ // 添加连接
+ var connection = new ConnectionControl(
+ lineType,
+ FlowChartCanvas,
+ index,
+ type,
+ startJunction,
+ endJunction,
+ IToJunction
+ );
+ Connections.Add(connection);
+ fromNodeControl.AddCnnection(connection);
+ toNodeControl.AddCnnection(connection);
+ EndConnection(); // 环境触发了创建节点连接事件
+ }
+ void IFlowCanvas.RemoveArgConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, int index)
+ {
+ if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, "非预期的连接");
+ return;
+ }
+ JunctionControlBase startJunction = IFormJunction.NextStepJunction;
+ JunctionControlBase endJunction = IToJunction.ExecuteJunction;
+
+ var removeConnections = Connections.Where(c =>
+ c.Start.Equals(startJunction)
+ && c.End.Equals(endJunction)
+ && (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
+
#region 画布键盘操作
///
/// 监听按键事件
@@ -449,7 +472,7 @@ namespace Serein.Workbench.Views
IsCanvasDragging = false;
SelectionRectangle.Visibility = Visibility.Collapsed;
CancelSelectNode();
- EndConnection();
+ EndConnection(); // Esc 按键 退出连线状态
return;
}
@@ -464,7 +487,6 @@ namespace Serein.Workbench.Views
}
catch
{
- Clipboard.SetText(text);
return;
}
}
@@ -590,10 +612,7 @@ namespace Serein.Workbench.Views
startCanvasDragPoint = currentMousePosition;
- foreach (var line in Connections)
- {
- line.RefreshLine(); // 画布移动时刷新所有连接线
- }
+ RefreshAllLine();
}
if (IsSelectControl) // 正在选取节点
@@ -807,7 +826,7 @@ namespace Serein.Workbench.Views
}
#endregion
}
- EndConnection();
+ EndConnection(); // 完成创建连线请求后取消连线
}
}
@@ -1012,6 +1031,17 @@ namespace Serein.Workbench.Views
#region 私有方法
+ ///
+ /// 刷新画布所有连线
+ ///
+ public void RefreshAllLine()
+ {
+ foreach (var line in Connections)
+ {
+ line.RefreshLine();
+ }
+ }
+
///
/// 完成选取操作
///
@@ -1088,9 +1118,9 @@ namespace Serein.Workbench.Views
nodeControl.BorderBrush = Brushes.Black;
nodeControl.BorderThickness = new Thickness(0);
- var startNodeGuid = ViewModel.Model.StartNode;
- var nodeGuid = nodeControl.ViewModel.NodeModel.Guid;
- if (startNodeGuid.Equals(nodeGuid))
+ var startNode = ViewModel.Model.StartNode;
+ var canvasStartNode = nodeControl.ViewModel.NodeModel;
+ if (canvasStartNode.Equals(startNode))
{
nodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"));
nodeControl.BorderThickness = new Thickness(2);
@@ -1300,6 +1330,10 @@ namespace Serein.Workbench.Views
///
private void ConfigureContextMenu(NodeControlBase nodeControl)
{
+ /*if(nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.UI)
+ {
+ return;
+ }*/
var canvasGuid = Guid;
var contextMenu = new ContextMenu();
var nodeGuid = nodeControl.ViewModel?.NodeModel?.Guid;
@@ -1412,21 +1446,7 @@ namespace Serein.Workbench.Views
#region 节点对齐 (有些小瑕疵)
- //public void UpdateConnectedLines()
- //{
- // //foreach (var nodeControl in selectNodeControls)
- // //{
- // // UpdateConnections(nodeControl);
- // //}
- // this.Dispatcher.Invoke(() =>
- // {
- // foreach (var line in Connections)
- // {
- // line.AddOrRefreshLine(); // 节点完成对齐
- // }
- // });
- //}
#region Plan A 群组对齐