mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-29 11:03:23 +08:00
实现了拖拽式设置方法调用顺序、方法入参参数来源
This commit is contained in:
@@ -30,6 +30,11 @@ namespace Serein.Library.Core.NodeFlow
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public RunState RunState { get; set; } = RunState.NoStart;
|
public RunState RunState { get; set; } = RunState.NoStart;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前节点执行完成后,设置该属性,让运行环境判断接下来要执行哪个分支的节点。
|
||||||
|
/// </summary>
|
||||||
|
public ConnectionInvokeType NextOrientation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 每个上下文分别存放节点的当前数据
|
/// 每个上下文分别存放节点的当前数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -28,6 +28,12 @@ namespace Serein.Library.Framework.NodeFlow
|
|||||||
/// 运行状态
|
/// 运行状态
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public RunState RunState { get; set; } = RunState.NoStart;
|
public RunState RunState { get; set; } = RunState.NoStart;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当前节点执行完成后,设置该属性,让运行环境判断接下来要执行哪个分支的节点。
|
||||||
|
/// </summary>
|
||||||
|
public ConnectionInvokeType NextOrientation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 每个上下文分别存放节点的当前数据
|
/// 每个上下文分别存放节点的当前数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -15,8 +15,16 @@ namespace Serein.Library.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
IFlowEnvironment Env { get; }
|
IFlowEnvironment Env { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否正在运行
|
||||||
|
/// </summary>
|
||||||
RunState RunState { get; }
|
RunState RunState { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 下一个要执行的节点
|
||||||
|
/// </summary>
|
||||||
|
ConnectionInvokeType NextOrientation { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取节点的数据(当前节点需要获取上一节点数据时,需要从 运行时上一节点 的Guid 通过这个方法进行获取
|
/// 获取节点的数据(当前节点需要获取上一节点数据时,需要从 运行时上一节点 的Guid 通过这个方法进行获取
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
|
|
||||||
|
|
||||||
using Serein.Library.Utils;
|
using Serein.Library.Utils;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
@@ -175,12 +176,33 @@ namespace Serein.Library.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
Remote,
|
Remote,
|
||||||
}
|
}
|
||||||
public NodeConnectChangeEventArgs(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType, ConnectChangeType changeType)
|
public NodeConnectChangeEventArgs(string fromNodeGuid,
|
||||||
|
string toNodeGuid,
|
||||||
|
JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线
|
||||||
|
ConnectionInvokeType connectionInvokeType, // 节点调用的方法类型(true/false/error/cancel )
|
||||||
|
ConnectChangeType changeType) // 需要创建连接线还是删除连接线
|
||||||
{
|
{
|
||||||
this.FromNodeGuid = fromNodeGuid;
|
this.FromNodeGuid = fromNodeGuid;
|
||||||
this.ToNodeGuid = toNodeGuid;
|
this.ToNodeGuid = toNodeGuid;
|
||||||
this.ConnectionType = connectionType;
|
this.ConnectionInvokeType = connectionInvokeType;
|
||||||
this.ChangeType = changeType;
|
this.ChangeType = changeType;
|
||||||
|
this.JunctionOfConnectionType = junctionOfConnectionType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeConnectChangeEventArgs(string fromNodeGuid,
|
||||||
|
string toNodeGuid,
|
||||||
|
JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线
|
||||||
|
int argIndex,
|
||||||
|
ConnectionArgSourceType connectionArgSourceType, // 节点对应的方法入参所需参数来源
|
||||||
|
ConnectChangeType changeType) // 需要创建连接线还是删除连接线
|
||||||
|
{
|
||||||
|
this.FromNodeGuid = fromNodeGuid;
|
||||||
|
this.ToNodeGuid = toNodeGuid;
|
||||||
|
this.ChangeType = changeType;
|
||||||
|
this.ArgIndex = argIndex;
|
||||||
|
this.ConnectionArgSourceType = connectionArgSourceType;
|
||||||
|
this.JunctionOfConnectionType = junctionOfConnectionType;
|
||||||
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接关系中始节点的Guid
|
/// 连接关系中始节点的Guid
|
||||||
@@ -193,11 +215,22 @@ namespace Serein.Library.Api
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接类型
|
/// 连接类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConnectionType ConnectionType { get; protected set; }
|
public ConnectionInvokeType ConnectionInvokeType { get; protected set; }
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示此次需要在两个节点之间创建连接关系,或是移除连接关系
|
/// 表示此次需要在两个节点之间创建连接关系,或是移除连接关系
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConnectChangeType ChangeType { get; protected set; }
|
public ConnectChangeType ChangeType { get; protected set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 指示需要创建什么类型的连接线
|
||||||
|
/// </summary>
|
||||||
|
public JunctionOfConnectionType JunctionOfConnectionType { get; protected set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 节点对应的方法入参所需参数来源
|
||||||
|
/// </summary>
|
||||||
|
public ConnectionArgSourceType ConnectionArgSourceType { get; protected set; }
|
||||||
|
public int ArgIndex { get; protected set; }
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -639,6 +672,13 @@ namespace Serein.Library.Api
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task StartAsyncInSelectNode(string startNodeGuid);
|
Task StartAsyncInSelectNode(string startNodeGuid);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 立刻调用某个节点,并获取其返回值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeGuid">节点Guid</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<object> InvokeNodeAsync(string nodeGuid);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 结束运行
|
/// 结束运行
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -663,8 +703,16 @@ namespace Serein.Library.Api
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fromNodeGuid">起始节点Guid</param>
|
/// <param name="fromNodeGuid">起始节点Guid</param>
|
||||||
/// <param name="toNodeGuid">目标节点Guid</param>
|
/// <param name="toNodeGuid">目标节点Guid</param>
|
||||||
/// <param name="connectionType">连接类型</param>
|
/// <param name="fromNodeJunctionType">起始节点控制点</param>
|
||||||
Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionType connectionType);
|
/// <param name="toNodeJunctionType">目标节点控制点</param>
|
||||||
|
/// <param name="connectionType">决定了方法执行后的后继行为</param>
|
||||||
|
/// <param name="argIndex">决定了方法入参来源</param>
|
||||||
|
Task<bool> ConnectNodeAsync(string fromNodeGuid,
|
||||||
|
string toNodeGuid,
|
||||||
|
JunctionType fromNodeJunctionType,
|
||||||
|
JunctionType toNodeJunctionType,
|
||||||
|
ConnectionInvokeType connectionType,
|
||||||
|
int argIndex);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建节点/区域/基础控件
|
/// 创建节点/区域/基础控件
|
||||||
@@ -680,7 +728,7 @@ namespace Serein.Library.Api
|
|||||||
/// <param name="fromNodeGuid">起始节点</param>
|
/// <param name="fromNodeGuid">起始节点</param>
|
||||||
/// <param name="toNodeGuid">目标节点</param>
|
/// <param name="toNodeGuid">目标节点</param>
|
||||||
/// <param name="connectionType">连接类型</param>
|
/// <param name="connectionType">连接类型</param>
|
||||||
Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType);
|
Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 移除节点/区域/基础控件
|
/// 移除节点/区域/基础控件
|
||||||
|
|||||||
28
Library/Enums/ConnectionArgSourceType.cs
Normal file
28
Library/Enums/ConnectionArgSourceType.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library
|
||||||
|
{
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 节点对应方法的入参来源
|
||||||
|
/// </summary>
|
||||||
|
public enum ConnectionArgSourceType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// (连接自身)从上一节点获取数据
|
||||||
|
/// </summary>
|
||||||
|
GetPreviousNodeData,
|
||||||
|
/// <summary>
|
||||||
|
/// 从指定节点获取数据
|
||||||
|
/// </summary>
|
||||||
|
GetOtherNodeData,
|
||||||
|
/// <summary>
|
||||||
|
/// 立刻执行某个节点获取其数据
|
||||||
|
/// </summary>
|
||||||
|
GetOtherNodeDataOfInvoke,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,7 +8,7 @@ namespace Serein.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 表示了两个节点之间的连接关系,同时表示节点运行完成后,所会执行的下一个节点类型。
|
/// 表示了两个节点之间的连接关系,同时表示节点运行完成后,所会执行的下一个节点类型。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum ConnectionType
|
public enum ConnectionInvokeType
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 将不会继续执行
|
/// 将不会继续执行
|
||||||
@@ -30,11 +30,8 @@ namespace Serein.Library
|
|||||||
/// 异常发生分支(当前节点对应的方法执行时出现非预期的异常)
|
/// 异常发生分支(当前节点对应的方法执行时出现非预期的异常)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IsError,
|
IsError,
|
||||||
/// <summary>
|
|
||||||
/// 无视
|
|
||||||
/// </summary>
|
|
||||||
// IsIgnore,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
27
Library/Enums/JunctionOfConnectionType.cs
Normal file
27
Library/Enums/JunctionOfConnectionType.cs
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 连接的控制点类型枚举
|
||||||
|
/// </summary>
|
||||||
|
public enum JunctionOfConnectionType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 没有关系,用于处理非预期连接的情况需要的返回值
|
||||||
|
/// </summary>
|
||||||
|
None,
|
||||||
|
/// <summary>
|
||||||
|
/// 表示方法执行顺序关系
|
||||||
|
/// </summary>
|
||||||
|
Invoke,
|
||||||
|
/// <summary>
|
||||||
|
/// 表示参数获取来源关系
|
||||||
|
/// </summary>
|
||||||
|
Arg
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
|||||||
namespace Serein.Library
|
namespace Serein.Library
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接点类型
|
/// 控制点类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum JunctionType
|
public enum JunctionType
|
||||||
{
|
{
|
||||||
@@ -28,4 +28,8 @@ namespace Serein.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
NextStep,
|
NextStep,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
56
Library/FlowNode/JunctionModel.cs
Normal file
56
Library/FlowNode/JunctionModel.cs
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.FlowNode
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 有1个Execute
|
||||||
|
* 有1个NextStep
|
||||||
|
* 有0~65535个入参 ushort
|
||||||
|
* 有1个ReturnData(void方法返回null)
|
||||||
|
*
|
||||||
|
* Execute: // 执行这个方法
|
||||||
|
* 只接受 NextStep 的连接
|
||||||
|
* ArgData:
|
||||||
|
* 互相之间不能连接,只能接受 Execute、ReturnData 的连接
|
||||||
|
* Execute:表示从 Execute所在节点 获取数据
|
||||||
|
* ReturnData: 表示从对应节点获取数据
|
||||||
|
* ReturnData:
|
||||||
|
* 只能发起主动连接,且只能连接到 ArgData
|
||||||
|
* NextStep
|
||||||
|
* 只能连接连接 Execute
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 依附于节点的连接点
|
||||||
|
/// </summary>
|
||||||
|
public class JunctionModel
|
||||||
|
{
|
||||||
|
public JunctionModel(NodeModelBase NodeModel, JunctionType JunctionType)
|
||||||
|
{
|
||||||
|
Guid = System.Guid.NewGuid().ToString();
|
||||||
|
this.NodeModel = NodeModel;
|
||||||
|
this.JunctionType = JunctionType;
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 用于标识连接点
|
||||||
|
/// </summary>
|
||||||
|
public string Guid { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标识连接点的类型
|
||||||
|
/// </summary>
|
||||||
|
public JunctionType JunctionType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接点依附的节点
|
||||||
|
/// </summary>
|
||||||
|
public NodeModelBase NodeModel { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using Serein.Library.NodeGenerator;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
|
using System.Net.Mime;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
|
|
||||||
namespace Serein.Library
|
namespace Serein.Library
|
||||||
@@ -71,8 +72,8 @@ namespace Serein.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前节点执行完毕后需要执行的下一个分支的类别
|
/// 当前节点执行完毕后需要执行的下一个分支的类别
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo]
|
//[PropertyInfo]
|
||||||
private ConnectionType _nextOrientation = ConnectionType.None;
|
//private ConnectionInvokeType _nextOrientation = ConnectionInvokeType.None;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
||||||
@@ -91,9 +92,9 @@ namespace Serein.Library
|
|||||||
{
|
{
|
||||||
public NodeModelBase(IFlowEnvironment environment)
|
public NodeModelBase(IFlowEnvironment environment)
|
||||||
{
|
{
|
||||||
PreviousNodes = new Dictionary<ConnectionType, List<NodeModelBase>>();
|
PreviousNodes = new Dictionary<ConnectionInvokeType, List<NodeModelBase>>();
|
||||||
SuccessorNodes = new Dictionary<ConnectionType, List<NodeModelBase>>();
|
SuccessorNodes = new Dictionary<ConnectionInvokeType, List<NodeModelBase>>();
|
||||||
foreach (ConnectionType ctType in NodeStaticConfig.ConnectionTypes)
|
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
|
||||||
{
|
{
|
||||||
PreviousNodes[ctType] = new List<NodeModelBase>();
|
PreviousNodes[ctType] = new List<NodeModelBase>();
|
||||||
SuccessorNodes[ctType] = new List<NodeModelBase>();
|
SuccessorNodes[ctType] = new List<NodeModelBase>();
|
||||||
@@ -102,15 +103,17 @@ namespace Serein.Library
|
|||||||
this.Env = environment;
|
this.Env = environment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 不同分支的父节点
|
/// 不同分支的父节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<ConnectionType, List<NodeModelBase>> PreviousNodes { get; }
|
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> PreviousNodes { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 不同分支的子节点
|
/// 不同分支的子节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<ConnectionType, List<NodeModelBase>> SuccessorNodes { get; }
|
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> SuccessorNodes { get; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ using Serein.Library.Utils.SereinExpression;
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.ComponentModel.Design;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
using System.Linq.Expressions;
|
||||||
using System.Net.Http.Headers;
|
using System.Net.Http.Headers;
|
||||||
@@ -24,22 +25,6 @@ namespace Serein.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
#region 调试中断
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 不再中断
|
|
||||||
/// </summary>
|
|
||||||
public void CancelInterrupt()
|
|
||||||
{
|
|
||||||
this.DebugSetting.InterruptClass = InterruptClass.None;
|
|
||||||
DebugSetting.CancelInterruptCallback?.Invoke();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 导出/导入项目文件节点信息
|
#region 导出/导入项目文件节点信息
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -56,10 +41,10 @@ namespace Serein.Library
|
|||||||
{
|
{
|
||||||
// if (MethodDetails == null) return null;
|
// if (MethodDetails == null) return null;
|
||||||
|
|
||||||
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
|
var trueNodes = SuccessorNodes[ConnectionInvokeType.IsSucceed].Select(item => item.Guid); // 真分支
|
||||||
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
|
var falseNodes = SuccessorNodes[ConnectionInvokeType.IsFail].Select(item => item.Guid);// 假分支
|
||||||
var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
|
var errorNodes = SuccessorNodes[ConnectionInvokeType.IsError].Select(item => item.Guid);// 异常分支
|
||||||
var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
|
var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支
|
||||||
|
|
||||||
// 生成参数列表
|
// 生成参数列表
|
||||||
Parameterdata[] parameterData = GetParameterdatas();
|
Parameterdata[] parameterData = GetParameterdatas();
|
||||||
@@ -86,7 +71,7 @@ namespace Serein.Library
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
|
public virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
|
||||||
{
|
{
|
||||||
this.Guid = nodeInfo.Guid;
|
this.Guid = nodeInfo.Guid;
|
||||||
|
|
||||||
if (nodeInfo.Position is null)
|
if (nodeInfo.Position is null)
|
||||||
{
|
{
|
||||||
@@ -104,6 +89,19 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 调试中断
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 不再中断
|
||||||
|
/// </summary>
|
||||||
|
public void CancelInterrupt()
|
||||||
|
{
|
||||||
|
this.DebugSetting.InterruptClass = InterruptClass.None;
|
||||||
|
DebugSetting.CancelInterruptCallback?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -137,8 +135,6 @@ namespace Serein.Library
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 开始执行
|
/// 开始执行
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -163,7 +159,7 @@ namespace Serein.Library
|
|||||||
var currentNode = stack.Pop();
|
var currentNode = stack.Pop();
|
||||||
|
|
||||||
// 筛选出上游分支
|
// 筛选出上游分支
|
||||||
var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].ToArray();
|
var upstreamNodes = currentNode.SuccessorNodes[ConnectionInvokeType.Upstream].ToArray();
|
||||||
for (int index = 0; index < upstreamNodes.Length; index++)
|
for (int index = 0; index < upstreamNodes.Length; index++)
|
||||||
{
|
{
|
||||||
NodeModelBase upstreamNode = upstreamNodes[index];
|
NodeModelBase upstreamNode = upstreamNodes[index];
|
||||||
@@ -176,7 +172,7 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
upstreamNode.PreviousNode = currentNode;
|
upstreamNode.PreviousNode = currentNode;
|
||||||
await upstreamNode.StartFlowAsync(context); // 执行流程节点的上游分支
|
await upstreamNode.StartFlowAsync(context); // 执行流程节点的上游分支
|
||||||
if (upstreamNode.NextOrientation == ConnectionType.IsError)
|
if (context.NextOrientation == ConnectionInvokeType.IsError)
|
||||||
{
|
{
|
||||||
// 如果上游分支执行失败,不再继续执行
|
// 如果上游分支执行失败,不再继续执行
|
||||||
// 使上游节点(仅上游节点本身,不包含上游节点的后继节点)
|
// 使上游节点(仅上游节点本身,不包含上游节点的后继节点)
|
||||||
@@ -197,7 +193,7 @@ namespace Serein.Library
|
|||||||
#region 执行完成
|
#region 执行完成
|
||||||
|
|
||||||
// 选择后继分支
|
// 选择后继分支
|
||||||
var nextNodes = currentNode.SuccessorNodes[currentNode.NextOrientation];
|
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
|
||||||
|
|
||||||
// 将下一个节点集合中的所有节点逆序推入栈中
|
// 将下一个节点集合中的所有节点逆序推入栈中
|
||||||
for (int i = nextNodes.Count - 1; i >= 0; i--)
|
for (int i = nextNodes.Count - 1; i >= 0; i--)
|
||||||
@@ -215,7 +211,6 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 执行节点对应的方法
|
/// 执行节点对应的方法
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -234,7 +229,6 @@ namespace Serein.Library
|
|||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
MethodDetails md = MethodDetails;
|
MethodDetails md = MethodDetails;
|
||||||
//var del = md.MethodDelegate.Clone();
|
|
||||||
if (md is null)
|
if (md is null)
|
||||||
{
|
{
|
||||||
throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
||||||
@@ -247,35 +241,70 @@ namespace Serein.Library
|
|||||||
{
|
{
|
||||||
md.ActingInstance = context.Env.IOC.Get(md.ActingInstanceType);
|
md.ActingInstance = context.Env.IOC.Get(md.ActingInstanceType);
|
||||||
}
|
}
|
||||||
// md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
|
|
||||||
object instance = md.ActingInstance;
|
|
||||||
|
|
||||||
|
|
||||||
object result = null;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
object[] args = GetParameters(context, this, md);
|
object[] args = await GetParametersAsync(context, this, md);
|
||||||
result = await dd.InvokeAsync(md.ActingInstance, args);
|
var result = await dd.InvokeAsync(md.ActingInstance, args);
|
||||||
NextOrientation = ConnectionType.IsSucceed;
|
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
||||||
NextOrientation = ConnectionType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
RuningException = ex;
|
RuningException = ex;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 执行单个节点对应的方法,并不做状态检查
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="env"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public virtual async Task<object> InvokeAsync(IFlowEnvironment env)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
MethodDetails md = MethodDetails;
|
||||||
|
if (md is null)
|
||||||
|
{
|
||||||
|
throw new Exception($"不存在方法信息{md.MethodName}");
|
||||||
|
}
|
||||||
|
if (!env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||||
|
{
|
||||||
|
throw new Exception($"不存在对应委托{md.MethodName}");
|
||||||
|
}
|
||||||
|
if (md.ActingInstance is null)
|
||||||
|
{
|
||||||
|
md.ActingInstance = env.IOC.Get(md.ActingInstanceType);
|
||||||
|
if (md.ActingInstance is null)
|
||||||
|
{
|
||||||
|
md.ActingInstance = env.IOC.Instantiate(md.ActingInstanceType);
|
||||||
|
if (md.ActingInstance is null)
|
||||||
|
{
|
||||||
|
throw new Exception($"无法创建相应的实例{md.ActingInstanceType.FullName}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
object[] args = await GetParametersAsync(null, this, md);
|
||||||
|
var result = await dd.InvokeAsync(md.ActingInstance, args);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取对应的参数数组
|
/// 获取对应的参数数组
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static object[] GetParameters(IDynamicContext context, NodeModelBase nodeModel, MethodDetails md)
|
public static async Task<object[]> GetParametersAsync(IDynamicContext context, NodeModelBase nodeModel, MethodDetails md)
|
||||||
{
|
{
|
||||||
|
await Task.Delay(0);
|
||||||
// 用正确的大小初始化参数数组
|
// 用正确的大小初始化参数数组
|
||||||
if (md.ParameterDetailss.Length == 0)
|
if (md.ParameterDetailss.Length == 0)
|
||||||
{
|
{
|
||||||
@@ -283,22 +312,34 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
|
|
||||||
object[] parameters = new object[md.ParameterDetailss.Length];
|
object[] parameters = new object[md.ParameterDetailss.Length];
|
||||||
var flowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据
|
var previousFlowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据
|
||||||
var previousDataType = flowData?.GetType();
|
var previousDataType = previousFlowData?.GetType(); // 当前传递数据的类型
|
||||||
|
|
||||||
for (int i = 0; i < parameters.Length; i++)
|
for (int i = 0; i < parameters.Length; i++)
|
||||||
{
|
{
|
||||||
|
|
||||||
object inputParameter; // 存放解析的临时参数
|
|
||||||
var ed = md.ParameterDetailss[i]; // 方法入参描述
|
var ed = md.ParameterDetailss[i]; // 方法入参描述
|
||||||
|
|
||||||
|
#region 获取基础的上下文数据
|
||||||
|
if (ed.DataType == typeof(IFlowEnvironment)) // 获取流程上下文
|
||||||
|
{
|
||||||
|
parameters[i] = nodeModel.Env;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (ed.DataType == typeof(IDynamicContext)) // 获取流程上下文
|
||||||
|
{
|
||||||
|
parameters[i] = context;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 确定[预入参]数据
|
||||||
|
object inputParameter; // 存放解析的临时参数
|
||||||
if (ed.IsExplicitData) // 判断是否使用显示的输入参数
|
if (ed.IsExplicitData) // 判断是否使用显示的输入参数
|
||||||
{
|
{
|
||||||
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && !(flowData is null))
|
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && !(previousFlowData is null))
|
||||||
{
|
{
|
||||||
// 执行表达式从上一节点获取对象
|
// 执行表达式从上一节点获取对象
|
||||||
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, flowData, out _);
|
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, previousFlowData, out _);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -308,9 +349,31 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inputParameter = flowData; // 使用上一节点的对象
|
if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
|
||||||
}
|
{
|
||||||
|
inputParameter = previousFlowData; // 使用运行时上一节点的返回值
|
||||||
|
}
|
||||||
|
else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
|
||||||
|
{
|
||||||
|
// 获取指定节点的数据
|
||||||
|
// 如果指定节点没有被执行,会返回null
|
||||||
|
// 如果执行过,会获取上一次执行结果作为预入参数据
|
||||||
|
inputParameter = ed.ArgDataSourceNodeMoels[i].FlowData;
|
||||||
|
}
|
||||||
|
else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
|
||||||
|
{
|
||||||
|
// 立刻调用对应节点获取数据。
|
||||||
|
var result = await ed.ArgDataSourceNodeMoels[i].InvokeAsync(nodeModel.Env);
|
||||||
|
inputParameter = result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new Exception("节点执行方法获取入参参数时,ConnectionArgSourceType枚举是意外的枚举值");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 入参存在取值转换器,调用对应的转换器获取入参数据
|
||||||
// 入参存在取值转换器
|
// 入参存在取值转换器
|
||||||
if (ed.ExplicitType.IsEnum && !(ed.Convertor is null))
|
if (ed.ExplicitType.IsEnum && !(ed.Convertor is null))
|
||||||
{
|
{
|
||||||
@@ -327,13 +390,11 @@ namespace Serein.Library
|
|||||||
parameters[i] = value;
|
parameters[i] = value;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
//if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum))
|
|
||||||
//{
|
|
||||||
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
// 入参存在类型转换器,获取枚举转换器中记录的枚举
|
#region 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型
|
||||||
|
// 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型
|
||||||
if (ed.ExplicitType.IsEnum && ed.DataType != ed.ExplicitType)
|
if (ed.ExplicitType.IsEnum && ed.DataType != ed.ExplicitType)
|
||||||
{
|
{
|
||||||
var resultEnum = Enum.Parse(ed.ExplicitType, ed.DataValue);
|
var resultEnum = Enum.Parse(ed.ExplicitType, ed.DataValue);
|
||||||
@@ -341,7 +402,7 @@ namespace Serein.Library
|
|||||||
var type = EnumHelper.GetBoundValue(ed.ExplicitType, resultEnum, attr => attr.Value);
|
var type = EnumHelper.GetBoundValue(ed.ExplicitType, resultEnum, attr => attr.Value);
|
||||||
if (type is Type enumBindType && !(enumBindType is null))
|
if (type is Type enumBindType && !(enumBindType is null))
|
||||||
{
|
{
|
||||||
var value = context.Env.IOC.Instantiate(enumBindType);
|
var value = nodeModel.Env.IOC.Instantiate(enumBindType);
|
||||||
if (value is null)
|
if (value is null)
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -351,62 +412,83 @@ namespace Serein.Library
|
|||||||
parameters[i] = value;
|
parameters[i] = value;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 对入参数据尝试进行转换
|
||||||
|
|
||||||
if (ed.DataType.IsValueType)
|
if (inputParameter.GetType() == ed.DataType)
|
||||||
{
|
{
|
||||||
var valueStr = inputParameter?.ToString();
|
parameters[i] = inputParameter; // 类型一致无需转换,直接装入入参数组
|
||||||
parameters[i] = valueStr.ToValueData(ed.DataType);
|
|
||||||
}
|
}
|
||||||
else
|
else if (ed.DataType.IsValueType)
|
||||||
{
|
{
|
||||||
|
// 值类型
|
||||||
var valueStr = inputParameter?.ToString();
|
var valueStr = inputParameter?.ToString();
|
||||||
if (ed.DataType == typeof(string))
|
parameters[i] = valueStr.ToValueData(ed.DataType); // 类型不一致,尝试进行转换,如果转换失败返回类型对应的默认值
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 引用类型
|
||||||
|
if (ed.DataType == typeof(string)) // 转为字符串
|
||||||
{
|
{
|
||||||
|
var valueStr = inputParameter?.ToString();
|
||||||
parameters[i] = valueStr;
|
parameters[i] = valueStr;
|
||||||
}
|
}
|
||||||
else if (ed.DataType == typeof(IDynamicContext))
|
else if(ed.DataType.IsSubclassOf(inputParameter.GetType())) // 入参类型 是 预入参数据类型 的 子类/实现类
|
||||||
{
|
{
|
||||||
parameters[i] = context;
|
// 方法入参中,父类不能隐式转为子类,这里需要进行强制转换
|
||||||
|
parameters[i] = ObjectConvertHelper.ConvertParentToChild(inputParameter, ed.DataType);
|
||||||
}
|
}
|
||||||
else if (ed.DataType == typeof(MethodDetails))
|
else if(ed.DataType.IsAssignableFrom(inputParameter.GetType())) // 入参类型 是 预入参数据类型 的 父类/接口
|
||||||
{
|
|
||||||
parameters[i] = md;
|
|
||||||
}
|
|
||||||
else if (ed.DataType == typeof(NodeModelBase))
|
|
||||||
{
|
|
||||||
parameters[i] = nodeModel;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
parameters[i] = inputParameter;
|
parameters[i] = inputParameter;
|
||||||
}
|
}
|
||||||
|
// 集合类型
|
||||||
|
else if(inputParameter is IEnumerable collection)
|
||||||
|
{
|
||||||
|
var enumerableMethods = typeof(Enumerable).GetMethods(); // 获取所有的 Enumerable 扩展方法
|
||||||
|
MethodInfo conversionMethod;
|
||||||
|
if (ed.DataType.IsArray) // 转为数组
|
||||||
|
{
|
||||||
|
parameters[i] = inputParameter;
|
||||||
|
conversionMethod = enumerableMethods.FirstOrDefault(m => m.Name == "ToArray" && m.IsGenericMethodDefinition);
|
||||||
|
}
|
||||||
|
else if (ed.DataType.GetGenericTypeDefinition() == typeof(List<>)) // 转为集合
|
||||||
|
{
|
||||||
|
conversionMethod = enumerableMethods.FirstOrDefault(m => m.Name == "ToList" && m.IsGenericMethodDefinition);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("输入对象不是集合或目标类型不支持(目前仅支持Array、List的自动转换)");
|
||||||
|
}
|
||||||
|
var genericMethod = conversionMethod.MakeGenericMethod(ed.DataType);
|
||||||
|
var result = genericMethod.Invoke(null, new object[] { collection });
|
||||||
|
parameters[i] = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//parameters[i] = ed.DataType switch
|
|
||||||
|
//else if (ed.DataType == typeof(MethodDetails)) // 希望获取节点对应的方法描述,好像没啥用
|
||||||
//{
|
//{
|
||||||
// Type t when t == typeof(string) => valueStr,
|
// parameters[i] = md;
|
||||||
// Type t when t == typeof(IDynamicContext) => context, // 上下文
|
//}
|
||||||
// Type t when t == typeof(DateTime) => string.IsNullOrEmpty(valueStr) ? null : DateTime.Parse(valueStr),
|
//else if (ed.DataType == typeof(NodeModelBase)) // 希望获取方法生成的节点,好像没啥用
|
||||||
|
//{
|
||||||
// Type t when t == typeof(MethodDetails) => md, // 节点方法描述
|
// parameters[i] = nodeModel;
|
||||||
// Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类
|
//}
|
||||||
|
|
||||||
// Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
|
|
||||||
// Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
|
|
||||||
// _ => inputParameter,
|
|
||||||
//};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
return parameters;
|
return parameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新节点数据,并检查监视表达式是否生效
|
/// 更新节点数据,并检查监视表达式是否生效
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -479,7 +561,6 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 释放对象
|
/// 释放对象
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
using Serein.Library.Api;
|
using Serein.Library.Api;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics.Contracts;
|
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Serein.Library
|
namespace Serein.Library
|
||||||
{
|
{
|
||||||
@@ -17,7 +14,7 @@ namespace Serein.Library
|
|||||||
private readonly IFlowEnvironment env;
|
private readonly IFlowEnvironment env;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 对应的节点
|
/// 所在的节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo(IsProtection = true)]
|
[PropertyInfo(IsProtection = true)]
|
||||||
private NodeModelBase _nodeModel;
|
private NodeModelBase _nodeModel;
|
||||||
@@ -29,7 +26,9 @@ namespace Serein.Library
|
|||||||
private int _index;
|
private int _index;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否为显式参数(固定值/表达式)
|
/// <para>是否为显式参数(固定值/表达式)</para>
|
||||||
|
/// <para>如果为 true ,则使用UI输入的文本值作为入参数据(过程中会尽可能转为类型需要的数据)。</para>
|
||||||
|
/// <para>如果为 false ,则根据 ArgDataSourceType 调用相应节点的GetFlowData()方法,获取返回的数据作为入参数据。</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo(IsNotification = true)]
|
[PropertyInfo(IsNotification = true)]
|
||||||
private bool _isExplicitData ;
|
private bool _isExplicitData ;
|
||||||
@@ -41,7 +40,7 @@ namespace Serein.Library
|
|||||||
private Func<object, object> _convertor ;
|
private Func<object, object> _convertor ;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 显式类型
|
/// 方法入参若无相关转换器特性标注,则无需关注该变量。该变量用于需要用到枚举BinValue转换器时,指示相应的入参变量需要转为的类型。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo]
|
[PropertyInfo]
|
||||||
private Type _explicitType ;
|
private Type _explicitType ;
|
||||||
@@ -56,7 +55,22 @@ namespace Serein.Library
|
|||||||
private string _explicitTypeName ;
|
private string _explicitTypeName ;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 方法需要的类型
|
/// 入参数据来源。默认使用上一节点作为入参数据。
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo(IsNotification = true)]
|
||||||
|
private ConnectionArgSourceType _argDataSourceType = ConnectionArgSourceType.GetPreviousNodeData;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 当 ArgDataSourceType 不为 GetPreviousNodeData 时(从运行时上一节点获取数据)。
|
||||||
|
/// 则通过该集合对应的节点,获取其 FlowData 作为预处理的入参参数。
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo(IsProtection = true)]
|
||||||
|
public NodeModelBase[] _argDataSourceNodeMoels;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方法入参需要的类型。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo]
|
[PropertyInfo]
|
||||||
private Type _dataType ;
|
private Type _dataType ;
|
||||||
@@ -74,7 +88,7 @@ namespace Serein.Library
|
|||||||
private string _dataValue;
|
private string _dataValue;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 如果是引用类型,拷贝时不会发生改变。
|
/// 只有当ExplicitTypeName 为 Select 时,才会需要该成员。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo(IsNotification = true)]
|
[PropertyInfo(IsNotification = true)]
|
||||||
private string[] _items ;
|
private string[] _items ;
|
||||||
@@ -91,6 +105,7 @@ namespace Serein.Library
|
|||||||
this.env = env;
|
this.env = env;
|
||||||
this.NodeModel = nodeModel;
|
this.NodeModel = nodeModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通过参数信息加载实体,用于加载项目文件、远程连接的场景
|
/// 通过参数信息加载实体,用于加载项目文件、远程连接的场景
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -109,12 +124,15 @@ namespace Serein.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 用于创建元数据
|
/// 用于创建元数据
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="info">方法参数信息</param>
|
|
||||||
public ParameterDetails()
|
public ParameterDetails()
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 转为描述
|
/// 转为描述
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -151,6 +169,7 @@ namespace Serein.Library
|
|||||||
Name = this.Name,
|
Name = this.Name,
|
||||||
DataValue = string.IsNullOrEmpty(DataValue) ? string.Empty : DataValue,
|
DataValue = string.IsNullOrEmpty(DataValue) ? string.Empty : DataValue,
|
||||||
Items = this.Items?.Select(it => it).ToArray(),
|
Items = this.Items?.Select(it => it).ToArray(),
|
||||||
|
|
||||||
};
|
};
|
||||||
return pd;
|
return pd;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,12 +29,12 @@ namespace Serein.Library
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 节点连接关系种类
|
/// 节点连接关系种类
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly ConnectionType[] ConnectionTypes = new ConnectionType[]
|
public static readonly ConnectionInvokeType[] ConnectionTypes = new ConnectionInvokeType[]
|
||||||
{
|
{
|
||||||
ConnectionType.Upstream,
|
ConnectionInvokeType.Upstream,
|
||||||
ConnectionType.IsSucceed,
|
ConnectionInvokeType.IsSucceed,
|
||||||
ConnectionType.IsFail,
|
ConnectionInvokeType.IsFail,
|
||||||
ConnectionType.IsError,
|
ConnectionInvokeType.IsError,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
82
Library/Utils/ObjectConvertHelper.cs
Normal file
82
Library/Utils/ObjectConvertHelper.cs
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library.Utils
|
||||||
|
{
|
||||||
|
public static class ObjectConvertHelper
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 父类转为子类
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent">父类对象</param>
|
||||||
|
/// <param name="childType">子类类型</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static object ConvertParentToChild(object parent,Type childType)
|
||||||
|
{
|
||||||
|
var child = Activator.CreateInstance(childType);
|
||||||
|
var parentType = parent.GetType();
|
||||||
|
|
||||||
|
// 复制父类属性
|
||||||
|
foreach (var prop in parentType.GetProperties())
|
||||||
|
{
|
||||||
|
if (prop.CanWrite)
|
||||||
|
{
|
||||||
|
var value = prop.GetValue(parent);
|
||||||
|
childType.GetProperty(prop.Name)?.SetValue(child, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return child;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 集合类型转换为Array/List
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj"></param>
|
||||||
|
/// <param name="targetType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="InvalidOperationException"></exception>
|
||||||
|
public static object ConvertToEnumerableType(object obj, Type targetType)
|
||||||
|
{
|
||||||
|
// 获取目标类型的元素类型
|
||||||
|
Type targetElementType = targetType.IsArray
|
||||||
|
? targetType.GetElementType()
|
||||||
|
: targetType.GetGenericArguments().FirstOrDefault();
|
||||||
|
|
||||||
|
if (targetElementType == null)
|
||||||
|
throw new InvalidOperationException("无法获取目标类型的元素类型");
|
||||||
|
|
||||||
|
// 检查输入对象是否为集合类型
|
||||||
|
if (obj is IEnumerable collection)
|
||||||
|
{
|
||||||
|
// 判断目标类型是否是数组
|
||||||
|
if (targetType.IsArray)
|
||||||
|
{
|
||||||
|
var toArrayMethod = typeof(Enumerable).GetMethod("ToArray").MakeGenericMethod(targetElementType);
|
||||||
|
return toArrayMethod.Invoke(null, new object[] { collection });
|
||||||
|
}
|
||||||
|
// 判断目标类型是否是 List<T>
|
||||||
|
else if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(List<>))
|
||||||
|
{
|
||||||
|
var toListMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(targetElementType);
|
||||||
|
return toListMethod.Invoke(null, new object[] { collection });
|
||||||
|
}
|
||||||
|
// 判断目标类型是否是 HashSet<T>
|
||||||
|
else if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(HashSet<>))
|
||||||
|
{
|
||||||
|
var toHashSetMethod = typeof(Enumerable).GetMethod("ToHashSet").MakeGenericMethod(targetElementType);
|
||||||
|
return toHashSetMethod.Invoke(null, new object[] { collection });
|
||||||
|
}
|
||||||
|
// 其他类型可以扩展类似的处理
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new InvalidOperationException("输入对象不是集合或目标类型不支持");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -88,10 +88,8 @@ namespace Serein.Library.Utils
|
|||||||
{
|
{
|
||||||
var constructor = type.GetConstructors().First(); // 获取第一个构造函数
|
var constructor = type.GetConstructors().First(); // 获取第一个构造函数
|
||||||
var parameters = constructor.GetParameters(); // 获取参数列表
|
var parameters = constructor.GetParameters(); // 获取参数列表
|
||||||
var parameterValues = parameters.Select(param => ResolveDependency(param.ParameterType)).ToArray();
|
var parameterValues = parameters.Select(param => ResolveDependency(param.ParameterType)).ToArray(); // 生成创建类型的入参参数
|
||||||
var instance = Activator.CreateInstance(type, parameterValues);
|
var instance = Activator.CreateInstance(type, parameterValues); // 创建实例
|
||||||
|
|
||||||
//var instance =CreateInstance(controllerType, parameters); // CreateInstance(controllerType, parameters); // 创建目标类型的实例
|
|
||||||
if (instance != null)
|
if (instance != null)
|
||||||
{
|
{
|
||||||
InjectDependencies(instance, false); // 完成创建后注入实例需要的特性依赖项
|
InjectDependencies(instance, false); // 完成创建后注入实例需要的特性依赖项
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ using Serein.Library.Utils.SereinExpression;
|
|||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
using Serein.NodeFlow.Tool;
|
using Serein.NodeFlow.Tool;
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Numerics;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
using static Serein.Library.Utils.ChannelFlowInterrupt;
|
||||||
@@ -409,6 +410,24 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 单独运行一个节点
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="context"></param>
|
||||||
|
/// <param name="nodeGuid"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<object> InvokeNodeAsync(string nodeGuid)
|
||||||
|
{
|
||||||
|
if(this.NodeModels.TryGetValue(nodeGuid, out var model))
|
||||||
|
{
|
||||||
|
return await model.ExecutingAsync(null);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 退出
|
/// 退出
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -633,23 +652,23 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<(ConnectionType connectionType, string[] guids)> allToNodes = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
|
List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes),
|
||||||
(ConnectionType.IsFail, nodeInfo.FalseNodes),
|
(ConnectionInvokeType.IsFail, nodeInfo.FalseNodes),
|
||||||
(ConnectionType.IsError, nodeInfo.ErrorNodes),
|
(ConnectionInvokeType.IsError, nodeInfo.ErrorNodes),
|
||||||
(ConnectionType.Upstream, nodeInfo.UpstreamNodes)];
|
(ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)];
|
||||||
|
|
||||||
List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
||||||
.Select(info => (info.connectionType,
|
.Select(info => (info.connectionType,
|
||||||
info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid])
|
info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid])
|
||||||
.ToArray()))
|
.ToArray()))
|
||||||
.ToList();
|
.ToList();
|
||||||
// 遍历每种类型的节点分支(四种)
|
// 遍历每种类型的节点分支(四种)
|
||||||
foreach ((ConnectionType connectionType, NodeModelBase[] toNodes) item in fromNodes)
|
foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in fromNodes)
|
||||||
{
|
{
|
||||||
// 遍历当前类型分支的节点(确认连接关系)
|
// 遍历当前类型分支的节点(确认连接关系)
|
||||||
foreach (var toNode in item.toNodes)
|
foreach (var toNode in item.toNodes)
|
||||||
{
|
{
|
||||||
ConnectNodeAsync(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
|
_ = ConnectInvokeOfNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -859,9 +878,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
NodeModelBase? pNode = pnc.Value[i];
|
NodeModelBase? pNode = pnc.Value[i];
|
||||||
pNode.SuccessorNodes[pCType].Remove(remoteNode);
|
pNode.SuccessorNodes[pCType].Remove(remoteNode);
|
||||||
|
|
||||||
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(pNode.Guid,
|
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(
|
||||||
|
pNode.Guid,
|
||||||
remoteNode.Guid,
|
remoteNode.Guid,
|
||||||
pCType,
|
JunctionOfConnectionType.Invoke,
|
||||||
|
pCType, // 对应的连接关系
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Remote))); // 通知UI
|
NodeConnectChangeEventArgs.ConnectChangeType.Remote))); // 通知UI
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -894,15 +915,63 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="fromNodeJunctionType">起始节点控制点</param>
|
/// <param name="fromNodeJunctionType">起始节点控制点</param>
|
||||||
/// <param name="toNodeJunctionType">目标节点控制点</param>
|
/// <param name="toNodeJunctionType">目标节点控制点</param>
|
||||||
/// <param name="connectionType">连接关系</param>
|
/// <param name="connectionType">连接关系</param>
|
||||||
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionType connectionType)
|
public async Task<bool> ConnectNodeAsync(string fromNodeGuid,
|
||||||
|
string toNodeGuid,
|
||||||
|
JunctionType fromNodeJunctionType,
|
||||||
|
JunctionType toNodeJunctionType,
|
||||||
|
ConnectionInvokeType connectionType,
|
||||||
|
int argIndex)
|
||||||
{
|
{
|
||||||
|
|
||||||
// 获取起始节点与目标节点
|
// 获取起始节点与目标节点
|
||||||
var fromNode = GuidToModel(fromNodeGuid);
|
var fromNode = GuidToModel(fromNodeGuid);
|
||||||
var toNode = GuidToModel(toNodeGuid);
|
var toNode = GuidToModel(toNodeGuid);
|
||||||
if (fromNode is null || toNode is null) return false;
|
if (fromNode is null || toNode is null) return false;
|
||||||
|
(var type,var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType);
|
||||||
|
if (!state)
|
||||||
|
{
|
||||||
|
Console.WriteLine("出现非预期的连接行为");
|
||||||
|
return false; // 出现不符预期的连接行为,忽略此次连接行为
|
||||||
|
}
|
||||||
|
|
||||||
// 开始连接
|
|
||||||
return await ConnectNodeAsync(fromNode, toNode, connectionType); // 外部调用连接方法
|
if(type == JunctionOfConnectionType.Invoke)
|
||||||
|
{
|
||||||
|
if (fromNodeJunctionType == JunctionType.Execute)
|
||||||
|
{
|
||||||
|
// 如果 起始控制点 是“方法调用”,需要反转 from to 节点
|
||||||
|
(fromNode, toNode) = (toNode, fromNode);
|
||||||
|
}
|
||||||
|
// 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点
|
||||||
|
state = ConnectInvokeOfNode(fromNode, toNode, connectionType); // 本地环境进行连接
|
||||||
|
}
|
||||||
|
else if (type == JunctionOfConnectionType.Arg)
|
||||||
|
{
|
||||||
|
ConnectionArgSourceType connectionArgSourceType;
|
||||||
|
|
||||||
|
if (fromNode.Guid.Equals(toNode.Guid))
|
||||||
|
{
|
||||||
|
connectionArgSourceType = ConnectionArgSourceType.GetPreviousNodeData;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
connectionArgSourceType = ConnectionArgSourceType.GetOtherNodeData;
|
||||||
|
}
|
||||||
|
|
||||||
|
// (连接自身的情况下)从上一个节点“返回值”控制点,连接到目标节点“方法入参”控制点
|
||||||
|
// 从起始节点“返回值”控制点,连接到目标节点“方法入参”控制点
|
||||||
|
if (fromNodeJunctionType == JunctionType.ArgData)
|
||||||
|
{
|
||||||
|
// 如果 起始控制点 是“方法入参”,需要反转 from to 节点
|
||||||
|
(fromNode, toNode) = (toNode, fromNode);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 确定方法入参关系
|
||||||
|
state = ConnectGerResultOfNode(fromNode, toNode, connectionArgSourceType, argIndex); // 本地环境进行连接
|
||||||
|
}
|
||||||
|
|
||||||
|
return state;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -913,7 +982,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="toNodeGuid">目标节点Guid</param>
|
/// <param name="toNodeGuid">目标节点Guid</param>
|
||||||
/// <param name="connectionType">连接关系</param>
|
/// <param name="connectionType">连接关系</param>
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
// 获取起始节点与目标节点
|
// 获取起始节点与目标节点
|
||||||
var fromNode = GuidToModel(fromNodeGuid);
|
var fromNode = GuidToModel(fromNodeGuid);
|
||||||
@@ -923,8 +992,6 @@ namespace Serein.NodeFlow.Env
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取方法描述
|
/// 获取方法描述
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1245,6 +1312,9 @@ namespace Serein.NodeFlow.Env
|
|||||||
#region 私有方法
|
#region 私有方法
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 加载指定路径的DLL文件
|
/// 加载指定路径的DLL文件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1290,7 +1360,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="toNodeGuid">目标节点Model</param>
|
/// <param name="toNodeGuid">目标节点Model</param>
|
||||||
/// <param name="connectionType">连接关系</param>
|
/// <param name="connectionType">连接关系</param>
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
private async Task<bool> RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
|
private async Task<bool> RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
fromNode.SuccessorNodes[connectionType].Remove(toNode);
|
fromNode.SuccessorNodes[connectionType].Remove(toNode);
|
||||||
toNode.PreviousNodes[connectionType].Remove(fromNode);
|
toNode.PreviousNodes[connectionType].Remove(fromNode);
|
||||||
@@ -1298,8 +1368,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(
|
||||||
|
fromNode.Guid,
|
||||||
toNode.Guid,
|
toNode.Guid,
|
||||||
|
JunctionOfConnectionType.Invoke,
|
||||||
connectionType,
|
connectionType,
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Remote)));
|
NodeConnectChangeEventArgs.ConnectChangeType.Remote)));
|
||||||
}
|
}
|
||||||
@@ -1431,13 +1503,85 @@ namespace Serein.NodeFlow.Env
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 检查连接
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fromNode">发起连接的起始节点</param>
|
||||||
|
/// <param name="toNode">要连接的目标节点</param>
|
||||||
|
/// <param name="fromNodeJunctionType">发起连接节点的控制点类型</param>
|
||||||
|
/// <param name="toNodeJunctionType">被连接节点的控制点类型</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static (JunctionOfConnectionType,bool) CheckConnect(NodeModelBase fromNode,
|
||||||
|
NodeModelBase toNode,
|
||||||
|
JunctionType fromNodeJunctionType,
|
||||||
|
JunctionType toNodeJunctionType)
|
||||||
|
{
|
||||||
|
var type = JunctionOfConnectionType.None;
|
||||||
|
var state = false;
|
||||||
|
if (fromNodeJunctionType == JunctionType.Execute)
|
||||||
|
{
|
||||||
|
if (toNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid))
|
||||||
|
{
|
||||||
|
// “方法执行”控制点拖拽到“下一节点”控制点,且不是同一个节点, 添加方法执行关系
|
||||||
|
type = JunctionOfConnectionType.Invoke;
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
else if (toNodeJunctionType == JunctionType.ArgData && fromNode.Guid.Equals(toNode.Guid))
|
||||||
|
{
|
||||||
|
// “方法执行”控制点拖拽到“方法入参”控制点,且是同一个节点,则添加获取参数关系,表示生成入参参数时自动从该节点的上一节点获取flowdata
|
||||||
|
type = JunctionOfConnectionType.Arg;
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fromNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid))
|
||||||
|
{
|
||||||
|
// “下一节点”控制点只能拖拽到“方法执行”控制点,且不能是同一个节点
|
||||||
|
if (toNodeJunctionType == JunctionType.Execute && !fromNode.Guid.Equals(toNode.Guid))
|
||||||
|
{
|
||||||
|
type = JunctionOfConnectionType.Invoke;
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fromNodeJunctionType == JunctionType.ArgData)
|
||||||
|
{
|
||||||
|
if (toNodeJunctionType == JunctionType.Execute && fromNode.Guid.Equals(toNode.Guid)) // 添加获取参数关系
|
||||||
|
{
|
||||||
|
// “方法入参”控制点拖拽到“方法执行”控制点,且是同一个节点,则添加获取参数关系,生成入参参数时自动从该节点的上一节点获取flowdata
|
||||||
|
type = JunctionOfConnectionType.Arg;
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
if(toNodeJunctionType == JunctionType.ReturnData && !fromNode.Guid.Equals(toNode.Guid))
|
||||||
|
{
|
||||||
|
// “”控制点拖拽到“方法返回值”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata
|
||||||
|
type = JunctionOfConnectionType.Arg;
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (fromNodeJunctionType == JunctionType.ReturnData)
|
||||||
|
{
|
||||||
|
if (toNodeJunctionType == JunctionType.ArgData && !fromNode.Guid.Equals(toNode.Guid))
|
||||||
|
{
|
||||||
|
// “方法返回值”控制点拖拽到“方法入参”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata
|
||||||
|
type = JunctionOfConnectionType.Arg;
|
||||||
|
state = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 剩下的情况都是不符预期的连接行为,忽略。
|
||||||
|
return (type,state);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接节点
|
/// 连接节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fromNode">起始节点</param>
|
/// <param name="fromNode">起始节点</param>
|
||||||
/// <param name="toNode">目标节点</param>
|
/// <param name="toNode">目标节点</param>
|
||||||
/// <param name="connectionType">连接关系</param>
|
/// <param name="connectionType">连接关系</param>
|
||||||
private async Task<bool> ConnectNodeAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
|
private bool ConnectInvokeOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
if (fromNode is null || toNode is null || fromNode == toNode)
|
if (fromNode is null || toNode is null || fromNode == toNode)
|
||||||
{
|
{
|
||||||
@@ -1446,10 +1590,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
var ToExistOnFrom = true;
|
var ToExistOnFrom = true;
|
||||||
var FromExistInTo = true;
|
var FromExistInTo = true;
|
||||||
ConnectionType[] ct = [ConnectionType.IsSucceed,
|
ConnectionInvokeType[] ct = [ConnectionInvokeType.IsSucceed,
|
||||||
ConnectionType.IsFail,
|
ConnectionInvokeType.IsFail,
|
||||||
ConnectionType.IsError,
|
ConnectionInvokeType.IsError,
|
||||||
ConnectionType.Upstream];
|
ConnectionInvokeType.Upstream];
|
||||||
|
|
||||||
if (toNode is SingleFlipflopNode flipflopNode)
|
if (toNode is SingleFlipflopNode flipflopNode)
|
||||||
{
|
{
|
||||||
@@ -1457,7 +1601,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
|
|
||||||
var isPass = false;
|
var isPass = false;
|
||||||
foreach (ConnectionType ctType in ct)
|
foreach (ConnectionInvokeType ctType in ct)
|
||||||
{
|
{
|
||||||
var FToTo = fromNode.SuccessorNodes[ctType].Where(it => it.Guid.Equals(toNode.Guid)).ToArray();
|
var FToTo = fromNode.SuccessorNodes[ctType].Where(it => it.Guid.Equals(toNode.Guid)).ToArray();
|
||||||
var ToOnF = toNode.PreviousNodes[ctType].Where(it => it.Guid.Equals(fromNode.Guid)).ToArray();
|
var ToOnF = toNode.PreviousNodes[ctType].Where(it => it.Guid.Equals(fromNode.Guid)).ToArray();
|
||||||
@@ -1494,12 +1638,18 @@ namespace Serein.NodeFlow.Env
|
|||||||
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支
|
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支
|
||||||
if (OperatingSystem.IsWindows())
|
if (OperatingSystem.IsWindows())
|
||||||
{
|
{
|
||||||
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
UIContextOperation?.Invoke(() =>
|
||||||
toNode.Guid,
|
OnNodeConnectChange?.Invoke(
|
||||||
connectionType,
|
new NodeConnectChangeEventArgs(
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI
|
fromNode.Guid, // 从哪个节点开始
|
||||||
|
toNode.Guid, // 连接到那个节点
|
||||||
|
JunctionOfConnectionType.Invoke,
|
||||||
|
connectionType, // 连接线的样式类型
|
||||||
|
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
|
||||||
|
))); // 通知UI
|
||||||
}
|
}
|
||||||
|
// Invoke
|
||||||
|
// GetResult
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1510,6 +1660,30 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接节点参数
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="fromNode"></param>
|
||||||
|
/// <param name="toNode"></param>
|
||||||
|
/// <param name="connectionArgSourceType"></param>
|
||||||
|
/// <param name="argIndex"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private bool ConnectGerResultOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionArgSourceType connectionArgSourceType,int argIndex)
|
||||||
|
{
|
||||||
|
UIContextOperation?.Invoke(() =>
|
||||||
|
OnNodeConnectChange?.Invoke(
|
||||||
|
new NodeConnectChangeEventArgs(
|
||||||
|
fromNode.Guid, // 从哪个节点开始
|
||||||
|
toNode.Guid, // 连接到那个节点
|
||||||
|
JunctionOfConnectionType.Arg,
|
||||||
|
(int)argIndex, // 连接线的样式类型
|
||||||
|
connectionArgSourceType,
|
||||||
|
NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接
|
||||||
|
))); // 通知UI
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更改起点节点
|
/// 更改起点节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -180,9 +180,14 @@ namespace Serein.NodeFlow.Env
|
|||||||
currentFlowEnvironment.ClearAll();
|
currentFlowEnvironment.ClearAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionType connectionType)
|
public async Task<bool> ConnectNodeAsync(string fromNodeGuid,
|
||||||
|
string toNodeGuid,
|
||||||
|
JunctionType fromNodeJunctionType,
|
||||||
|
JunctionType toNodeJunctionType,
|
||||||
|
ConnectionInvokeType connectionType,
|
||||||
|
int argIndex)
|
||||||
{
|
{
|
||||||
return await currentFlowEnvironment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, connectionType);
|
return await currentFlowEnvironment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, connectionType, argIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
|
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
|
||||||
@@ -265,13 +270,12 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public bool RemoteDll(string assemblyFullName)
|
public bool RemoteDll(string assemblyFullName)
|
||||||
{
|
{
|
||||||
return currentFlowEnvironment.RemoteDll(assemblyFullName);
|
return currentFlowEnvironment.RemoteDll(assemblyFullName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
return await currentFlowEnvironment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connectionType);
|
return await currentFlowEnvironment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connectionType);
|
||||||
}
|
}
|
||||||
@@ -282,7 +286,6 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public void SetConsoleOut()
|
public void SetConsoleOut()
|
||||||
{
|
{
|
||||||
currentFlowEnvironment.SetConsoleOut();
|
currentFlowEnvironment.SetConsoleOut();
|
||||||
@@ -313,6 +316,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid);
|
await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<object> InvokeNodeAsync(string nodeGuid)
|
||||||
|
{
|
||||||
|
return await currentFlowEnvironment.InvokeNodeAsync(nodeGuid);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task StartRemoteServerAsync(int port = 7525)
|
public async Task StartRemoteServerAsync(int port = 7525)
|
||||||
{
|
{
|
||||||
await currentFlowEnvironment.StartRemoteServerAsync(port);
|
await currentFlowEnvironment.StartRemoteServerAsync(port);
|
||||||
|
|||||||
@@ -107,14 +107,14 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="flowStateType"></param>
|
/// <param name="flowStateType"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="NotImplementedException"></exception>
|
/// <exception cref="NotImplementedException"></exception>
|
||||||
public static ConnectionType ToContentType(this FlipflopStateType flowStateType)
|
public static ConnectionInvokeType ToContentType(this FlipflopStateType flowStateType)
|
||||||
{
|
{
|
||||||
return flowStateType switch
|
return flowStateType switch
|
||||||
{
|
{
|
||||||
FlipflopStateType.Succeed => ConnectionType.IsSucceed,
|
FlipflopStateType.Succeed => ConnectionInvokeType.IsSucceed,
|
||||||
FlipflopStateType.Fail => ConnectionType.IsFail,
|
FlipflopStateType.Fail => ConnectionInvokeType.IsFail,
|
||||||
FlipflopStateType.Error => ConnectionType.IsError,
|
FlipflopStateType.Error => ConnectionInvokeType.IsError,
|
||||||
FlipflopStateType.Cancel => ConnectionType.None,
|
FlipflopStateType.Cancel => ConnectionInvokeType.None,
|
||||||
_ => throw new NotImplementedException("未定义的流程状态")
|
_ => throw new NotImplementedException("未定义的流程状态")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -126,11 +126,11 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool NotExitPreviousNode(this SingleFlipflopNode node)
|
public static bool NotExitPreviousNode(this SingleFlipflopNode node)
|
||||||
{
|
{
|
||||||
ConnectionType[] ct = [ConnectionType.IsSucceed,
|
ConnectionInvokeType[] ct = [ConnectionInvokeType.IsSucceed,
|
||||||
ConnectionType.IsFail,
|
ConnectionInvokeType.IsFail,
|
||||||
ConnectionType.IsError,
|
ConnectionInvokeType.IsError,
|
||||||
ConnectionType.Upstream];
|
ConnectionInvokeType.Upstream];
|
||||||
foreach (ConnectionType ctType in ct)
|
foreach (ConnectionInvokeType ctType in ct)
|
||||||
{
|
{
|
||||||
if (node.PreviousNodes[ctType].Count > 0)
|
if (node.PreviousNodes[ctType].Count > 0)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -340,7 +340,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)]
|
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectNode)]
|
||||||
public async Task<object> ConnectNode(string fromNodeGuid, string toNodeGuid, string connectionType)
|
public async Task<object> ConnectNode(string fromNodeGuid, string toNodeGuid, string connectionType)
|
||||||
{
|
{
|
||||||
if (!EnumHelper.TryConvertEnum<ConnectionType>(connectionType, out var tmpConnectionType))
|
if (!EnumHelper.TryConvertEnum<ConnectionInvokeType>(connectionType, out var tmpConnectionType))
|
||||||
{
|
{
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
@@ -348,7 +348,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
//environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType);
|
//environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType);
|
||||||
var result = await environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, tmpConnectionType);
|
var result = await environment.ConnectNodeAsync(fromNodeGuid, toNodeGuid,0,0, tmpConnectionType,0);
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
state = result
|
state = result
|
||||||
@@ -365,7 +365,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)]
|
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveConnect)]
|
||||||
public async Task<object> RemoveConnect(string fromNodeGuid, string toNodeGuid, string connectionType)
|
public async Task<object> RemoveConnect(string fromNodeGuid, string toNodeGuid, string connectionType)
|
||||||
{
|
{
|
||||||
if (!EnumHelper.TryConvertEnum<ConnectionType>(connectionType, out var tmpConnectionType))
|
if (!EnumHelper.TryConvertEnum<ConnectionInvokeType>(connectionType, out var tmpConnectionType))
|
||||||
{
|
{
|
||||||
return new
|
return new
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -218,18 +218,18 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<(ConnectionType connectionType, string[] guids)> allToNodes = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
|
List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes),
|
||||||
(ConnectionType.IsFail, nodeInfo.FalseNodes),
|
(ConnectionInvokeType.IsFail, nodeInfo.FalseNodes),
|
||||||
(ConnectionType.IsError, nodeInfo.ErrorNodes),
|
(ConnectionInvokeType.IsError, nodeInfo.ErrorNodes),
|
||||||
(ConnectionType.Upstream, nodeInfo.UpstreamNodes)];
|
(ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)];
|
||||||
|
|
||||||
List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
||||||
.Select(info => (info.connectionType,
|
.Select(info => (info.connectionType,
|
||||||
info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid])
|
info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid])
|
||||||
.ToArray()))
|
.ToArray()))
|
||||||
.ToList();
|
.ToList();
|
||||||
// 遍历每种类型的节点分支(四种)
|
// 遍历每种类型的节点分支(四种)
|
||||||
foreach ((ConnectionType connectionType, NodeModelBase[] toNodes) item in fromNodes)
|
foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in fromNodes)
|
||||||
{
|
{
|
||||||
// 遍历当前类型分支的节点(确认连接关系)
|
// 遍历当前类型分支的节点(确认连接关系)
|
||||||
foreach (var toNode in item.toNodes)
|
foreach (var toNode in item.toNodes)
|
||||||
@@ -237,6 +237,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
||||||
toNode.Guid,
|
toNode.Guid,
|
||||||
|
JunctionOfConnectionType.Invoke,
|
||||||
item.connectionType,
|
item.connectionType,
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点
|
NodeConnectChangeEventArgs.ConnectChangeType.Create))); // 通知UI连接节点
|
||||||
//OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
//OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
||||||
@@ -273,7 +274,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConnectNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionType connectionType)
|
private void ConnectNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
if (fromNode is null || toNode is null || fromNode == toNode)
|
if (fromNode is null || toNode is null || fromNode == toNode)
|
||||||
{
|
{
|
||||||
@@ -282,13 +283,13 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
var ToExistOnFrom = true;
|
var ToExistOnFrom = true;
|
||||||
var FromExistInTo = true;
|
var FromExistInTo = true;
|
||||||
ConnectionType[] ct = [ConnectionType.IsSucceed,
|
ConnectionInvokeType[] ct = [ConnectionInvokeType.IsSucceed,
|
||||||
ConnectionType.IsFail,
|
ConnectionInvokeType.IsFail,
|
||||||
ConnectionType.IsError,
|
ConnectionInvokeType.IsError,
|
||||||
ConnectionType.Upstream];
|
ConnectionInvokeType.Upstream];
|
||||||
|
|
||||||
|
|
||||||
foreach (ConnectionType ctType in ct)
|
foreach (ConnectionInvokeType ctType in ct)
|
||||||
{
|
{
|
||||||
var FToTo = fromNode.SuccessorNodes[ctType].Where(it => it.Guid.Equals(toNode.Guid)).ToArray();
|
var FToTo = fromNode.SuccessorNodes[ctType].Where(it => it.Guid.Equals(toNode.Guid)).ToArray();
|
||||||
var ToOnF = toNode.PreviousNodes[ctType].Where(it => it.Guid.Equals(fromNode.Guid)).ToArray();
|
var ToOnF = toNode.PreviousNodes[ctType].Where(it => it.Guid.Equals(fromNode.Guid)).ToArray();
|
||||||
@@ -325,6 +326,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支
|
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支
|
||||||
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
|
||||||
toNode.Guid,
|
toNode.Guid,
|
||||||
|
JunctionOfConnectionType.Invoke,
|
||||||
connectionType,
|
connectionType,
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
|
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
|
||||||
}
|
}
|
||||||
@@ -424,7 +426,22 @@ namespace Serein.NodeFlow.Env
|
|||||||
//UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid,nodeGuid)));
|
//UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid,nodeGuid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionType connectionType)
|
public async Task<object> InvokeNodeAsync(string nodeGuid)
|
||||||
|
{
|
||||||
|
Console.WriteLine("远程环境尚未实现接口 InvokeNodeAsync");
|
||||||
|
_ = msgClient.SendAsync(EnvMsgTheme.SetStartNode, new
|
||||||
|
{
|
||||||
|
nodeGuid
|
||||||
|
});
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> ConnectNodeAsync(string fromNodeGuid,
|
||||||
|
string toNodeGuid,
|
||||||
|
JunctionType fromNodeJunctionType,
|
||||||
|
JunctionType toNodeJunctionType,
|
||||||
|
ConnectionInvokeType connectionType,
|
||||||
|
int argIndex = 0)
|
||||||
{
|
{
|
||||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ConnectNode, new
|
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ConnectNode, new
|
||||||
{
|
{
|
||||||
@@ -438,6 +455,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
|
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
|
||||||
toNodeGuid,
|
toNodeGuid,
|
||||||
|
JunctionOfConnectionType.Invoke,
|
||||||
connectionType,
|
connectionType,
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
|
NodeConnectChangeEventArgs.ConnectChangeType.Create)); // 通知UI
|
||||||
}
|
}
|
||||||
@@ -472,7 +490,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
return nodeInfo;
|
return nodeInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
public async Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveConnect, new
|
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveConnect, new
|
||||||
{
|
{
|
||||||
@@ -486,6 +504,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
|
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNodeGuid,
|
||||||
toNodeGuid,
|
toNodeGuid,
|
||||||
|
JunctionOfConnectionType.Invoke,
|
||||||
connectionType,
|
connectionType,
|
||||||
NodeConnectChangeEventArgs.ConnectChangeType.Remote));
|
NodeConnectChangeEventArgs.ConnectChangeType.Remote));
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -351,9 +351,9 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task
|
var newFlowData = await singleFlipFlopNode.ExecutingAsync(context); // 获取触发器等待Task
|
||||||
await NodeModelBase.RefreshFlowDataAndExpInterrupt(context, singleFlipFlopNode, newFlowData); // 全局触发器触发后刷新该触发器的节点数据
|
await NodeModelBase.RefreshFlowDataAndExpInterrupt(context, singleFlipFlopNode, newFlowData); // 全局触发器触发后刷新该触发器的节点数据
|
||||||
if (singleFlipFlopNode.NextOrientation != ConnectionType.None)
|
if (context.NextOrientation != ConnectionInvokeType.None)
|
||||||
{
|
{
|
||||||
var nextNodes = singleFlipFlopNode.SuccessorNodes[singleFlipFlopNode.NextOrientation];
|
var nextNodes = singleFlipFlopNode.SuccessorNodes[context.NextOrientation];
|
||||||
for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--)
|
for (int i = nextNodes.Count - 1; i >= 0 && !_flipFlopCts.IsCancellationRequested; i--)
|
||||||
{
|
{
|
||||||
// 筛选出启用的节点
|
// 筛选出启用的节点
|
||||||
|
|||||||
@@ -35,10 +35,10 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
if (MethodDetails is null) return null;
|
if (MethodDetails is null) return null;
|
||||||
|
|
||||||
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
|
var trueNodes = SuccessorNodes[ConnectionInvokeType.IsSucceed].Select(item => item.Guid); // 真分支
|
||||||
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
|
var falseNodes = SuccessorNodes[ConnectionInvokeType.IsFail].Select(item => item.Guid);// 假分支
|
||||||
var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
|
var errorNodes = SuccessorNodes[ConnectionInvokeType.IsError].Select(item => item.Guid);// 异常分支
|
||||||
var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
|
var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支
|
||||||
// 生成参数列表
|
// 生成参数列表
|
||||||
Parameterdata[] parameterData = GetParameterdatas();
|
Parameterdata[] parameterData = GetParameterdatas();
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ namespace Serein.NodeFlow.Model
|
|||||||
foreach (SingleConditionNode? node in ConditionNodes)
|
foreach (SingleConditionNode? node in ConditionNodes)
|
||||||
{
|
{
|
||||||
var state = await JudgeAsync(context, node);
|
var state = await JudgeAsync(context, node);
|
||||||
NextOrientation = state; // 每次判读完成后,设置区域后继方向为判断结果
|
context.NextOrientation = state; // 每次判读完成后,设置区域后继方向为判断结果
|
||||||
if (state != ConnectionType.IsSucceed)
|
if (state != ConnectionInvokeType.IsSucceed)
|
||||||
{
|
{
|
||||||
// 如果条件不通过,立刻推出循环
|
// 如果条件不通过,立刻推出循环
|
||||||
break;
|
break;
|
||||||
@@ -62,19 +62,19 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private async Task<ConnectionType> JudgeAsync(IDynamicContext context, SingleConditionNode node)
|
private async Task<ConnectionInvokeType> JudgeAsync(IDynamicContext context, SingleConditionNode node)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await node.ExecutingAsync(context);
|
await node.ExecutingAsync(context);
|
||||||
return node.NextOrientation;
|
return context.NextOrientation;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
Console.WriteLine(ex.Message);
|
Console.WriteLine(ex.Message);
|
||||||
NextOrientation = ConnectionType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
RuningException = ex;
|
RuningException = ex;
|
||||||
return ConnectionType.IsError;
|
return ConnectionInvokeType.IsError;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -91,10 +91,10 @@ namespace Serein.NodeFlow.Model
|
|||||||
//var falseNodes = FailBranch.Select(item => item.Guid);// 假分支
|
//var falseNodes = FailBranch.Select(item => item.Guid);// 假分支
|
||||||
//var upstreamNodes = UpstreamBranch.Select(item => item.Guid);// 上游分支
|
//var upstreamNodes = UpstreamBranch.Select(item => item.Guid);// 上游分支
|
||||||
//var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支
|
//var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支
|
||||||
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
|
var trueNodes = SuccessorNodes[ConnectionInvokeType.IsSucceed].Select(item => item.Guid); // 真分支
|
||||||
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
|
var falseNodes = SuccessorNodes[ConnectionInvokeType.IsFail].Select(item => item.Guid);// 假分支
|
||||||
var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
|
var errorNodes = SuccessorNodes[ConnectionInvokeType.IsError].Select(item => item.Guid);// 异常分支
|
||||||
var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
|
var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支
|
||||||
|
|
||||||
// 生成参数列表
|
// 生成参数列表
|
||||||
Parameterdata[] parameterData = GetParameterdatas();
|
Parameterdata[] parameterData = GetParameterdatas();
|
||||||
|
|||||||
@@ -76,15 +76,15 @@ namespace Serein.NodeFlow.Model
|
|||||||
{
|
{
|
||||||
|
|
||||||
var isPass = SereinConditionParser.To(parameter, Expression);
|
var isPass = SereinConditionParser.To(parameter, Expression);
|
||||||
NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail;
|
context.NextOrientation = isPass ? ConnectionInvokeType.IsSucceed : ConnectionInvokeType.IsFail;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
NextOrientation = ConnectionType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
RuningException = ex;
|
RuningException = ex;
|
||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($"{result} {Expression} -> " + NextOrientation);
|
Console.WriteLine($"{result} {Expression} -> " + context.NextOrientation);
|
||||||
return Task.FromResult(result);
|
return Task.FromResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -47,12 +47,12 @@ namespace Serein.NodeFlow.Model
|
|||||||
result = data;
|
result = data;
|
||||||
}
|
}
|
||||||
|
|
||||||
NextOrientation = ConnectionType.IsSucceed;
|
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||||
return Task.FromResult(result);
|
return Task.FromResult(result);
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
NextOrientation = ConnectionType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
RuningException = ex;
|
RuningException = ex;
|
||||||
return Task.FromResult(data);
|
return Task.FromResult(data);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,11 +42,11 @@ namespace Serein.NodeFlow.Model
|
|||||||
object instance = md.ActingInstance;
|
object instance = md.ActingInstance;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var args = GetParameters(context, this, md);
|
var args = await GetParametersAsync(context, this, md);
|
||||||
var result = await dd.InvokeAsync(md.ActingInstance, args);
|
var result = await dd.InvokeAsync(md.ActingInstance, args);
|
||||||
dynamic flipflopContext = result;
|
dynamic flipflopContext = result;
|
||||||
FlipflopStateType flipflopStateType = flipflopContext.State;
|
FlipflopStateType flipflopStateType = flipflopContext.State;
|
||||||
NextOrientation = flipflopStateType.ToContentType();
|
context.NextOrientation = flipflopStateType.ToContentType();
|
||||||
if (flipflopContext.Type == TriggerType.Overtime)
|
if (flipflopContext.Type == TriggerType.Overtime)
|
||||||
{
|
{
|
||||||
throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
throw new FlipflopException(base.MethodDetails.MethodName + "触发器超时触发。Guid" + base.Guid);
|
||||||
@@ -61,14 +61,14 @@ namespace Serein.NodeFlow.Model
|
|||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
||||||
NextOrientation = ConnectionType.None;
|
context.NextOrientation = ConnectionInvokeType.None;
|
||||||
RuningException = ex;
|
RuningException = ex;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
await Console.Out.WriteLineAsync($"触发器[{this.MethodDetails.MethodName}]异常:" + ex);
|
||||||
NextOrientation = ConnectionType.IsError;
|
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||||
RuningException = ex;
|
RuningException = ex;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -118,6 +118,7 @@ namespace Serein.Library.NodeGenerator
|
|||||||
|
|
||||||
// 生成命名空间和类的开始部分
|
// 生成命名空间和类的开始部分
|
||||||
sb.AppendLine($"using System;");
|
sb.AppendLine($"using System;");
|
||||||
|
sb.AppendLine($"using System.Linq;");
|
||||||
sb.AppendLine($"using System.Threading;");
|
sb.AppendLine($"using System.Threading;");
|
||||||
sb.AppendLine($"using System.Threading.Tasks;");
|
sb.AppendLine($"using System.Threading.Tasks;");
|
||||||
sb.AppendLine($"using System.Collections.Concurrent;");
|
sb.AppendLine($"using System.Collections.Concurrent;");
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ using Serein.Library.Api;
|
|||||||
using Serein.Library.Utils;
|
using Serein.Library.Utils;
|
||||||
using Serein.Library.Utils.SereinExpression;
|
using Serein.Library.Utils.SereinExpression;
|
||||||
using Serein.NodeFlow.Tool;
|
using Serein.NodeFlow.Tool;
|
||||||
|
using Serein.Workbench.Node;
|
||||||
using Serein.Workbench.Node.View;
|
using Serein.Workbench.Node.View;
|
||||||
using Serein.Workbench.Node.ViewModel;
|
using Serein.Workbench.Node.ViewModel;
|
||||||
using Serein.Workbench.Themes;
|
using Serein.Workbench.Themes;
|
||||||
|
using System;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
@@ -118,11 +120,11 @@ namespace Serein.Workbench
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前正在绘制的连接线
|
/// 当前正在绘制的连接线
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Line? currentLine;
|
//private Line? currentLine;
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前正在绘制的真假分支属性
|
/// 当前正在绘制的真假分支属性
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private ConnectionType currentConnectionType;
|
private ConnectionInvokeType currentConnectionType;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -229,7 +231,6 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 窗体加载方法
|
#region 窗体加载方法
|
||||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
@@ -250,7 +251,7 @@ namespace Serein.Workbench
|
|||||||
InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Height);// 设置画布大小
|
InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Height);// 设置画布大小
|
||||||
foreach (var connection in Connections)
|
foreach (var connection in Connections)
|
||||||
{
|
{
|
||||||
connection.AddOrRefreshLine(); // 窗体完成加载后试图刷新所有连接线
|
connection.RefreshLine(); // 窗体完成加载后试图刷新所有连接线
|
||||||
}
|
}
|
||||||
|
|
||||||
var canvasData = project.Basic.Canvas;
|
var canvasData = project.Basic.Canvas;
|
||||||
@@ -358,44 +359,145 @@ namespace Serein.Workbench
|
|||||||
{
|
{
|
||||||
string fromNodeGuid = eventArgs.FromNodeGuid;
|
string fromNodeGuid = eventArgs.FromNodeGuid;
|
||||||
string toNodeGuid = eventArgs.ToNodeGuid;
|
string toNodeGuid = eventArgs.ToNodeGuid;
|
||||||
if (!TryGetControl(fromNodeGuid, out var fromNode)
|
if (!TryGetControl(fromNodeGuid, out var fromNodeControl)
|
||||||
|| !TryGetControl(toNodeGuid, out var toNode))
|
|| !TryGetControl(toNodeGuid, out var toNodeControl))
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ConnectionType connectionType = eventArgs.ConnectionType;
|
|
||||||
if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接
|
if (eventArgs.JunctionOfConnectionType == JunctionOfConnectionType.Invoke)
|
||||||
{
|
{
|
||||||
// 添加连接
|
#region 创建/删除节点之间的调用关系
|
||||||
var connection = new ConnectionControl(EnvDecorator, FlowChartCanvas, connectionType, fromNode, toNode);
|
ConnectionInvokeType connectionType = eventArgs.ConnectionInvokeType;
|
||||||
|
if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接
|
||||||
if (toNode is FlipflopNodeControl flipflopControl
|
|
||||||
&& flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
|
|
||||||
{
|
{
|
||||||
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
|
if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
|
||||||
|
{
|
||||||
|
Console.WriteLine("非预期的情况");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
JunctionControlBase startJunction = IFormJunction.NextStepJunction;
|
||||||
|
JunctionControlBase endJunction = IToJunction.ExecuteJunction;
|
||||||
|
|
||||||
|
// 添加连接
|
||||||
|
var connection = new ConnectionControl(
|
||||||
|
FlowChartCanvas,
|
||||||
|
connectionType,
|
||||||
|
startJunction,
|
||||||
|
endJunction,
|
||||||
|
() => EnvDecorator.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connectionType)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (toNodeControl is FlipflopNodeControl flipflopControl
|
||||||
|
&& flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
|
||||||
|
{
|
||||||
|
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
|
||||||
|
}
|
||||||
|
connection.RefreshLine(); // 添加贝塞尔曲线显示
|
||||||
|
Connections.Add(connection);
|
||||||
|
fromNodeControl.AddCnnection(connection);
|
||||||
|
toNodeControl.AddCnnection(connection);
|
||||||
|
EndConnection();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
connection.InvalidateVisual(); // 添加贝塞尔曲线显示
|
else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remote) // 移除连接
|
||||||
|
{
|
||||||
Connections.Add(connection);
|
// 需要移除连接
|
||||||
EndConnection();
|
var removeConnections = Connections.Where(c => c.Start.MyNode.Guid.Equals(fromNodeGuid)
|
||||||
|
&& c.End.MyNode.Guid.Equals(toNodeGuid))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var connection in removeConnections)
|
||||||
|
{
|
||||||
|
connection.DeleteConnection();
|
||||||
|
Connections.Remove(connection);
|
||||||
|
fromNodeControl.RemoveCnnection(connection);
|
||||||
|
toNodeControl.RemoveCnnection(connection);
|
||||||
|
if(NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control))
|
||||||
|
{
|
||||||
|
JudgmentFlipFlopNode(control); // 连接关系变更时判断
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remote) // 移除连接
|
else
|
||||||
{
|
{
|
||||||
// 需要移除连接
|
#region 创建/删除节点之间的参数传递关系
|
||||||
var removeConnections = Connections.Where(c => c.Start.ViewModel.NodeModel.Guid.Equals(fromNodeGuid)
|
ConnectionArgSourceType connectionArgSourceType = eventArgs.ConnectionArgSourceType;
|
||||||
&& c.End.ViewModel.NodeModel.Guid.Equals(toNodeGuid))
|
if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接
|
||||||
.ToList();
|
|
||||||
|
|
||||||
|
|
||||||
foreach (var connection in removeConnections)
|
|
||||||
{
|
{
|
||||||
connection.RemoveFromCanvas();
|
if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
|
||||||
Connections.Remove(connection);
|
{
|
||||||
JudgmentFlipFlopNode(connection.End); // 连接关系变更时判断
|
Console.WriteLine("非预期的情况");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
JunctionControlBase startJunction = eventArgs.ConnectionArgSourceType switch
|
||||||
|
{
|
||||||
|
ConnectionArgSourceType.GetPreviousNodeData => IFormJunction.ExecuteJunction, // 自身节点
|
||||||
|
ConnectionArgSourceType.GetOtherNodeData => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
|
||||||
|
ConnectionArgSourceType.GetOtherNodeDataOfInvoke => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
|
||||||
|
_ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发
|
||||||
|
};
|
||||||
|
|
||||||
|
JunctionControlBase endJunction = IToJunction.ArgDataJunction[eventArgs.ArgIndex];
|
||||||
|
LineType lineType = LineType.Bezier;
|
||||||
|
if(eventArgs.ConnectionArgSourceType == ConnectionArgSourceType.GetPreviousNodeData)
|
||||||
|
{
|
||||||
|
lineType = LineType.Semicircle;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加连接
|
||||||
|
var connection = new ConnectionControl(
|
||||||
|
lineType,
|
||||||
|
FlowChartCanvas,
|
||||||
|
eventArgs.ArgIndex,
|
||||||
|
eventArgs.ConnectionArgSourceType,
|
||||||
|
startJunction,
|
||||||
|
endJunction,
|
||||||
|
() => EnvDecorator.RemoveConnectAsync(fromNodeGuid, toNodeGuid, 0)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (toNodeControl is FlipflopNodeControl flipflopControl
|
||||||
|
&& flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
|
||||||
|
{
|
||||||
|
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
|
||||||
|
}
|
||||||
|
connection.RefreshLine(); // 添加贝塞尔曲线显示
|
||||||
|
Connections.Add(connection);
|
||||||
|
fromNodeControl.AddCnnection(connection);
|
||||||
|
toNodeControl.AddCnnection(connection);
|
||||||
|
EndConnection();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remote) // 移除连接
|
||||||
|
{
|
||||||
|
// 需要移除连接
|
||||||
|
var removeConnections = Connections.Where(c => c.Start.MyNode.Guid.Equals(fromNodeGuid)
|
||||||
|
&& c.End.MyNode.Guid.Equals(toNodeGuid))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
|
||||||
|
foreach (var connection in removeConnections)
|
||||||
|
{
|
||||||
|
connection.DeleteConnection();
|
||||||
|
Connections.Remove(connection);
|
||||||
|
fromNodeControl.RemoveCnnection(connection);
|
||||||
|
toNodeControl.RemoveCnnection(connection);
|
||||||
|
if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control))
|
||||||
|
{
|
||||||
|
JudgmentFlipFlopNode(control); // 连接关系变更时判断
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -434,6 +536,7 @@ namespace Serein.Workbench
|
|||||||
}
|
}
|
||||||
|
|
||||||
FlowChartCanvas.Children.Remove(nodeControl);
|
FlowChartCanvas.Children.Remove(nodeControl);
|
||||||
|
nodeControl.RemoveAllConection();
|
||||||
NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid);
|
NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -724,7 +827,7 @@ namespace Serein.Workbench
|
|||||||
private void FlowEnvironment_OnNodeMoved(NodeMovedEventArgs eventArgs)
|
private void FlowEnvironment_OnNodeMoved(NodeMovedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl)) return;
|
if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl)) return;
|
||||||
UpdateConnections(nodeControl);
|
nodeControl.UpdateLocationConnections();
|
||||||
|
|
||||||
//var newLeft = eventArgs.X;
|
//var newLeft = eventArgs.X;
|
||||||
//var newTop = eventArgs.Y;
|
//var newTop = eventArgs.Y;
|
||||||
@@ -859,35 +962,34 @@ namespace Serein.Workbench
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 开始创建连接 True线 操作,设置起始块和绘制连接线。
|
/// 开始创建连接 True线 操作,设置起始块和绘制连接线。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void StartConnection(NodeControlBase startNodeControl, ConnectionType connectionType)
|
//private void StartConnection(NodeControlBase startNodeControl, ConnectionInvokeType connectionType)
|
||||||
{
|
//{
|
||||||
var tf = Connections.FirstOrDefault(it => it.Start == startNodeControl)?.Type;
|
// var tf = Connections.FirstOrDefault(it => it.Start.MyNode.Guid == startNodeControl.ViewModel.NodeModel.Guid)?.Type;
|
||||||
IsConnecting = true;
|
// IsConnecting = true;
|
||||||
currentConnectionType = connectionType;
|
// currentConnectionType = connectionType;
|
||||||
startConnectNodeControl = startNodeControl;
|
// startConnectNodeControl = startNodeControl;
|
||||||
|
|
||||||
// 确保起点和终点位置的正确顺序
|
// // 确保起点和终点位置的正确顺序
|
||||||
currentLine = new Line
|
// currentLine = new Line
|
||||||
{
|
// {
|
||||||
Stroke = connectionType == ConnectionType.IsSucceed ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"))
|
// Stroke = connectionType == ConnectionInvokeType.IsSucceed ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"))
|
||||||
: connectionType == ConnectionType.IsFail ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905"))
|
// : connectionType == ConnectionInvokeType.IsFail ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905"))
|
||||||
: connectionType == ConnectionType.IsError ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B"))
|
// : connectionType == ConnectionInvokeType.IsError ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B"))
|
||||||
: new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
|
// : new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
|
||||||
StrokeDashArray = new DoubleCollection([2]),
|
// StrokeDashArray = new DoubleCollection([2]),
|
||||||
StrokeThickness = 2,
|
// StrokeThickness = 2,
|
||||||
X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2,
|
// X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2,
|
||||||
Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2,
|
// Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2,
|
||||||
X2 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2, // 初始时终点与起点重合
|
// X2 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2, // 初始时终点与起点重合
|
||||||
Y2 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2,
|
// Y2 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2,
|
||||||
};
|
// };
|
||||||
|
|
||||||
FlowChartCanvas.Children.Add(currentLine);
|
// FlowChartCanvas.Children.Add(currentLine);
|
||||||
this.KeyDown += MainWindow_KeyDown;
|
// this.KeyDown += MainWindow_KeyDown;
|
||||||
}
|
//}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
@@ -963,10 +1065,10 @@ namespace Serein.Workbench
|
|||||||
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => EnvDecorator.SetStartNode(nodeGuid)));
|
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => EnvDecorator.SetStartNode(nodeGuid)));
|
||||||
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => EnvDecorator.RemoveNodeAsync(nodeGuid)));
|
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => EnvDecorator.RemoveNodeAsync(nodeGuid)));
|
||||||
|
|
||||||
contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsSucceed)));
|
//contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection(nodeControl, ConnectionInvokeType.IsSucceed)));
|
||||||
contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsFail)));
|
//contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection(nodeControl, ConnectionInvokeType.IsFail)));
|
||||||
contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsError)));
|
//contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection(nodeControl, ConnectionInvokeType.IsError)));
|
||||||
contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection(nodeControl, ConnectionType.Upstream)));
|
//contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection(nodeControl, ConnectionInvokeType.Upstream)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1028,21 +1130,6 @@ namespace Serein.Workbench
|
|||||||
Console.WriteLine(ex);
|
Console.WriteLine(ex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//private void DisplayFlowDataTreeViewer(object @object)
|
|
||||||
//{
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// var typeViewerWindow = new ViewObjectViewerWindow();
|
|
||||||
// typeViewerWindow.LoadObjectInformation(@object);
|
|
||||||
// typeViewerWindow.Show();
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
// Console.WriteLine(ex);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 拖拽DLL文件到左侧功能区,加载相关节点清单
|
#region 拖拽DLL文件到左侧功能区,加载相关节点清单
|
||||||
@@ -1090,30 +1177,34 @@ namespace Serein.Workbench
|
|||||||
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
|
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
if (e.LeftButton == MouseButtonState.Pressed && GlobalJunctionData.MyGlobalData is not null)
|
if (e.LeftButton == MouseButtonState.Pressed && GlobalJunctionData.MyGlobalConnectingData is not null)
|
||||||
{
|
{
|
||||||
// 正在连接节点
|
// 正在连接节点
|
||||||
var virtualLine = GlobalJunctionData.MyGlobalData.VirtualLine;
|
//var controlPointPosition = GlobalJunctionData.MyGlobalConnectingData.StartPoint;
|
||||||
var controlPointPosition = GlobalJunctionData.MyGlobalData.StartPoint;
|
|
||||||
var currentPoint = e.GetPosition(FlowChartCanvas);
|
var currentPoint = e.GetPosition(FlowChartCanvas);
|
||||||
virtualLine.VirtualLine.X1 = controlPointPosition.X;
|
GlobalJunctionData.MyGlobalConnectingData.UpdatePoint(currentPoint);
|
||||||
virtualLine.VirtualLine.Y1 = controlPointPosition.Y;
|
|
||||||
virtualLine.VirtualLine.X2 = currentPoint.X;
|
|
||||||
virtualLine.VirtualLine.Y2 = currentPoint.Y;
|
//virtualLine.VirtualLine.UpdatePoints(currentPoint);
|
||||||
|
|
||||||
|
//virtualLine.VirtualLine.X1 = controlPointPosition.X;
|
||||||
|
//virtualLine.VirtualLine.Y1 = controlPointPosition.Y;
|
||||||
|
//virtualLine.VirtualLine.X2 = currentPoint.X;
|
||||||
|
//virtualLine.VirtualLine.Y2 = currentPoint.Y;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (IsConnecting) // 正在连接节点
|
//if (IsConnecting) // 正在连接节点
|
||||||
{
|
//{
|
||||||
Point position = e.GetPosition(FlowChartCanvas);
|
// Point position = e.GetPosition(FlowChartCanvas);
|
||||||
if (currentLine is null || startConnectNodeControl is null)
|
// if (currentLine is null || startConnectNodeControl is null)
|
||||||
{
|
// {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
currentLine.X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2;
|
// currentLine.X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2;
|
||||||
currentLine.Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2;
|
// currentLine.Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2;
|
||||||
currentLine.X2 = position.X;
|
// currentLine.X2 = position.X;
|
||||||
currentLine.Y2 = position.Y;
|
// currentLine.Y2 = position.Y;
|
||||||
}
|
//}
|
||||||
|
|
||||||
if (IsCanvasDragging && e.MiddleButton == MouseButtonState.Pressed) // 正在移动画布(按住中键)
|
if (IsCanvasDragging && e.MiddleButton == MouseButtonState.Pressed) // 正在移动画布(按住中键)
|
||||||
{
|
{
|
||||||
@@ -1128,7 +1219,7 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
foreach (var line in Connections)
|
foreach (var line in Connections)
|
||||||
{
|
{
|
||||||
line.AddOrRefreshLine(); // 画布移动时刷新所有连接线
|
line.RefreshLine(); // 画布移动时刷新所有连接线
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1288,10 +1379,10 @@ namespace Serein.Workbench
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void Block_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
private void Block_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (GlobalJunctionData.IsCreatingConnection)
|
//if (GlobalJunctionData.IsCreatingConnection)
|
||||||
{
|
//{
|
||||||
return;
|
// return;
|
||||||
}
|
//}
|
||||||
if(sender is NodeControlBase nodeControl)
|
if(sender is NodeControlBase nodeControl)
|
||||||
{
|
{
|
||||||
ChangeViewerObjOfNode(nodeControl);
|
ChangeViewerObjOfNode(nodeControl);
|
||||||
@@ -1302,6 +1393,7 @@ namespace Serein.Workbench
|
|||||||
e.Handled = true; // 防止事件传播影响其他控件
|
e.Handled = true; // 防止事件传播影响其他控件
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。
|
/// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1353,7 +1445,7 @@ namespace Serein.Workbench
|
|||||||
// 更新节点之间线的连接位置
|
// 更新节点之间线的连接位置
|
||||||
foreach (var nodeControl in selectNodeControls)
|
foreach (var nodeControl in selectNodeControls)
|
||||||
{
|
{
|
||||||
UpdateConnections(nodeControl);
|
nodeControl.UpdateLocationConnections();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -1367,7 +1459,7 @@ namespace Serein.Workbench
|
|||||||
double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距
|
double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距
|
||||||
double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距
|
double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距
|
||||||
this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
|
this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
|
||||||
UpdateConnections(nodeControl);
|
nodeControl.UpdateLocationConnections();
|
||||||
}
|
}
|
||||||
startControlDragPoint = currentPosition; // 更新起始点位置
|
startControlDragPoint = currentPosition; // 更新起始点位置
|
||||||
}
|
}
|
||||||
@@ -1429,18 +1521,17 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsConnecting)
|
//if (IsConnecting)
|
||||||
{
|
//{
|
||||||
var formNodeGuid = startConnectNodeControl?.ViewModel.NodeModel.Guid;
|
// var formNodeGuid = startConnectNodeControl?.ViewModel.NodeModel.Guid;
|
||||||
var toNodeGuid = (sender as NodeControlBase)?.ViewModel.NodeModel.Guid;
|
// var toNodeGuid = (sender as NodeControlBase)?.ViewModel.NodeModel.Guid;
|
||||||
if (string.IsNullOrEmpty(formNodeGuid) || string.IsNullOrEmpty(toNodeGuid))
|
// if (string.IsNullOrEmpty(formNodeGuid) || string.IsNullOrEmpty(toNodeGuid))
|
||||||
{
|
// {
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
EnvDecorator.ConnectNodeAsync(formNodeGuid, toNodeGuid, currentConnectionType);
|
// EnvDecorator.ConnectNodeAsync(formNodeGuid, toNodeGuid,0,0, currentConnectionType);
|
||||||
}
|
//}
|
||||||
|
//GlobalJunctionData.OK();
|
||||||
GlobalJunctionData.OK();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1463,28 +1554,30 @@ namespace Serein.Workbench
|
|||||||
IsConnecting = false;
|
IsConnecting = false;
|
||||||
startConnectNodeControl = null;
|
startConnectNodeControl = null;
|
||||||
// 移除虚线
|
// 移除虚线
|
||||||
if (currentLine != null)
|
//if (currentLine != null)
|
||||||
{
|
//{
|
||||||
FlowChartCanvas.Children.Remove(currentLine);
|
// FlowChartCanvas.Children.Remove(currentLine);
|
||||||
currentLine = null;
|
// currentLine = null;
|
||||||
}
|
//}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新与指定控件相关的所有连接的位置。
|
/// 更新与指定控件相关的所有连接的位置。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateConnections(NodeControlBase nodeControl)
|
//private void UpdateConnections(NodeControlBase nodeControl)
|
||||||
{
|
//{
|
||||||
foreach (var connection in Connections)
|
// nodeControl.UpdateLocationConnections();
|
||||||
{
|
// //foreach (var connection in Connections)
|
||||||
if (connection.Start == nodeControl || connection.End == nodeControl)
|
// //{
|
||||||
{
|
// // if (connection.Start.MyNode.Guid == nodeControl.ViewModel.NodeModel.Guid
|
||||||
connection.AddOrRefreshLine(); // 主动更新某个控件相关的所有连接线
|
// // || connection.End.MyNode.Guid == nodeControl.ViewModel.NodeModel.Guid)
|
||||||
//connection.RemoveFromCanvas();
|
// // {
|
||||||
//BezierLineDrawer.UpdateBezierLine(FlowChartCanvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
|
// // connection.RefreshLine(); // 主动更新某个控件相关的所有连接线
|
||||||
}
|
// // //connection.RemoveFromCanvas();
|
||||||
}
|
// // //BezierLineDrawer.UpdateBezierLine(FlowChartCanvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
|
||||||
}
|
// // }
|
||||||
|
// //}
|
||||||
|
//}
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 拖动画布实现缩放平移效果
|
#region 拖动画布实现缩放平移效果
|
||||||
@@ -1688,7 +1781,7 @@ namespace Serein.Workbench
|
|||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
if (GlobalJunctionData.IsCreatingConnection)
|
if (GlobalJunctionData.MyGlobalConnectingData is not null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -1724,7 +1817,7 @@ namespace Serein.Workbench
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在画布中释放鼠标按下,结束选取状态
|
/// 在画布中释放鼠标按下,结束选取状态 / 停止创建连线,尝试连接节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="sender"></param>
|
/// <param name="sender"></param>
|
||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
@@ -1744,26 +1837,36 @@ namespace Serein.Workbench
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 创建连线
|
// 创建连线
|
||||||
if (GlobalJunctionData.MyGlobalData is not null)
|
if (GlobalJunctionData.MyGlobalConnectingData is not null)
|
||||||
{
|
{
|
||||||
var myData = GlobalJunctionData.MyGlobalData;
|
var myData = GlobalJunctionData.MyGlobalConnectingData;
|
||||||
|
GlobalJunctionData.OK();
|
||||||
var canvas = this.FlowChartCanvas;
|
var canvas = this.FlowChartCanvas;
|
||||||
if (GlobalJunctionData.CanCreate)
|
if (myData.IsCanConnected)
|
||||||
{
|
{
|
||||||
//var startPoint = myDataType.StartPoint;
|
|
||||||
var currentendPoint = e.GetPosition(canvas); // 当前鼠标落点
|
var currentendPoint = e.GetPosition(canvas); // 当前鼠标落点
|
||||||
var changingJunctionPosition = myData.ChangingJunction.TranslatePoint(new Point(0, 0), canvas);
|
var changingJunctionPosition = myData.CurrentJunction.TranslatePoint(new Point(0, 0), canvas);
|
||||||
var changingJunctionRect = new Rect(changingJunctionPosition, new Size(myData.ChangingJunction.Width, myData.ChangingJunction.Height));
|
var changingJunctionRect = new Rect(changingJunctionPosition, new Size(myData.CurrentJunction.Width, myData.CurrentJunction.Height));
|
||||||
|
|
||||||
if (changingJunctionRect.Contains(currentendPoint))
|
if (changingJunctionRect.Contains(currentendPoint)) // 可以创建连接
|
||||||
{
|
{
|
||||||
this.EnvDecorator.ConnectNodeAsync(myData.StartJunction.NodeGuid, myData.ChangingJunction.NodeGuid, ConnectionType.IsSucceed);
|
var argIndex = 0;
|
||||||
}
|
if(myData.StartJunction is ArgJunctionControl argJunction1)
|
||||||
|
{
|
||||||
|
argIndex = argJunction1.ArgIndex;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (myData.CurrentJunction is ArgJunctionControl argJunction2)
|
||||||
|
{
|
||||||
|
argIndex = argJunction2.ArgIndex;
|
||||||
|
}
|
||||||
|
this.EnvDecorator.ConnectNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
|
||||||
|
myData.StartJunction.JunctionType,
|
||||||
|
myData.CurrentJunction.JunctionType,
|
||||||
|
ConnectionInvokeType.IsSucceed,argIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalJunctionData.OK();
|
|
||||||
}
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
|
|
||||||
@@ -2628,7 +2731,7 @@ namespace Serein.Workbench
|
|||||||
FlowChartCanvas.Children.Clear();
|
FlowChartCanvas.Children.Clear();
|
||||||
Connections.Clear();
|
Connections.Clear();
|
||||||
NodeControls.Clear();
|
NodeControls.Clear();
|
||||||
currentLine = null;
|
//currentLine = null;
|
||||||
startConnectNodeControl = null;
|
startConnectNodeControl = null;
|
||||||
MessageBox.Show("所有DLL已卸载。", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
|
MessageBox.Show("所有DLL已卸载。", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,14 +10,14 @@
|
|||||||
d:DataContext="{d:DesignInstance vm:ActionNodeControlViewModel}"
|
d:DataContext="{d:DesignInstance vm:ActionNodeControlViewModel}"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
MaxWidth="300">
|
MaxWidth="300">
|
||||||
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<!--<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />-->
|
<!--<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />-->
|
||||||
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
||||||
<!--<ResourceDictionary Source="/Serein.Workbench;Node/View/NodeExecuteJunctionControl.xaml" x:Key="NodeExecuteJunctionControl"/>-->
|
<!--<ResourceDictionary Source="/Serein.Workbench;Node/View/NodeExecuteJunctionControl.xaml" x:Key="NodeExecuteJunctionControl"/>-->
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
<Border BorderBrush="#8DE9FD" BorderThickness="1">
|
<Border BorderBrush="#8DE9FD" BorderThickness="4">
|
||||||
|
|
||||||
|
|
||||||
<Grid>
|
<Grid>
|
||||||
@@ -28,10 +28,10 @@
|
|||||||
|
|
||||||
<!--<TextBlock Text="{Binding NodelModel.DebugSetting.IsInterrupt}}"></TextBlock>-->
|
<!--<TextBlock Text="{Binding NodelModel.DebugSetting.IsInterrupt}}"></TextBlock>-->
|
||||||
<Border x:Name="InterruptBorder">
|
<Border x:Name="InterruptBorder">
|
||||||
|
|
||||||
<Border.Style>
|
<Border.Style>
|
||||||
<Style TargetType="Border">
|
<Style TargetType="Border">
|
||||||
<!--默认无边框-->
|
<!--默认无边框-->
|
||||||
<Setter Property="BorderBrush" Value="Transparent" />
|
<Setter Property="BorderBrush" Value="Transparent" />
|
||||||
<Setter Property="BorderThickness" Value="0" />
|
<Setter Property="BorderThickness" Value="0" />
|
||||||
<Style.Triggers>
|
<Style.Triggers>
|
||||||
@@ -52,24 +52,32 @@
|
|||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Grid Grid.Row="0" Background="#8DE9FD" >
|
|
||||||
|
<!--<Grid Grid.Row="0" Background="#8DE9FD" >-->
|
||||||
|
<Grid Grid.Row="0" >
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="3*"/>
|
||||||
|
<RowDefinition/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="auto"/>
|
||||||
<ColumnDefinition Width="*"/>
|
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<local:ExecuteJunctionControl Grid.Column="0" NodeGuid="{Binding NodeModel.Guid}" x:Name="ExecuteJunctionControl"/>
|
<local:ExecuteJunctionControl Grid.Column="0" MyNode="{Binding NodeModel}" x:Name="ExecuteJunctionControl" HorizontalAlignment="Left" Grid.RowSpan="2"/>
|
||||||
<TextBlock Grid.Column="1" Text="{Binding NodeModel.MethodDetails.MethodTips, Mode=TwoWay}" HorizontalAlignment="Center"/>
|
<StackPanel Grid.Column="1" Grid.RowSpan="2" >
|
||||||
<local:ResultJunctionControl Grid.Column="2" NodeGuid="{Binding NodeModel.Guid}" x:Name="ResultJunctionControl"/>
|
<TextBlock Text="{Binding NodeModel.MethodDetails.MethodTips, Mode=TwoWay}" HorizontalAlignment="Center"/>
|
||||||
|
</StackPanel>
|
||||||
|
<local:NextStepJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="NextStepJunctionControl" HorizontalAlignment="Right" Grid.RowSpan="2"/>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|
||||||
<!--<StackPanel Background="#8DE9FD" >
|
<!--<StackPanel Background="#8DE9FD" >
|
||||||
|
|
||||||
</StackPanel>-->
|
</StackPanel>-->
|
||||||
|
|
||||||
<themes:MethodDetailsControl Grid.Row="2" MethodDetails="{Binding NodeModel.MethodDetails}"/>
|
<themes:MethodDetailsControl x:Name="MethodDetailsControl" Grid.Row="2" MethodDetails="{Binding NodeModel.MethodDetails}"/>
|
||||||
<!-- ParameterProtectionMask 参数保护 -->
|
<!-- ParameterProtectionMask 参数保护 -->
|
||||||
<!--取反 Visibility="{Binding DebugSetting.IsEnable, Converter={StaticResource InvertedBoolConverter}, ConverterParameter=Inverted}"-->
|
<!--取反 Visibility="{Binding DebugSetting.IsEnable, Converter={StaticResource InvertedBoolConverter}, ConverterParameter=Inverted}"-->
|
||||||
<Border Grid.Row="2" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
|
<Border Grid.Row="2" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
|
||||||
@@ -79,16 +87,21 @@
|
|||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<Border Grid.Column="0" BorderThickness="1">
|
<Border Grid.Column="0" BorderThickness="1">
|
||||||
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
<TextBlock Text="result ->" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||||
</Border>
|
</Border>
|
||||||
<Border Grid.Column="1" BorderThickness="1">
|
<Border Grid.Column="1" BorderThickness="1">
|
||||||
<TextBlock Text="{Binding NodeModel.MethodDetails.ReturnType.FullName, Mode=OneTime}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<TextBlock Text="{Binding NodeModel.MethodDetails.ReturnType.FullName, Mode=OneTime}" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
|
</Border>
|
||||||
|
|
||||||
|
<Border Grid.Column="2" BorderThickness="1">
|
||||||
|
<local:ResultJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="ResultJunctionControl" HorizontalAlignment="Right"/>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
<Grid Grid.Row="4" Background="#8DE9FD" >
|
<Grid Grid.Row="4" Background="Azure" >
|
||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
|
|||||||
@@ -1,21 +1,94 @@
|
|||||||
using Serein.NodeFlow.Model;
|
using Serein.NodeFlow.Model;
|
||||||
using Serein.Workbench.Node.ViewModel;
|
using Serein.Workbench.Node.ViewModel;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace Serein.Workbench.Node.View
|
namespace Serein.Workbench.Node.View
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// ActionNode.xaml 的交互逻辑
|
/// ActionNode.xaml 的交互逻辑
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ActionNodeControl : NodeControlBase
|
public partial class ActionNodeControl : NodeControlBase, INodeJunction
|
||||||
{
|
{
|
||||||
public ActionNodeControl(ActionNodeControlViewModel viewModel) : base(viewModel)
|
public ActionNodeControl(ActionNodeControlViewModel viewModel) : base(viewModel)
|
||||||
{
|
{
|
||||||
DataContext = viewModel;
|
DataContext = viewModel;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
ExecuteJunctionControl.NodeGuid = viewModel.NodeModel.Guid;
|
ExecuteJunctionControl.MyNode.Guid = viewModel.NodeModel.Guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 入参控制点(可能有,可能没)
|
||||||
|
/// </summary>
|
||||||
|
JunctionControlBase INodeJunction.ExecuteJunction => this.ExecuteJunctionControl;
|
||||||
|
/// <summary>
|
||||||
|
/// 下一个调用方法控制点(可能有,可能没)
|
||||||
|
/// </summary>
|
||||||
|
|
||||||
|
JunctionControlBase INodeJunction.NextStepJunction => this.NextStepJunctionControl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 返回值控制点(可能有,可能没)
|
||||||
|
/// </summary>
|
||||||
|
JunctionControlBase INodeJunction.ReturnDataJunction => this.ResultJunctionControl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方法入参控制点(可能有,可能没)
|
||||||
|
/// </summary>
|
||||||
|
JunctionControlBase[] INodeJunction.ArgDataJunction { get {
|
||||||
|
if(argDataJunction == null)
|
||||||
|
{
|
||||||
|
// 获取 MethodDetailsControl 实例
|
||||||
|
var methodDetailsControl = this.MethodDetailsControl;
|
||||||
|
argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
|
||||||
|
|
||||||
|
var itemsControl = FindVisualChild<ItemsControl>(methodDetailsControl); // 查找 ItemsControl
|
||||||
|
if (itemsControl != null)
|
||||||
|
{
|
||||||
|
var controls = new List<JunctionControlBase>();
|
||||||
|
|
||||||
|
for (int i = 0; i < itemsControl.Items.Count; i++)
|
||||||
|
{
|
||||||
|
var container = itemsControl.ItemContainerGenerator.ContainerFromIndex(i) as FrameworkElement;
|
||||||
|
if (container != null)
|
||||||
|
{
|
||||||
|
var argControl = FindVisualChild<ArgJunctionControl>(container);
|
||||||
|
if (argControl != null)
|
||||||
|
{
|
||||||
|
controls.Add(argControl); // 收集 ArgJunctionControl 实例
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
argDataJunction = controls.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return argDataJunction;
|
||||||
|
} }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 方法入参控制点(可能有,可能没)
|
||||||
|
/// </summary>
|
||||||
|
private JunctionControlBase[] argDataJunction;
|
||||||
|
|
||||||
|
private T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
|
||||||
|
{
|
||||||
|
for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
|
||||||
|
{
|
||||||
|
var child = VisualTreeHelper.GetChild(parent, i);
|
||||||
|
if (child is T typedChild)
|
||||||
|
{
|
||||||
|
return typedChild;
|
||||||
|
}
|
||||||
|
|
||||||
|
var childOfChild = FindVisualChild<T>(child);
|
||||||
|
if (childOfChild != null)
|
||||||
|
{
|
||||||
|
return childOfChild;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,25 +28,27 @@
|
|||||||
<Setter Property="ContentTemplate">
|
<Setter Property="ContentTemplate">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Background="#E3FDFD">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Grid.ColumnDefinitions>
|
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
|
||||||
<ColumnDefinition Width="auto"/>
|
<Grid Background="#E3FDFD">
|
||||||
<ColumnDefinition Width="50"/>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="30"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="30"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="50"/>
|
||||||
</Grid.ColumnDefinitions>
|
<ColumnDefinition Width="*"/>
|
||||||
<view:ArgJunctionControl Grid.Column="0" ArgIndex="{Binding Index}" NodeGuid="{Binding NodeModel.Guid}" />
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="1" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
<CheckBox Grid.Column="2" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="3" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="4" MinWidth="50" Text="无须指定参数"/>
|
<TextBlock Grid.Column="3" MinWidth="50" Text="无须指定参数"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
</MultiDataTrigger>
|
</MultiDataTrigger>
|
||||||
|
|
||||||
<!--指定参数:选项类型-->
|
<!--指定参数:选项类型-->
|
||||||
<MultiDataTrigger>
|
<MultiDataTrigger>
|
||||||
<MultiDataTrigger.Conditions>
|
<MultiDataTrigger.Conditions>
|
||||||
@@ -56,23 +58,24 @@
|
|||||||
<Setter Property="ContentTemplate">
|
<Setter Property="ContentTemplate">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Background="#E3FDFD">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Grid.ColumnDefinitions>
|
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
|
||||||
<ColumnDefinition Width="auto"/>
|
<Grid Background="#E3FDFD">
|
||||||
<ColumnDefinition Width="50"/>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="30"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="30"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="50"/>
|
||||||
</Grid.ColumnDefinitions>
|
<ColumnDefinition Width="*"/>
|
||||||
<view:ArgJunctionControl Grid.Column="0" ArgIndex="{Binding Index}" NodeGuid="{Binding NodeModel.Guid}" />
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="1" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
<CheckBox Grid.Column="2" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="3" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
<ComboBox Grid.Column="4"
|
<ComboBox Grid.Column="3"
|
||||||
MinWidth="50"
|
MinWidth="50"
|
||||||
ItemsSource="{Binding Items}"
|
ItemsSource="{Binding Items}"
|
||||||
SelectedItem="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
SelectedItem="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
@@ -86,20 +89,22 @@
|
|||||||
<Setter Property="ContentTemplate">
|
<Setter Property="ContentTemplate">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Background="#E3FDFD">
|
<StackPanel Orientation="Horizontal">
|
||||||
<Grid.ColumnDefinitions>
|
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
|
||||||
<ColumnDefinition Width="auto"/>
|
<Grid Background="#E3FDFD">
|
||||||
<ColumnDefinition Width="50"/>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="30"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="30"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="50"/>
|
||||||
</Grid.ColumnDefinitions>
|
<ColumnDefinition Width="*"/>
|
||||||
<view:ArgJunctionControl Grid.Column="0" ArgIndex="{Binding Index}" NodeGuid="{Binding NodeModel.Guid}" />
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="1" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
||||||
<CheckBox Grid.Column="2" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="3" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
||||||
<TextBox Grid.Column="4" MinWidth="50" Text="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
</Grid>
|
<TextBox Grid.Column="3" MinWidth="50" Text="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
|
</Grid>
|
||||||
|
</StackPanel>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ namespace Serein.Workbench.Themes
|
|||||||
private class NodeTreeModel
|
private class NodeTreeModel
|
||||||
{
|
{
|
||||||
public NodeModelBase RootNode { get; set; }
|
public NodeModelBase RootNode { get; set; }
|
||||||
public Dictionary<ConnectionType, List<NodeModelBase>> ChildNodes { get; set; }
|
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> ChildNodes { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -48,12 +48,12 @@ namespace Serein.Workbench.Themes
|
|||||||
NodeTreeModel nodeTreeModel = new NodeTreeModel
|
NodeTreeModel nodeTreeModel = new NodeTreeModel
|
||||||
{
|
{
|
||||||
RootNode = rootNodeModel,
|
RootNode = rootNodeModel,
|
||||||
ChildNodes = new Dictionary<ConnectionType, List<NodeModelBase>>()
|
ChildNodes = new Dictionary<ConnectionInvokeType, List<NodeModelBase>>()
|
||||||
{
|
{
|
||||||
{ConnectionType.Upstream, []},
|
{ConnectionInvokeType.Upstream, []},
|
||||||
{ConnectionType.IsSucceed, [rootNodeModel]},
|
{ConnectionInvokeType.IsSucceed, [rootNodeModel]},
|
||||||
{ConnectionType.IsFail, []},
|
{ConnectionInvokeType.IsFail, []},
|
||||||
{ConnectionType.IsError, []},
|
{ConnectionInvokeType.IsError, []},
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
string? itemName = rootNodeModel.MethodDetails?.MethodTips;
|
string? itemName = rootNodeModel.MethodDetails?.MethodTips;
|
||||||
@@ -231,25 +231,25 @@ namespace Serein.Workbench.Themes
|
|||||||
item.Items.Clear();
|
item.Items.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public static TreeView ToTreeView(NodeTreeItemViewControl item, ConnectionType connectionType)
|
public static TreeView ToTreeView(NodeTreeItemViewControl item, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
return connectionType switch
|
return connectionType switch
|
||||||
{
|
{
|
||||||
ConnectionType.Upstream => item.UpstreamTreeNodes,
|
ConnectionInvokeType.Upstream => item.UpstreamTreeNodes,
|
||||||
ConnectionType.IsError => item.IsErrorTreeNodes,
|
ConnectionInvokeType.IsError => item.IsErrorTreeNodes,
|
||||||
ConnectionType.IsFail => item.IsFailTreeNodes,
|
ConnectionInvokeType.IsFail => item.IsFailTreeNodes,
|
||||||
ConnectionType.IsSucceed => item.IsSucceedTreeNodes,
|
ConnectionInvokeType.IsSucceed => item.IsSucceedTreeNodes,
|
||||||
_ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType)
|
_ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public static Grid ToGridView(NodeTreeItemViewControl item, ConnectionType connectionType)
|
public static Grid ToGridView(NodeTreeItemViewControl item, ConnectionInvokeType connectionType)
|
||||||
{
|
{
|
||||||
return connectionType switch
|
return connectionType switch
|
||||||
{
|
{
|
||||||
ConnectionType.Upstream => item.UpstreamTreeGuid,
|
ConnectionInvokeType.Upstream => item.UpstreamTreeGuid,
|
||||||
ConnectionType.IsError => item.IsErrorTreeGuid,
|
ConnectionInvokeType.IsError => item.IsErrorTreeGuid,
|
||||||
ConnectionType.IsFail => item.IsFailTreeGuid,
|
ConnectionInvokeType.IsFail => item.IsFailTreeGuid,
|
||||||
ConnectionType.IsSucceed => item.IsSucceedTreeGuid,
|
ConnectionInvokeType.IsSucceed => item.IsSucceedTreeGuid,
|
||||||
_ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType)
|
_ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
34
Workbench/Node/INodeJunction.cs
Normal file
34
Workbench/Node/INodeJunction.cs
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
using Serein.Workbench.Node.View;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 约束一个节点应该有哪些控制点
|
||||||
|
/// </summary>
|
||||||
|
public interface INodeJunction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 方法执行入口控制点
|
||||||
|
/// </summary>
|
||||||
|
JunctionControlBase ExecuteJunction { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// 执行完成后下一个要执行的方法控制点
|
||||||
|
/// </summary>
|
||||||
|
JunctionControlBase NextStepJunction { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参数节点控制点
|
||||||
|
/// </summary>
|
||||||
|
JunctionControlBase[] ArgDataJunction { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// 返回值控制点
|
||||||
|
/// </summary>
|
||||||
|
JunctionControlBase ReturnDataJunction { get; }
|
||||||
|
}
|
||||||
|
}
|
||||||
222
Workbench/Node/Junction/BezierLine.cs
Normal file
222
Workbench/Node/Junction/BezierLine.cs
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
using Serein.Library;
|
||||||
|
using Serein.Workbench.Extension;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Net;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node.View
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 连接线的类型
|
||||||
|
/// </summary>
|
||||||
|
public enum LineType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 贝塞尔曲线
|
||||||
|
/// </summary>
|
||||||
|
Bezier,
|
||||||
|
/// <summary>
|
||||||
|
/// 半圆线
|
||||||
|
/// </summary>
|
||||||
|
Semicircle,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 贝塞尔曲线
|
||||||
|
/// </summary>
|
||||||
|
public class BezierLine : Shape
|
||||||
|
{
|
||||||
|
private readonly double strokeThickness;
|
||||||
|
|
||||||
|
private readonly LineType lineType;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 确定起始坐标和目标坐标、外光样式的曲线
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start"></param>
|
||||||
|
/// <param name="end"></param>
|
||||||
|
/// <param name="brush"></param>
|
||||||
|
/// <param name="strokeThickness"></param>
|
||||||
|
public BezierLine(LineType lineType, Point start, Point end, Brush brush, double strokeThickness = 4)
|
||||||
|
{
|
||||||
|
this.lineType = lineType;
|
||||||
|
this.brush = brush;
|
||||||
|
startPoint = start;
|
||||||
|
endPoint = end;
|
||||||
|
this.strokeThickness = strokeThickness;
|
||||||
|
InitElementPoint();
|
||||||
|
InvalidateVisual(); // 触发重绘
|
||||||
|
}
|
||||||
|
public void InitElementPoint()
|
||||||
|
{
|
||||||
|
hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
|
||||||
|
hitVisiblePen.Freeze(); // Freeze以提高性能
|
||||||
|
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
|
||||||
|
visualPen.Freeze(); // Freeze以提高性能
|
||||||
|
linkSize = 4; // 整线条粗细
|
||||||
|
Panel.SetZIndex(this, -9999999); // 置底
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新线条落点位置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="start"></param>
|
||||||
|
/// <param name="end"></param>
|
||||||
|
public void UpdatePoints(Point start, Point end)
|
||||||
|
{
|
||||||
|
startPoint = start;
|
||||||
|
endPoint = end;
|
||||||
|
InvalidateVisual(); // 触发重绘
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新线条落点位置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point"></param>
|
||||||
|
public void UpdateEndPoints(Point point)
|
||||||
|
{
|
||||||
|
endPoint = point;
|
||||||
|
InvalidateVisual(); // 触发重绘
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 更新线条落点位置
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point"></param>
|
||||||
|
public void UpdateStartPoints(Point point)
|
||||||
|
{
|
||||||
|
startPoint = point;
|
||||||
|
InvalidateVisual(); // 触发重绘
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 控件重绘事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawingContext"></param>
|
||||||
|
protected override void OnRender(DrawingContext drawingContext)
|
||||||
|
{
|
||||||
|
// 刷新线条显示位置
|
||||||
|
switch (this.lineType)
|
||||||
|
{
|
||||||
|
case LineType.Bezier:
|
||||||
|
DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize);
|
||||||
|
break;
|
||||||
|
case LineType.Semicircle:
|
||||||
|
DrawBezierCurve(drawingContext, startPoint, endPoint);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly StreamGeometry streamGeometry = new StreamGeometry();
|
||||||
|
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
|
||||||
|
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
|
||||||
|
private Pen hitVisiblePen; // 初始化碰撞检测线
|
||||||
|
private Pen visualPen; // 默认可视化Pen
|
||||||
|
private Point startPoint; // 连接线的起始节点
|
||||||
|
private Point endPoint; // 连接线的终点
|
||||||
|
private Brush brush; // 线条颜色
|
||||||
|
double linkSize; // 根据缩放比例调整线条粗细
|
||||||
|
protected override Geometry DefiningGeometry => streamGeometry;
|
||||||
|
|
||||||
|
#region 工具方法
|
||||||
|
|
||||||
|
|
||||||
|
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
|
||||||
|
private Vector axis = new Vector(1, 0);
|
||||||
|
private Vector startToEnd;
|
||||||
|
private void DrawBezierCurve(DrawingContext drawingContext,
|
||||||
|
Point start,
|
||||||
|
Point end,
|
||||||
|
double linkSize,
|
||||||
|
bool isHitTestVisible = false,
|
||||||
|
double strokeThickness = 1.0,
|
||||||
|
bool isMouseOver = false,
|
||||||
|
double dashOffset = 0.0)
|
||||||
|
{
|
||||||
|
// 控制点的计算逻辑
|
||||||
|
double power = 100; // 控制贝塞尔曲线的“拉伸”强度
|
||||||
|
|
||||||
|
// 计算轴向向量与起点到终点的向量
|
||||||
|
//var axis = new Vector(1, 0);
|
||||||
|
startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
|
||||||
|
|
||||||
|
// 计算拉伸程度k,拉伸与水平夹角正相关
|
||||||
|
var k = 1 - Math.Pow(Math.Max(0, axis.DotProduct(startToEnd)), 10.0);
|
||||||
|
|
||||||
|
// 如果起点x大于终点x,增加额外的偏移量,避免重叠
|
||||||
|
var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
|
||||||
|
|
||||||
|
// 控制点的实际计算
|
||||||
|
c0 = new Point(+(power + bias) * k + start.X, start.Y);
|
||||||
|
c1 = new Point(-(power + bias) * k + end.X, end.Y);
|
||||||
|
|
||||||
|
// 准备StreamGeometry以用于绘制曲线
|
||||||
|
streamGeometry.Clear();
|
||||||
|
using (var context = streamGeometry.Open())
|
||||||
|
{
|
||||||
|
context.BeginFigure(start, true, false); // 曲线起点
|
||||||
|
context.BezierTo(c0, c1, end, true, false); // 画贝塞尔曲线
|
||||||
|
}
|
||||||
|
|
||||||
|
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
|
||||||
|
|
||||||
|
// 绘制碰撞检测线
|
||||||
|
//if (true)
|
||||||
|
//{
|
||||||
|
// //hitVisiblePen = new Pen(Brushes.Transparent, linkSize + strokeThickness);
|
||||||
|
// //hitVisiblePen.Freeze();
|
||||||
|
// drawingContext.DrawGeometry(null, hitVisiblePen, streamGeometry);
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
private void DrawBezierCurve(DrawingContext drawingContext, Point start, Point end)
|
||||||
|
{
|
||||||
|
// 计算中心点和半径
|
||||||
|
// 计算圆心和半径
|
||||||
|
double x = 35 ;
|
||||||
|
// 创建一个弧线路径
|
||||||
|
streamGeometry.Clear();
|
||||||
|
using (var context = streamGeometry.Open())
|
||||||
|
{
|
||||||
|
// 开始绘制
|
||||||
|
context.BeginFigure(start, false, false);
|
||||||
|
|
||||||
|
// 生成弧线
|
||||||
|
context.ArcTo(
|
||||||
|
end, // 结束点
|
||||||
|
new Size(x, x), // 椭圆的半径
|
||||||
|
0, // 椭圆的旋转角度
|
||||||
|
false, // 是否大弧
|
||||||
|
SweepDirection.Counterclockwise, // 方向
|
||||||
|
true, // 是否连接到起始点
|
||||||
|
true // 是否使用高质量渲染
|
||||||
|
);
|
||||||
|
|
||||||
|
// 结束绘制
|
||||||
|
context.LineTo(start, false, false); // 连接到起始点(可选)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 绘制弧线
|
||||||
|
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
using Serein.Library;
|
|
||||||
using Serein.Library.Utils;
|
|
||||||
using System;
|
|
||||||
using System.Windows;
|
|
||||||
using System.Windows.Controls;
|
|
||||||
using System.Windows.Input;
|
|
||||||
using System.Windows.Media;
|
|
||||||
using System.Windows.Shapes;
|
|
||||||
|
|
||||||
namespace Serein.Workbench.Node.View
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class JunctionControlBase : UserControl
|
|
||||||
{
|
|
||||||
|
|
||||||
public double _MyWidth = 20;
|
|
||||||
public double _MyHeight = 20;
|
|
||||||
|
|
||||||
protected JunctionControlBase()
|
|
||||||
{
|
|
||||||
//this.Width = 20;
|
|
||||||
//this.Height = 20;
|
|
||||||
this.MouseDown += ControlPointBase_MouseDown;
|
|
||||||
this.MouseMove += ControlPointBase_MouseMove; ;
|
|
||||||
}
|
|
||||||
#region 控件属性,所在的节点
|
|
||||||
public static readonly DependencyProperty NodeGuidProperty =
|
|
||||||
DependencyProperty.Register("NodeGuid", typeof(string), typeof(JunctionControlBase), new PropertyMetadata(default(string)));
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 所在的节点
|
|
||||||
/// </summary>
|
|
||||||
public string NodeGuid
|
|
||||||
{
|
|
||||||
get { return (string)GetValue(NodeGuidProperty); }
|
|
||||||
set { SetValue(NodeGuidProperty, value.ToString()); }
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 控件属性,连接器类型
|
|
||||||
public static readonly DependencyProperty JunctionTypeProperty =
|
|
||||||
DependencyProperty.Register("JunctionType", typeof(string), typeof(JunctionControlBase), new PropertyMetadata(default(string)));
|
|
||||||
|
|
||||||
public JunctionType JunctionType
|
|
||||||
{
|
|
||||||
get { return EnumHelper.ConvertEnum<JunctionType>(GetValue(JunctionTypeProperty).ToString()); }
|
|
||||||
set { SetValue(JunctionTypeProperty, value.ToString()); }
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public abstract void Render();
|
|
||||||
private void ControlPointBase_MouseMove(object sender, MouseEventArgs e)
|
|
||||||
{
|
|
||||||
if (GlobalJunctionData.MyGlobalData is null) return;
|
|
||||||
GlobalJunctionData.MyGlobalData.ChangingJunction = this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 在碰撞点上按下鼠标控件开始进行移动
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
/// <param name="e"></param>
|
|
||||||
private void ControlPointBase_MouseDown(object sender, MouseButtonEventArgs e)
|
|
||||||
{
|
|
||||||
if (e.LeftButton == MouseButtonState.Pressed)
|
|
||||||
{
|
|
||||||
GlobalJunctionData.MyGlobalData = new ConnectingData();
|
|
||||||
var myDataType = GlobalJunctionData.MyGlobalData;
|
|
||||||
myDataType.StartJunction = this;
|
|
||||||
var canvas = MainWindow.GetParentOfType<Canvas>(this);
|
|
||||||
myDataType.StartPoint = this.TranslatePoint(new Point(this.Width /2 , this.Height /2 ), canvas);
|
|
||||||
myDataType.VirtualLine = new MyLine(canvas, new Line // 虚拟线
|
|
||||||
{
|
|
||||||
Stroke = Brushes.OldLace,
|
|
||||||
StrokeThickness = 2
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
134
Workbench/Node/Junction/JunctionControlBase.cs
Normal file
134
Workbench/Node/Junction/JunctionControlBase.cs
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
using Serein.Library;
|
||||||
|
using Serein.Library.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node.View
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class JunctionControlBase : Shape
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
protected JunctionControlBase()
|
||||||
|
{
|
||||||
|
this.Width = 25;
|
||||||
|
this.Height = 20;
|
||||||
|
this.MouseDown += ControlPointBase_MouseDown;
|
||||||
|
this.MouseMove += ControlPointBase_MouseMove;
|
||||||
|
}
|
||||||
|
#region 控件属性,所在的节点
|
||||||
|
public static readonly DependencyProperty NodeProperty =
|
||||||
|
DependencyProperty.Register(nameof(MyNode), typeof(NodeModelBase), typeof(JunctionControlBase), new PropertyMetadata(default(NodeModelBase)));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 所在的节点
|
||||||
|
/// </summary>
|
||||||
|
public NodeModelBase MyNode
|
||||||
|
{
|
||||||
|
get { return (NodeModelBase)GetValue(NodeProperty); }
|
||||||
|
set { SetValue(NodeProperty, value); }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 控件属性,连接器类型
|
||||||
|
public static readonly DependencyProperty JunctionTypeProperty =
|
||||||
|
DependencyProperty.Register(nameof(JunctionType), typeof(string), typeof(JunctionControlBase), new PropertyMetadata(default(string)));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控制点类型
|
||||||
|
/// </summary>
|
||||||
|
public JunctionType JunctionType
|
||||||
|
{
|
||||||
|
get { return EnumHelper.ConvertEnum<JunctionType>(GetValue(JunctionTypeProperty).ToString()); }
|
||||||
|
set { SetValue(JunctionTypeProperty, value.ToString()); }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
protected readonly StreamGeometry StreamGeometry = new StreamGeometry();
|
||||||
|
protected override Geometry DefiningGeometry => StreamGeometry;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重绘方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawingContext"></param>
|
||||||
|
public abstract void Render(DrawingContext drawingContext);
|
||||||
|
/// <summary>
|
||||||
|
/// 中心点
|
||||||
|
/// </summary>
|
||||||
|
public abstract Point MyCenterPoint { get; }
|
||||||
|
|
||||||
|
// 处理鼠标悬停状态
|
||||||
|
private bool _isMouseOver;
|
||||||
|
public bool IsMouseOver
|
||||||
|
{
|
||||||
|
get => _isMouseOver;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_isMouseOver = value;
|
||||||
|
InvalidateVisual();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控件重绘事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawingContext"></param>
|
||||||
|
protected override void OnRender(DrawingContext drawingContext)
|
||||||
|
{
|
||||||
|
Render(drawingContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
protected void ControlPointBase_MouseMove(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (GlobalJunctionData.MyGlobalConnectingData is null) return;
|
||||||
|
GlobalJunctionData.MyGlobalConnectingData.CurrentJunction = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在碰撞点上按下鼠标控件开始进行移动
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
protected void ControlPointBase_MouseDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.LeftButton == MouseButtonState.Pressed)
|
||||||
|
{
|
||||||
|
GlobalJunctionData.MyGlobalConnectingData = new ConnectingData();
|
||||||
|
var myDataType = GlobalJunctionData.MyGlobalConnectingData;
|
||||||
|
myDataType.StartJunction = this;
|
||||||
|
var canvas = MainWindow.GetParentOfType<Canvas>(this);
|
||||||
|
if (canvas != null)
|
||||||
|
{
|
||||||
|
//myDataType.StartPoint = this.MyCenterPoint;
|
||||||
|
myDataType.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas);
|
||||||
|
var bezierLine = new BezierLine(LineType.Bezier, myDataType.StartPoint, myDataType.StartPoint, Brushes.Green);
|
||||||
|
myDataType.VirtualLine = new MyLine(canvas, bezierLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Point GetStartPoint()
|
||||||
|
{
|
||||||
|
return new Point(this.ActualWidth / 2, this.ActualHeight / 2); // 起始节点选择右侧边缘中心
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace Serein.Workbench.Node.View
|
|||||||
#region Model,不科学的全局变量
|
#region Model,不科学的全局变量
|
||||||
public class MyLine
|
public class MyLine
|
||||||
{
|
{
|
||||||
public MyLine(Canvas canvas, Line line)
|
public MyLine(Canvas canvas, BezierLine line)
|
||||||
{
|
{
|
||||||
Canvas = canvas;
|
Canvas = canvas;
|
||||||
VirtualLine = line;
|
VirtualLine = line;
|
||||||
@@ -21,7 +21,7 @@ namespace Serein.Workbench.Node.View
|
|||||||
}
|
}
|
||||||
|
|
||||||
public Canvas Canvas { get; set; }
|
public Canvas Canvas { get; set; }
|
||||||
public Line VirtualLine { get; set; }
|
public BezierLine VirtualLine { get; set; }
|
||||||
|
|
||||||
public void Remove()
|
public void Remove()
|
||||||
{
|
{
|
||||||
@@ -32,33 +32,98 @@ namespace Serein.Workbench.Node.View
|
|||||||
public class ConnectingData
|
public class ConnectingData
|
||||||
{
|
{
|
||||||
public JunctionControlBase StartJunction { get; set; }
|
public JunctionControlBase StartJunction { get; set; }
|
||||||
public JunctionControlBase ChangingJunction { get; set; }
|
public JunctionControlBase CurrentJunction { get; set; }
|
||||||
public Point StartPoint { get; set; }
|
public Point StartPoint { get; set; }
|
||||||
public MyLine VirtualLine { get; set; }
|
public MyLine VirtualLine { get; set; }
|
||||||
}
|
|
||||||
|
|
||||||
public static class GlobalJunctionData
|
/// <summary>
|
||||||
{
|
/// 是否允许连接
|
||||||
private static ConnectingData? myGlobalData;
|
/// </summary>
|
||||||
|
|
||||||
public static ConnectingData? MyGlobalData
|
public bool IsCanConnected { get
|
||||||
{
|
|
||||||
get => myGlobalData;
|
|
||||||
set
|
|
||||||
{
|
{
|
||||||
if (myGlobalData == null)
|
if(StartJunction is null
|
||||||
|
|| CurrentJunction is null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
myGlobalData = value;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (!StartPoint.Equals(CurrentJunction))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 自己连接自己的情况下,只能是从arg控制点连接到execute控制点。
|
||||||
|
if (CurrentJunction.JunctionType == Library.JunctionType.Execute
|
||||||
|
&& StartJunction.JunctionType == Library.JunctionType.ArgData)
|
||||||
|
{
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CurrentJunction.JunctionType == Library.JunctionType.ArgData
|
||||||
|
&& StartJunction.JunctionType == Library.JunctionType.Execute)
|
||||||
|
{
|
||||||
|
// 需要是自己连接自己,且只能是从arg控制点连接到execute控制点。
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新临时的连接线
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="point"></param>
|
||||||
|
public void UpdatePoint(Point point)
|
||||||
|
{
|
||||||
|
if (StartJunction is null
|
||||||
|
|| CurrentJunction is null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (StartJunction.JunctionType == Library.JunctionType.Execute
|
||||||
|
|| StartJunction.JunctionType == Library.JunctionType.ArgData)
|
||||||
|
{
|
||||||
|
VirtualLine.VirtualLine.UpdateStartPoints(point);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
VirtualLine.VirtualLine.UpdateEndPoints(point);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static bool IsCreatingConnection => myGlobalData is not null;
|
}
|
||||||
|
|
||||||
public static bool CanCreate => myGlobalData?.ChangingJunction.Equals(myGlobalData?.StartJunction) == false;
|
public static class GlobalJunctionData
|
||||||
|
{
|
||||||
|
private static ConnectingData? myGlobalData;
|
||||||
|
private static object _lockObj = new object();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建节点之间控制点的连接行为
|
||||||
|
/// </summary>
|
||||||
|
public static ConnectingData? MyGlobalConnectingData
|
||||||
|
{
|
||||||
|
get => myGlobalData;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
lock (_lockObj)
|
||||||
|
{
|
||||||
|
myGlobalData ??= value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除连接视觉效果
|
||||||
|
/// </summary>
|
||||||
public static void OK()
|
public static void OK()
|
||||||
{
|
{
|
||||||
myGlobalData?.VirtualLine.Remove();
|
myGlobalData?.VirtualLine.Remove();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Serein.Workbench.Node.View
|
|||||||
public ArgJunctionControl()
|
public ArgJunctionControl()
|
||||||
{
|
{
|
||||||
base.JunctionType = JunctionType.ArgData;
|
base.JunctionType = JunctionType.ArgData;
|
||||||
Render();
|
this.InvalidateVisual();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 控件属性,对应的参数
|
#region 控件属性,对应的参数
|
||||||
@@ -25,28 +25,50 @@ namespace Serein.Workbench.Node.View
|
|||||||
get { return (int)GetValue(ArgIndexProperty); }
|
get { return (int)GetValue(ArgIndexProperty); }
|
||||||
set { SetValue(ArgIndexProperty, value); }
|
set { SetValue(ArgIndexProperty, value); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
private Point _myCenterPoint;
|
||||||
public override void Render()
|
public override Point MyCenterPoint { get => _myCenterPoint; }
|
||||||
|
public override void Render(DrawingContext drawingContext)
|
||||||
{
|
{
|
||||||
if(double.IsNaN(base.Width))
|
double width = ActualWidth;
|
||||||
{
|
double height = ActualHeight;
|
||||||
base.Width = base._MyWidth;
|
|
||||||
}
|
// 输入连接器的背景
|
||||||
if (double.IsNaN(base.Height))
|
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
|
||||||
{
|
var connectorRect = new Rect(4, 4, width - 8, height - 8);
|
||||||
base.Height = base._MyHeight;
|
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
|
||||||
}
|
|
||||||
|
// 定义圆形的大小和位置
|
||||||
|
double connectorSize = 10; // 连接器的大小
|
||||||
|
double circleCenterX = 8; // 圆心 X 坐标
|
||||||
|
double circleCenterY = height / 2; // 圆心 Y 坐标
|
||||||
|
var circlePoint = new Point(circleCenterX, circleCenterY);
|
||||||
|
|
||||||
|
// 绘制连接器的圆形部分
|
||||||
|
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
|
||||||
|
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY);
|
||||||
|
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
|
||||||
|
|
||||||
|
|
||||||
var ellipse = new Ellipse
|
|
||||||
|
|
||||||
|
// 定义三角形的间距
|
||||||
|
double triangleOffsetX = 4; // 三角形与圆形的间距
|
||||||
|
double triangleCenterX = circleCenterX + connectorSize / 2 + triangleOffsetX; // 三角形中心 X 坐标
|
||||||
|
double triangleCenterY = circleCenterY; // 三角形中心 Y 坐标
|
||||||
|
|
||||||
|
// 绘制三角形
|
||||||
|
var pathGeometry = new StreamGeometry();
|
||||||
|
using (var context = pathGeometry.Open())
|
||||||
{
|
{
|
||||||
Width = base.Width,
|
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - 4.5), true, true);
|
||||||
Height = base.Height,
|
context.LineTo(new Point(triangleCenterX + 5, triangleCenterY), true, false);
|
||||||
Fill = Brushes.Orange,
|
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
|
||||||
ToolTip = "入参"
|
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
|
||||||
};
|
}
|
||||||
Content = ellipse;
|
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using System.Windows.Media;
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
|
|
||||||
@@ -6,32 +8,75 @@ namespace Serein.Workbench.Node.View
|
|||||||
{
|
{
|
||||||
public class ExecuteJunctionControl : JunctionControlBase
|
public class ExecuteJunctionControl : JunctionControlBase
|
||||||
{
|
{
|
||||||
//public override JunctionType JunctionType { get; } = JunctionType.Execute;
|
|
||||||
|
|
||||||
|
|
||||||
public ExecuteJunctionControl()
|
public ExecuteJunctionControl()
|
||||||
{
|
{
|
||||||
base.JunctionType = JunctionType.Execute;
|
base.JunctionType = JunctionType.Execute;
|
||||||
Render();
|
this.InvalidateVisual();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
private Point _myCenterPoint;
|
||||||
public override void Render()
|
public override Point MyCenterPoint { get => _myCenterPoint; }
|
||||||
|
public override void Render(DrawingContext drawingContext)
|
||||||
{
|
{
|
||||||
if (double.IsNaN(base.Width))
|
double width = ActualWidth;
|
||||||
{
|
double height = ActualHeight;
|
||||||
base.Width = base._MyWidth;
|
|
||||||
}
|
|
||||||
if (double.IsNaN(base.Height))
|
|
||||||
{
|
|
||||||
base.Height = base._MyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rect = new Rectangle
|
// 绘制边框
|
||||||
|
//var borderBrush = new SolidColorBrush(Colors.Black);
|
||||||
|
//var borderThickness = 1.0;
|
||||||
|
//var borderRect = new Rect(0, 0, width, height);
|
||||||
|
//drawingContext.DrawRectangle(null, new Pen(borderBrush, borderThickness), borderRect);
|
||||||
|
|
||||||
|
// 输入连接器的背景
|
||||||
|
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
|
||||||
|
var connectorRect = new Rect(4, 4, width - 8, height - 8);
|
||||||
|
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
|
||||||
|
|
||||||
|
// 定义圆形的大小和位置
|
||||||
|
double connectorSize = 10; // 连接器的大小
|
||||||
|
double circleCenterX = 8; // 圆心 X 坐标
|
||||||
|
double circleCenterY = height / 2; // 圆心 Y 坐标
|
||||||
|
|
||||||
|
var circlePoint = new Point(circleCenterX, circleCenterY);
|
||||||
|
// 绘制连接器的圆形部分
|
||||||
|
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
|
||||||
|
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY);
|
||||||
|
|
||||||
|
|
||||||
|
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 定义三角形的间距
|
||||||
|
double triangleOffsetX = 4; // 三角形与圆形的间距
|
||||||
|
double triangleCenterX = circleCenterX + connectorSize / 2 + triangleOffsetX; // 三角形中心 X 坐标
|
||||||
|
double triangleCenterY = circleCenterY; // 三角形中心 Y 坐标
|
||||||
|
|
||||||
|
// 绘制三角形
|
||||||
|
var pathGeometry = new StreamGeometry();
|
||||||
|
using (var context = pathGeometry.Open())
|
||||||
{
|
{
|
||||||
Width = base.Width,
|
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - 4.5), true, true);
|
||||||
Height = base.Height,
|
context.LineTo(new Point(triangleCenterX + 5, triangleCenterY), true, false);
|
||||||
Fill = Brushes.Green,
|
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
|
||||||
ToolTip = "方法执行"
|
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
|
||||||
};
|
}
|
||||||
Content = rect;
|
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
|
||||||
|
|
||||||
|
// 绘制标签
|
||||||
|
//var formattedText = new FormattedText(
|
||||||
|
// "执行",
|
||||||
|
// System.Globalization.CultureInfo.CurrentCulture,
|
||||||
|
// FlowDirection.LeftToRight,
|
||||||
|
// new Typeface("Segoe UI"),
|
||||||
|
// 12,
|
||||||
|
// Brushes.Black,
|
||||||
|
// VisualTreeHelper.GetDpi(this).PixelsPerDip);
|
||||||
|
//drawingContext.DrawText(formattedText, new Point(18,1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Windows.Media;
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
|
|
||||||
@@ -11,28 +12,50 @@ namespace Serein.Workbench.Node.View
|
|||||||
public NextStepJunctionControl()
|
public NextStepJunctionControl()
|
||||||
{
|
{
|
||||||
base.JunctionType = JunctionType.NextStep;
|
base.JunctionType = JunctionType.NextStep;
|
||||||
Render();
|
this.InvalidateVisual();
|
||||||
}
|
}
|
||||||
|
private Point _myCenterPoint;
|
||||||
public override void Render()
|
public override Point MyCenterPoint { get => _myCenterPoint; }
|
||||||
|
public override void Render(DrawingContext drawingContext)
|
||||||
{
|
{
|
||||||
if (double.IsNaN(base.Width))
|
double width = ActualWidth;
|
||||||
{
|
double height = ActualHeight;
|
||||||
base.Width = base._MyWidth;
|
|
||||||
}
|
|
||||||
if (double.IsNaN(base.Height))
|
|
||||||
{
|
|
||||||
base.Height = base._MyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rect = new Rectangle
|
// 输入连接器的背景
|
||||||
|
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
|
||||||
|
var connectorRect = new Rect(4, 4, width - 8, height - 8);
|
||||||
|
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
|
||||||
|
|
||||||
|
// 定义圆形的大小和位置
|
||||||
|
double connectorSize = 10; // 连接器的大小
|
||||||
|
double circleCenterX = 8; // 圆心 X 坐标
|
||||||
|
double circleCenterY = height / 2; // 圆心 Y 坐标
|
||||||
|
|
||||||
|
var circlePoint = new Point(circleCenterX, circleCenterY);
|
||||||
|
// 绘制连接器的圆形部分
|
||||||
|
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
|
||||||
|
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
|
||||||
|
_myCenterPoint = new Point(circleCenterX + connectorSize / 2, circleCenterY);
|
||||||
|
|
||||||
|
// 绘制连接器的圆形部分
|
||||||
|
//var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
|
||||||
|
|
||||||
|
|
||||||
|
// 定义三角形的间距
|
||||||
|
double triangleOffsetX = 4; // 三角形与圆形的间距
|
||||||
|
double triangleCenterX = circleCenterX + connectorSize / 2 + triangleOffsetX; // 三角形中心 X 坐标
|
||||||
|
double triangleCenterY = circleCenterY; // 三角形中心 Y 坐标
|
||||||
|
|
||||||
|
// 绘制三角形
|
||||||
|
var pathGeometry = new StreamGeometry();
|
||||||
|
using (var context = pathGeometry.Open())
|
||||||
{
|
{
|
||||||
Width = base.Width,
|
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - 4.5), true, true);
|
||||||
Height = base.Height,
|
context.LineTo(new Point(triangleCenterX + 5, triangleCenterY), true, false);
|
||||||
Fill = Brushes.Blue,
|
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
|
||||||
ToolTip = "下一个方法值"
|
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
|
||||||
};
|
}
|
||||||
Content = rect;
|
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Windows.Media;
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
|
|
||||||
@@ -12,28 +13,47 @@ namespace Serein.Workbench.Node.View
|
|||||||
public ResultJunctionControl()
|
public ResultJunctionControl()
|
||||||
{
|
{
|
||||||
base.JunctionType = JunctionType.ReturnData;
|
base.JunctionType = JunctionType.ReturnData;
|
||||||
Render();
|
this.InvalidateVisual();
|
||||||
}
|
}
|
||||||
|
private Point _myCenterPoint;
|
||||||
|
public override Point MyCenterPoint { get => _myCenterPoint; }
|
||||||
|
|
||||||
public override void Render()
|
public override void Render(DrawingContext drawingContext)
|
||||||
{
|
{
|
||||||
if (double.IsNaN(base.Width))
|
double width = ActualWidth;
|
||||||
{
|
double height = ActualHeight;
|
||||||
base.Width = base._MyWidth;
|
|
||||||
}
|
|
||||||
if (double.IsNaN(base.Height))
|
|
||||||
{
|
|
||||||
base.Height = base._MyHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
var rect = new Rectangle
|
// 输入连接器的背景
|
||||||
|
var connectorBackground = IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent;
|
||||||
|
var connectorRect = new Rect(4, 4, width - 8, height - 8);
|
||||||
|
drawingContext.DrawRectangle(connectorBackground, null, connectorRect);
|
||||||
|
|
||||||
|
// 定义圆形的大小和位置
|
||||||
|
double connectorSize = 10; // 连接器的大小
|
||||||
|
double circleCenterX = 8; // 圆心 X 坐标
|
||||||
|
double circleCenterY = height / 2; // 圆心 Y 坐标
|
||||||
|
var circlePoint = new Point(circleCenterX, circleCenterY);
|
||||||
|
|
||||||
|
// 绘制连接器的圆形部分
|
||||||
|
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
|
||||||
|
_myCenterPoint = new Point(circleCenterX - connectorSize / 2, circleCenterY);
|
||||||
|
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), ellipse);
|
||||||
|
|
||||||
|
// 定义三角形的间距
|
||||||
|
double triangleOffsetX = 4; // 三角形与圆形的间距
|
||||||
|
double triangleCenterX = circleCenterX + connectorSize / 2 + triangleOffsetX; // 三角形中心 X 坐标
|
||||||
|
double triangleCenterY = circleCenterY; // 三角形中心 Y 坐标
|
||||||
|
|
||||||
|
// 绘制三角形
|
||||||
|
var pathGeometry = new StreamGeometry();
|
||||||
|
using (var context = pathGeometry.Open())
|
||||||
{
|
{
|
||||||
Width = base.Width,
|
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - 4.5), true, true);
|
||||||
Height = base.Height,
|
context.LineTo(new Point(triangleCenterX + 5, triangleCenterY), true, false);
|
||||||
Fill = Brushes.Red,
|
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
|
||||||
ToolTip = "返回值"
|
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
|
||||||
};
|
}
|
||||||
Content = rect;
|
drawingContext.DrawGeometry(IsMouseOver ? Brushes.DarkCyan : Brushes.Transparent, new Pen(Brushes.Black, 1), pathGeometry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,11 @@ namespace Serein.Workbench.Node.View
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public abstract class NodeControlBase : UserControl, IDynamicFlowNode
|
public abstract class NodeControlBase : UserControl, IDynamicFlowNode
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 记录与该节点控件有关的所有连接
|
||||||
|
/// </summary>
|
||||||
|
private readonly List<ConnectionControl> connectionControls = new List<ConnectionControl>();
|
||||||
|
|
||||||
public NodeControlViewModelBase ViewModel { get; set; }
|
public NodeControlViewModelBase ViewModel { get; set; }
|
||||||
|
|
||||||
|
|
||||||
@@ -30,7 +35,51 @@ namespace Serein.Workbench.Node.View
|
|||||||
SetBinding();
|
SetBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 添加与该节点有关的连接后,记录下来
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
public void AddCnnection(ConnectionControl connection)
|
||||||
|
{
|
||||||
|
connectionControls.Add(connection);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除了连接之后,还需要从节点中的记录移除
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="connection"></param>
|
||||||
|
public void RemoveCnnection(ConnectionControl connection)
|
||||||
|
{
|
||||||
|
connectionControls.Remove(connection);
|
||||||
|
connection.Remote();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除了连接之后,还需要从节点中的记录移除
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveAllConection()
|
||||||
|
{
|
||||||
|
foreach (var connection in this.connectionControls)
|
||||||
|
{
|
||||||
|
connection.Remote(); // 主动更新连线位置
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 更新与该节点有关的数据
|
||||||
|
/// </summary>
|
||||||
|
public void UpdateLocationConnections()
|
||||||
|
{
|
||||||
|
foreach (var connection in this.connectionControls)
|
||||||
|
{
|
||||||
|
connection.RefreshLine(); // 主动更新连线位置
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 设置绑定:
|
||||||
|
/// Canvas.X and Y : 画布位置
|
||||||
|
/// </summary>
|
||||||
public void SetBinding()
|
public void SetBinding()
|
||||||
{
|
{
|
||||||
// 绑定 Canvas.Left
|
// 绑定 Canvas.Left
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
using Serein.Library.Api;
|
using Serein.Library.Api;
|
||||||
using Serein.Workbench.Extension;
|
using Serein.Workbench.Extension;
|
||||||
|
using System;
|
||||||
|
using System.Net;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
@@ -39,7 +41,7 @@ namespace Serein.Workbench.Node.View
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接类型
|
/// 连接类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConnectionType Type { get; set; }
|
public ConnectionInvokeType Type { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -68,26 +70,26 @@ namespace Serein.Workbench.Node.View
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
|
||||||
* 有1个Execute
|
|
||||||
* 有1个NextStep
|
|
||||||
* 有0~65535个入参 ushort
|
|
||||||
* 有1个ReturnData(void方法返回null)
|
|
||||||
*
|
|
||||||
* Execute: // 执行这个方法
|
|
||||||
* 只接受 NextStep 的连接
|
|
||||||
* ArgData:
|
|
||||||
* 互相之间不能连接,只能接受 Execute、ReturnData 的连接
|
|
||||||
* Execute:表示从 Execute所在节点 获取数据
|
|
||||||
* ReturnData: 表示从对应节点获取数据
|
|
||||||
* ReturnData:
|
|
||||||
* 只能发起主动连接,且只能连接到 ArgData
|
|
||||||
* NextStep
|
|
||||||
* 只能连接连接 Execute
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -102,34 +104,58 @@ namespace Serein.Workbench.Node.View
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接控件,表示控件的连接关系
|
/// 连接控件,表示控件的连接关系
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConnectionControl : Shape
|
public class ConnectionControl
|
||||||
{
|
{
|
||||||
private readonly IFlowEnvironment environment;
|
|
||||||
|
private readonly Action RemoteCallback;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 初始化连接控件
|
/// 关于调用
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="Canvas"></param>
|
/// <param name="Canvas"></param>
|
||||||
/// <param name="Type"></param>
|
/// <param name="Type"></param>
|
||||||
public ConnectionControl(IFlowEnvironment environment,
|
public ConnectionControl(Canvas Canvas,
|
||||||
Canvas Canvas,
|
ConnectionInvokeType Type,
|
||||||
ConnectionType Type,
|
JunctionControlBase Start,
|
||||||
NodeControlBase Start,
|
JunctionControlBase End,
|
||||||
NodeControlBase End)
|
Action remoteCallback)
|
||||||
{
|
{
|
||||||
this.environment = environment;
|
this.LineType = LineType.Bezier;
|
||||||
|
this.RemoteCallback = remoteCallback;
|
||||||
this.Canvas = Canvas;
|
this.Canvas = Canvas;
|
||||||
this.Type = Type;
|
this.Type = Type;
|
||||||
this.Start = Start;
|
this.Start = Start;
|
||||||
this.End = End;
|
this.End = End;
|
||||||
|
//this.Start.Background = GetLineColor(Type); // 线条颜色
|
||||||
|
//this.End.Background = GetLineColor(Type); // 线条颜色
|
||||||
|
InitElementPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关于入参
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Canvas"></param>
|
||||||
|
/// <param name="Type"></param>
|
||||||
|
public ConnectionControl(LineType LineType,
|
||||||
|
Canvas Canvas,
|
||||||
|
int argIndex,
|
||||||
|
ConnectionArgSourceType connectionArgSourceType,
|
||||||
|
JunctionControlBase Start,
|
||||||
|
JunctionControlBase End,
|
||||||
|
Action remoteCallback)
|
||||||
|
{
|
||||||
|
this.LineType = LineType;
|
||||||
|
this.RemoteCallback = remoteCallback;
|
||||||
|
this.Canvas = Canvas;
|
||||||
|
this.ArgIndex = ArgIndex;
|
||||||
|
this.ConnectionArgSourceType = connectionArgSourceType;
|
||||||
|
this.Start = Start;
|
||||||
|
this.End = End;
|
||||||
|
//this.Start.Background = GetLineColor(Type); // 线条颜色
|
||||||
|
//this.End.Background = GetLineColor(Type); // 线条颜色
|
||||||
InitElementPoint();
|
InitElementPoint();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -139,108 +165,114 @@ namespace Serein.Workbench.Node.View
|
|||||||
public Canvas Canvas { get; }
|
public Canvas Canvas { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 连接类型
|
/// 调用方法类型,连接类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConnectionType Type { get; }
|
public ConnectionInvokeType Type { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 起始控件
|
/// 获取参数类型,第几个参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NodeControlBase Start { get; set; }
|
public int ArgIndex { get; set; } = -1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 结束控件
|
/// 参数来源(决定了连接线的样式)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NodeControlBase End { get; set; }
|
public ConnectionArgSourceType ConnectionArgSourceType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 起始控制点
|
||||||
|
/// </summary>
|
||||||
|
public JunctionControlBase Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 目标控制点
|
||||||
|
/// </summary>
|
||||||
|
public JunctionControlBase End { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接线
|
||||||
|
/// </summary>
|
||||||
|
private BezierLine BezierLine;
|
||||||
|
|
||||||
|
private LineType LineType;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 配置连接曲线的右键菜单
|
/// 配置连接曲线的右键菜单
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="line"></param>
|
/// <param name="line"></param>
|
||||||
private void ConfigureLineContextMenu(ConnectionControl connection)
|
private void ConfigureLineContextMenu()
|
||||||
{
|
{
|
||||||
var contextMenu = new ContextMenu();
|
var contextMenu = new ContextMenu();
|
||||||
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
|
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => this.DeleteConnection()));
|
||||||
connection.ContextMenu = contextMenu;
|
BezierLine.ContextMenu = contextMenu;
|
||||||
connection.ContextMenu = contextMenu;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 删除该连线
|
/// 删除该连线
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="line"></param>
|
/// <param name="line"></param>
|
||||||
private void DeleteConnection(ConnectionControl connection)
|
public void DeleteConnection()
|
||||||
{
|
{
|
||||||
var connectionToRemove = connection;
|
if(this.Start is JunctionControlBase startJunctionControlBase)
|
||||||
if (connectionToRemove is null)
|
|
||||||
{
|
{
|
||||||
return;
|
//startJunctionControlBase.Background = Brushes.Transparent;
|
||||||
}
|
}
|
||||||
// 获取起始节点与终止节点,消除映射关系
|
if (this.End is JunctionControlBase endJunctionControlBase)
|
||||||
var fromNodeGuid = connectionToRemove.Start.ViewModel.NodeModel.Guid;
|
{
|
||||||
var toNodeGuid = connectionToRemove.End.ViewModel.NodeModel.Guid;
|
//endJunctionControlBase.Background = Brushes.Transparent;
|
||||||
environment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connection.Type);
|
}
|
||||||
}
|
Canvas.Children.Remove(BezierLine); // 移除线
|
||||||
|
RemoteCallback?.Invoke();
|
||||||
/// <summary>
|
|
||||||
/// 移除
|
|
||||||
/// </summary>
|
|
||||||
public void RemoveFromCanvas()
|
|
||||||
{
|
|
||||||
Canvas.Children.Remove(this); // 移除线
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 重新绘制
|
/// 重新绘制
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void AddOrRefreshLine()
|
public void RefreshLine()
|
||||||
{
|
{
|
||||||
this.InvalidateVisual();
|
(Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End);
|
||||||
|
BezierLine.UpdatePoints(startPoint, endPoint);
|
||||||
|
//BezierLine.UpdatePoints(startPoint, endPoint);
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 删除该连线
|
||||||
|
/// </summary>
|
||||||
|
public void Remote()
|
||||||
|
{
|
||||||
|
Canvas.Children.Remove(BezierLine);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 绘制
|
||||||
|
/// </summary>
|
||||||
public void InitElementPoint()
|
public void InitElementPoint()
|
||||||
{
|
{
|
||||||
leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心
|
leftCenterOfEndLocation = Start.MyCenterPoint;
|
||||||
rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心
|
rightCenterOfStartLocation = End.MyCenterPoint;
|
||||||
brush = GetLineColor(Type); // 线条颜色
|
//leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心
|
||||||
hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
|
//rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心
|
||||||
hitVisiblePen.Freeze(); // Freeze以提高性能
|
|
||||||
visualPen = new Pen(brush, 1.0); // 默认可视化Pen
|
|
||||||
visualPen.Freeze(); // Freeze以提高性能
|
|
||||||
ConfigureLineContextMenu(this); // 设置连接右键事件
|
|
||||||
linkSize = 4; // 整线条粗细
|
linkSize = 4; // 整线条粗细
|
||||||
Canvas.Children.Add(this); // 添加线
|
(Point startPoint, Point endPoint) = RefreshPoint(Canvas, Start, End);
|
||||||
Grid.SetZIndex(this, -9999999); // 置底
|
BezierLine = new BezierLine(LineType, startPoint, endPoint, GetLineColor(Type), linkSize);
|
||||||
|
Grid.SetZIndex(BezierLine, -9999999); // 置底
|
||||||
|
Canvas.Children.Add(BezierLine);
|
||||||
|
|
||||||
|
|
||||||
|
ConfigureLineContextMenu(); //配置右键菜单
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 控件重绘事件
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="drawingContext"></param>
|
|
||||||
protected override void OnRender(DrawingContext drawingContext)
|
|
||||||
{
|
|
||||||
RefreshPoint(Canvas, this.Start, this.End); // 刷新坐标
|
|
||||||
DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize, brush); // 刷新线条显示位置
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly StreamGeometry streamGeometry = new StreamGeometry();
|
double linkSize; // 根据缩放比例调整线条粗细
|
||||||
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
|
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
|
||||||
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
|
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
|
||||||
private Pen hitVisiblePen; // 初始化碰撞检测线
|
|
||||||
private Pen visualPen; // 默认可视化Pen
|
|
||||||
private Point startPoint; // 连接线的起始节点
|
|
||||||
private Point endPoint; // 连接线的终点
|
|
||||||
private Brush brush; // 线条颜色
|
|
||||||
double linkSize; // 根据缩放比例调整线条粗细
|
|
||||||
protected override Geometry DefiningGeometry => streamGeometry;
|
|
||||||
|
|
||||||
#region 工具方法
|
#region 工具方法
|
||||||
|
private (Point startPoint,Point endPoint) RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
|
||||||
public void RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
|
|
||||||
{
|
{
|
||||||
endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置
|
var startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置
|
||||||
startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置
|
var endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置
|
||||||
|
return (startPoint, endPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -249,72 +281,280 @@ namespace Serein.Workbench.Node.View
|
|||||||
/// <param name="currentConnectionType"></param>
|
/// <param name="currentConnectionType"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="Exception"></exception>
|
/// <exception cref="Exception"></exception>
|
||||||
public static SolidColorBrush GetLineColor(ConnectionType currentConnectionType)
|
public static SolidColorBrush GetLineColor(ConnectionInvokeType currentConnectionType)
|
||||||
{
|
{
|
||||||
return currentConnectionType switch
|
return currentConnectionType switch
|
||||||
{
|
{
|
||||||
ConnectionType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")),
|
ConnectionInvokeType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")),
|
||||||
ConnectionType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
|
ConnectionInvokeType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
|
||||||
ConnectionType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
|
ConnectionInvokeType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
|
||||||
ConnectionType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
|
ConnectionInvokeType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
|
||||||
_ => throw new Exception(),
|
_ => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#56CEF6")),
|
||||||
|
//_ => throw new Exception(),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
|
|
||||||
private Vector axis = new Vector(1, 0);
|
|
||||||
private Vector startToEnd;
|
|
||||||
private void DrawBezierCurve(DrawingContext drawingContext,
|
|
||||||
Point start,
|
|
||||||
Point end,
|
|
||||||
double linkSize,
|
|
||||||
Brush brush,
|
|
||||||
bool isHitTestVisible = false,
|
|
||||||
double strokeThickness = 1.0,
|
|
||||||
bool isMouseOver = false,
|
|
||||||
double dashOffset = 0.0)
|
|
||||||
{
|
|
||||||
// 控制点的计算逻辑
|
|
||||||
double power = 8 * 8; // 控制贝塞尔曲线的“拉伸”强度
|
|
||||||
|
|
||||||
// 计算轴向向量与起点到终点的向量
|
|
||||||
//var axis = new Vector(1, 0);
|
|
||||||
startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
|
|
||||||
|
|
||||||
// 计算拉伸程度k,拉伸与水平夹角正相关
|
|
||||||
var k = 1 - Math.Pow(Math.Max(0, axis.DotProduct(startToEnd)), 10.0);
|
|
||||||
|
|
||||||
// 如果起点x大于终点x,增加额外的偏移量,避免重叠
|
|
||||||
var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
|
|
||||||
|
|
||||||
// 控制点的实际计算
|
|
||||||
c0 = new Point(+(power + bias) * k + start.X, start.Y);
|
|
||||||
c1 = new Point(-(power + bias) * k + end.X, end.Y);
|
|
||||||
|
|
||||||
// 准备StreamGeometry以用于绘制曲线
|
|
||||||
streamGeometry.Clear();
|
|
||||||
using (var context = streamGeometry.Open())
|
|
||||||
{
|
|
||||||
context.BeginFigure(start, true, false); // 曲线起点
|
|
||||||
context.BezierTo(c0, c1, end, true, false); // 画贝塞尔曲线
|
|
||||||
}
|
|
||||||
|
|
||||||
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
|
|
||||||
|
|
||||||
// 绘制碰撞检测线
|
|
||||||
//if (true)
|
|
||||||
//{
|
|
||||||
// //hitVisiblePen = new Pen(Brushes.Transparent, linkSize + strokeThickness);
|
|
||||||
// //hitVisiblePen.Freeze();
|
|
||||||
// drawingContext.DrawGeometry(null, hitVisiblePen, streamGeometry);
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
|
|
||||||
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
/// <summary>
|
||||||
|
/// 连接控件,表示控件的连接关系
|
||||||
|
/// </summary>
|
||||||
|
public class ConnectionControl : Shape
|
||||||
|
{
|
||||||
|
private readonly Action RemoteCallback;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关于调用
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Canvas"></param>
|
||||||
|
/// <param name="Type"></param>
|
||||||
|
public ConnectionControl(Canvas Canvas,
|
||||||
|
ConnectionInvokeType Type,
|
||||||
|
JunctionControlBase Start,
|
||||||
|
JunctionControlBase End,
|
||||||
|
Action remoteCallback)
|
||||||
|
{
|
||||||
|
this.RemoteCallback = remoteCallback;
|
||||||
|
this.Canvas = Canvas;
|
||||||
|
this.Type = Type;
|
||||||
|
this.Start = Start;
|
||||||
|
this.End = End;
|
||||||
|
this.Start.Background = GetLineColor(Type); // 线条颜色
|
||||||
|
this.End.Background = GetLineColor(Type); // 线条颜色
|
||||||
|
InitElementPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 关于入参
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Canvas"></param>
|
||||||
|
/// <param name="Type"></param>
|
||||||
|
public ConnectionControl(Canvas Canvas,
|
||||||
|
int argIndex,
|
||||||
|
ConnectionArgSourceType connectionArgSourceType,
|
||||||
|
JunctionControlBase Start,
|
||||||
|
JunctionControlBase End,
|
||||||
|
Action remoteCallback)
|
||||||
|
{
|
||||||
|
this.RemoteCallback = remoteCallback;
|
||||||
|
this.Canvas = Canvas;
|
||||||
|
this.ArgIndex = ArgIndex;
|
||||||
|
this.ConnectionArgSourceType = connectionArgSourceType;
|
||||||
|
this.Start = Start;
|
||||||
|
this.End = End;
|
||||||
|
this.Start.Background = GetLineColor(Type); // 线条颜色
|
||||||
|
this.End.Background = GetLineColor(Type); // 线条颜色
|
||||||
|
InitElementPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 所在的画布
|
||||||
|
/// </summary>
|
||||||
|
public Canvas Canvas { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 调用方法类型,连接类型
|
||||||
|
/// </summary>
|
||||||
|
public ConnectionInvokeType Type { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 获取参数类型,第几个参数
|
||||||
|
/// </summary>
|
||||||
|
public int ArgIndex { get; set; } = -1;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参数来源(决定了连接线的样式)
|
||||||
|
/// </summary>
|
||||||
|
public ConnectionArgSourceType ConnectionArgSourceType { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 起始控制点
|
||||||
|
/// </summary>
|
||||||
|
public JunctionControlBase Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 目标控制点
|
||||||
|
/// </summary>
|
||||||
|
public JunctionControlBase End { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置连接曲线的右键菜单
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="line"></param>
|
||||||
|
private void ConfigureLineContextMenu(ConnectionControl connection)
|
||||||
|
{
|
||||||
|
var contextMenu = new ContextMenu();
|
||||||
|
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
|
||||||
|
connection.ContextMenu = contextMenu;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除该连线
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="line"></param>
|
||||||
|
private void DeleteConnection(ConnectionControl connection)
|
||||||
|
{
|
||||||
|
var connectionToRemove = connection;
|
||||||
|
if (connectionToRemove is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(this.Start is JunctionControlBase startJunctionControlBase)
|
||||||
|
{
|
||||||
|
startJunctionControlBase.Background = Brushes.Transparent;
|
||||||
|
}
|
||||||
|
if (this.End is JunctionControlBase endJunctionControlBase)
|
||||||
|
{
|
||||||
|
endJunctionControlBase.Background = Brushes.Transparent;
|
||||||
|
}
|
||||||
|
this.Canvas.g
|
||||||
|
RemoteCallback?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveFromCanvas()
|
||||||
|
{
|
||||||
|
Canvas.Children.Remove(this); // 移除线
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重新绘制
|
||||||
|
/// </summary>
|
||||||
|
public void AddOrRefreshLine()
|
||||||
|
{
|
||||||
|
this.InvalidateVisual();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitElementPoint()
|
||||||
|
{
|
||||||
|
leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心
|
||||||
|
rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心
|
||||||
|
brush = GetLineColor(Type); // 线条颜色
|
||||||
|
hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
|
||||||
|
hitVisiblePen.Freeze(); // Freeze以提高性能
|
||||||
|
visualPen = new Pen(brush, 2.0); // 默认可视化Pen
|
||||||
|
visualPen.Freeze(); // Freeze以提高性能
|
||||||
|
ConfigureLineContextMenu(this); // 设置连接右键事件
|
||||||
|
linkSize = 4; // 整线条粗细
|
||||||
|
Canvas.Children.Add(this); // 添加线
|
||||||
|
Grid.SetZIndex(this, -9999999); // 置底
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控件重绘事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawingContext"></param>
|
||||||
|
protected override void OnRender(DrawingContext drawingContext)
|
||||||
|
{
|
||||||
|
RefreshPoint(Canvas, this.Start, this.End); // 刷新坐标
|
||||||
|
DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize, brush); // 刷新线条显示位置
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly StreamGeometry streamGeometry = new StreamGeometry();
|
||||||
|
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
|
||||||
|
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
|
||||||
|
private Pen hitVisiblePen; // 初始化碰撞检测线
|
||||||
|
private Pen visualPen; // 默认可视化Pen
|
||||||
|
private Point startPoint; // 连接线的起始节点
|
||||||
|
private Point endPoint; // 连接线的终点
|
||||||
|
private Brush brush; // 线条颜色
|
||||||
|
double linkSize; // 根据缩放比例调整线条粗细
|
||||||
|
protected override Geometry DefiningGeometry => streamGeometry;
|
||||||
|
|
||||||
|
#region 工具方法
|
||||||
|
|
||||||
|
public void RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
|
||||||
|
{
|
||||||
|
endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置
|
||||||
|
startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据连接类型指定颜色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currentConnectionType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public static SolidColorBrush GetLineColor(ConnectionInvokeType currentConnectionType)
|
||||||
|
{
|
||||||
|
return currentConnectionType switch
|
||||||
|
{
|
||||||
|
ConnectionInvokeType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")),
|
||||||
|
ConnectionInvokeType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
|
||||||
|
ConnectionInvokeType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
|
||||||
|
ConnectionInvokeType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
|
||||||
|
_ => throw new Exception(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
|
||||||
|
private Vector axis = new Vector(1, 0);
|
||||||
|
private Vector startToEnd;
|
||||||
|
private void DrawBezierCurve(DrawingContext drawingContext,
|
||||||
|
Point start,
|
||||||
|
Point end,
|
||||||
|
double linkSize,
|
||||||
|
Brush brush,
|
||||||
|
bool isHitTestVisible = false,
|
||||||
|
double strokeThickness = 1.0,
|
||||||
|
bool isMouseOver = false,
|
||||||
|
double dashOffset = 0.0)
|
||||||
|
{
|
||||||
|
// 控制点的计算逻辑
|
||||||
|
double power = 8 * 8; // 控制贝塞尔曲线的“拉伸”强度
|
||||||
|
|
||||||
|
// 计算轴向向量与起点到终点的向量
|
||||||
|
//var axis = new Vector(1, 0);
|
||||||
|
startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
|
||||||
|
|
||||||
|
// 计算拉伸程度k,拉伸与水平夹角正相关
|
||||||
|
var k = 1 - Math.Pow(Math.Max(0, axis.DotProduct(startToEnd)), 10.0);
|
||||||
|
|
||||||
|
// 如果起点x大于终点x,增加额外的偏移量,避免重叠
|
||||||
|
var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
|
||||||
|
|
||||||
|
// 控制点的实际计算
|
||||||
|
c0 = new Point(+(power + bias) * k + start.X, start.Y);
|
||||||
|
c1 = new Point(-(power + bias) * k + end.X, end.Y);
|
||||||
|
|
||||||
|
// 准备StreamGeometry以用于绘制曲线
|
||||||
|
streamGeometry.Clear();
|
||||||
|
using (var context = streamGeometry.Open())
|
||||||
|
{
|
||||||
|
context.BeginFigure(start, true, false); // 曲线起点
|
||||||
|
context.BezierTo(c0, c1, end, true, false); // 画贝塞尔曲线
|
||||||
|
}
|
||||||
|
|
||||||
|
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
|
||||||
|
|
||||||
|
// 绘制碰撞检测线
|
||||||
|
//if (true)
|
||||||
|
//{
|
||||||
|
// //hitVisiblePen = new Pen(Brushes.Transparent, linkSize + strokeThickness);
|
||||||
|
// //hitVisiblePen.Freeze();
|
||||||
|
// drawingContext.DrawGeometry(null, hitVisiblePen, streamGeometry);
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user