From 8b4ec31d83908c6c81188d12a54eb49a01b9dcab Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Sat, 2 Nov 2024 22:11:38 +0800
Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E5=AF=B9NodeaAction?=
=?UTF-8?q?=E7=89=B9=E6=80=A7=E6=A0=87=E8=AE=B0=E6=96=B9=E6=B3=95=E4=B8=AD?=
=?UTF-8?q?=EF=BC=8C=E5=AF=B9params=E5=8F=AF=E5=8F=98=E5=8F=82=E6=95=B0?=
=?UTF-8?q?=E7=9A=84=E6=94=AF=E6=8C=81?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Library/Api/IFlowEnvironment.cs | 9 +
Library/FlowNode/MethodDetails.cs | 63 +++++--
Library/FlowNode/NodeModelBaseFunc.cs | 78 +++++----
Library/FlowNode/ParameterDetails.cs | 3 +
Library/FlowNode/ParameterDetailsInfo.cs | 5 +
Library/Utils/EmitHelper.cs | 13 +-
NodeFlow/Env/FlowEnvironment.cs | 23 ++-
NodeFlow/Env/FlowEnvironmentDecorator.cs | 12 +-
NodeFlow/Env/FlowFunc.cs | 2 +-
NodeFlow/Env/RemoteFlowEnvironment.cs | 13 ++
NodeFlow/Tool/NodeMethodDetailsHelper.cs | 56 +++---
WorkBench/Themes/MethodDetailsControl.xaml | 161 +++++++++---------
WorkBench/Themes/MethodDetailsControl.xaml.cs | 32 +++-
.../Node/Junction/JunctionControlBase.cs | 145 +++++++++++++++-
Workbench/Node/RelayCommand.cs | 26 +++
15 files changed, 472 insertions(+), 169 deletions(-)
create mode 100644 Workbench/Node/RelayCommand.cs
diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs
index cef123b..46e5615 100644
--- a/Library/Api/IFlowEnvironment.cs
+++ b/Library/Api/IFlowEnvironment.cs
@@ -844,6 +844,15 @@ namespace Serein.Library.Api
///
Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value);
+ ///
+ /// 改变可选参数的数目
+ ///
+ /// 对应的节点Guid
+ /// true,增加参数;false,减少参数
+ /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)
+ ///
+ Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex);
+
///
/// 获取方法描述信息
diff --git a/Library/FlowNode/MethodDetails.cs b/Library/FlowNode/MethodDetails.cs
index f20ba18..402490b 100644
--- a/Library/FlowNode/MethodDetails.cs
+++ b/Library/FlowNode/MethodDetails.cs
@@ -76,7 +76,7 @@ namespace Serein.Library
/// 0表示第一个参数是可选参数
///
[PropertyInfo]
- private int _isParamsArgIndex = -1;
+ private int _paramsArgIndex = -1;
///
/// 出参类型
@@ -89,42 +89,72 @@ namespace Serein.Library
public partial class MethodDetails
{
- #region 新增可选参数
+ #region 更改可变参数
///
- /// 新增可选参数
+ /// 是否存在可变参数(-1表示不存在)
+ ///
+ public bool HasParamsArg => _paramsArgIndex >= 0;
+
+ ///
+ /// 新增可变参数
///
///
- public void AddParamsArg(int index = 0)
+ public bool AddParamsArg(int index = 0)
{
- if (IsParamsArgIndex >= 0 // 方法是否包含可选参数
+ if (ParamsArgIndex >= 0 // 方法是否包含可变参数
&& index >= 0 // 如果包含,则判断从哪个参数赋值
- && index >= IsParamsArgIndex // 需要判断是否为可选参数的部分
+ && index >= ParamsArgIndex // 需要判断是否为可选参数的部分
&& index < ParameterDetailss.Length) // 防止下标越界
{
var newPd = ParameterDetailss[index].CloneOfModel(this.NodeModel); // 复制出属于本身节点的参数描述
newPd.Index = ParameterDetailss.Length; // 更新索引
newPd.IsParams = true;
ParameterDetailss = AddToArray(ParameterDetailss, newPd); // 新增
+ return true;
+ }
+ else
+ {
+ return false;
}
}
///
- /// 移除可选参数
+ /// 移除可变参数
///
///
- public void RemoveParamsArg(int index = 0)
+ public bool RemoveParamsArg(int index = 0)
{
- if (IsParamsArgIndex >= 0 // 方法是否包含可选参数
+ if (ParamsArgIndex >= 0 // 方法是否包含可变参数
&& index >= 0 // 如果包含,则判断从哪个参数赋值
- && index >= IsParamsArgIndex // 需要判断是否为可选参数的部分
+ && index > ParamsArgIndex // 需要判断是否为可选参数的部分,并且不能删除原始的可变参数描述
&& index < ParameterDetailss.Length) // 防止下标越界
{
- //var newPd = ParameterDetailss[index].CloneOfModel(this.NodeModel); // 复制出属于本身节点的参数描述
- //newPd.Index = ParameterDetailss.Length; // 更新索引
ParameterDetailss[index] = null; // 释放对象引用
- ParameterDetailss = RemoteToArray(ParameterDetailss, index); // 新增
+ var tmp = RemoteToArray(ParameterDetailss, index); // 新增;
+ UpdateParamIndex(ref tmp);
+ ParameterDetailss = tmp; // 新增
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+
+ }
+
+ ///
+ /// 更新参数的索引
+ ///
+ ///
+ private void UpdateParamIndex(ref ParameterDetails[] parameterDetails)
+ {
+ for (int i = 0; i < parameterDetails.Length; i++)
+ {
+ var pd = parameterDetails[i];
+ pd.Index = i;
}
}
+
public static T[] AddToArray(T[] original, T newObject)
{
// 创建一个新数组,比原数组大1
@@ -197,7 +227,7 @@ namespace Serein.Library
MethodDynamicType = nodeType;
ReturnType = Type.GetType(Info.ReturnTypeFullName);
ParameterDetailss = Info.ParameterDetailsInfos.Select(pinfo => new ParameterDetails(pinfo)).ToArray();
- IsParamsArgIndex = Info.IsParamsArgIndex;
+ ParamsArgIndex = Info.IsParamsArgIndex;
}
///
@@ -213,7 +243,7 @@ namespace Serein.Library
NodeType = this.MethodDynamicType.ToString(),
ParameterDetailsInfos = this.ParameterDetailss.Select(p => p.ToInfo()).ToArray(),
ReturnTypeFullName = this.ReturnType.FullName,
- IsParamsArgIndex = this.IsParamsArgIndex,
+ IsParamsArgIndex = this.ParamsArgIndex,
};
}
@@ -234,7 +264,7 @@ namespace Serein.Library
MethodName = this.MethodName,
MethodLockName = this.MethodLockName,
IsProtectionParameter = this.IsProtectionParameter,
- IsParamsArgIndex= this.IsParamsArgIndex,
+ ParamsArgIndex = this.ParamsArgIndex,
};
md.ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(); // 拷贝属于节点方法的新入参描述
return md;
@@ -251,6 +281,7 @@ namespace Serein.Library
for (int i = 0; i < ParameterDetailss.Length; i++)
{
ParameterDetails arg = this.ParameterDetailss[i];
+ sb.AppendLine(arg.ToString());
}
sb.AppendLine($"");
sb.AppendLine($"返回值信息:");
diff --git a/Library/FlowNode/NodeModelBaseFunc.cs b/Library/FlowNode/NodeModelBaseFunc.cs
index 0842402..db95286 100644
--- a/Library/FlowNode/NodeModelBaseFunc.cs
+++ b/Library/FlowNode/NodeModelBaseFunc.cs
@@ -323,28 +323,35 @@ namespace Serein.Library
return null;// md.ActingInstance
}
- object[] parameters = new object[md.ParameterDetailss.Length];
+ object[] parameters;
- //var previousFlowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据
-
-
- object[] paramsArgs = null; // 初始化可选参数
- int paramsArgIndex = 0; // 可选参数下标
- if (md.IsParamsArgIndex >= 0) // 是否存在入参参数
+ Array paramsArgs = null; // 初始化可选参数
+ int paramsArgIndex = 0; // 可选参数下标,与 object[] paramsArgs 一起使用
+ Type paramsArgType = null; // 可变参数的参数类型
+ if (md.ParamsArgIndex >= 0)
{
- // 可选参数数组长度 = 方法参数个数 - ( 可选入参下标 + 1 )
- int paramsLength = md.ParameterDetailss.Length - md.IsParamsArgIndex;
- paramsArgs = new object[paramsLength]; // 重新实例化可选参数
- parameters[md.ParameterDetailss.Length-1] = paramsArgs; // 如果存在可选参数,入参参数最后一项则为可选参数
+ // 存在可变入参参数
+ paramsArgType = md.ParameterDetailss[md.ParamsArgIndex].DataType.GetElementType(); // 获取可变参数的参数类型
+ // 可变参数数组长度 = 方法参数个数 - ( 可选入参下标 + 1 )
+ int paramsLength = md.ParameterDetailss.Length - md.ParamsArgIndex;
+ //paramsArgs = paramsArgType.MakeArrayType(paramsLength);
+ paramsArgs = Array.CreateInstance(paramsArgType, paramsLength);// 可变参数
+ parameters = new object[md.ParamsArgIndex+1]; // 调用方法的入参数组
+ parameters[md.ParamsArgIndex] = paramsArgs; // 如果存在可选参数,入参参数最后一项则为可变参数
+ }
+ else
+ {
+ // 不存在可选参数
+ parameters = new object[md.ParameterDetailss.Length]; // 调用方法的入参数组
}
bool hasParams = false;
- for (int i = 0; i < parameters.Length; i++)
+ for (int i = 0; i < md.ParameterDetailss.Length; i++)
{
var pd = md.ParameterDetailss[i]; // 方法入参描述
// 入参参数下标循环到可选参数时,开始写入到可选参数数组
- if(i >= md.IsParamsArgIndex)
+ if(paramsArgs != null && i >= md.ParamsArgIndex)
{
// 控制参数赋值方向:
// true => paramsArgs
@@ -352,13 +359,17 @@ namespace Serein.Library
hasParams = true;
}
+ // 可选参数为 Array 类型,所以需要获取子项类型
+ // 如果 hasParams 为 true ,说明一定存在可选参数,所以 paramsArgType 一定不为 null
+ Type argDataType = hasParams ? paramsArgType : pd.DataType;
+
#region 获取基础的上下文数据
- if (pd.DataType == typeof(IFlowEnvironment)) // 获取流程上下文
+ if (argDataType == typeof(IFlowEnvironment)) // 获取流程上下文
{
parameters[i] = nodeModel.Env;
continue;
}
- if (pd.DataType == typeof(IDynamicContext)) // 获取流程上下文
+ if (argDataType == typeof(IDynamicContext)) // 获取流程上下文
{
parameters[i] = context;
continue;
@@ -367,7 +378,7 @@ namespace Serein.Library
#region 确定[预入参]数据
object inputParameter; // 存放解析的临时参数
- if (pd.IsExplicitData) // 判断是否使用显示的输入参数
+ if (pd.IsExplicitData && !pd.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase)) // 判断是否使用显示的输入参数
{
// 使用输入的固定值
inputParameter = pd.DataValue;
@@ -415,10 +426,10 @@ namespace Serein.Library
}
#region 对于非值类型的null检查
- if (!pd.DataType.IsValueType && inputParameter is null)
+ if (!argDataType.IsValueType && inputParameter is null)
{
parameters[i] = null;
- throw new Exception($"[arg{pd.Index}][{pd.Name}][{pd.DataType}]参数不能为null");
+ throw new Exception($"[arg{pd.Index}][{pd.Name}][{argDataType}]参数不能为null");
// continue;
}
#endregion
@@ -452,8 +463,9 @@ namespace Serein.Library
{
if (hasParams)
{
+ paramsArgs.SetValue(value, paramsArgIndex++);
// 处理可选参数
- paramsArgs[paramsArgIndex++] = value;
+ //paramsArgs[paramsArgIndex++] = value;
}
else
{
@@ -466,7 +478,7 @@ namespace Serein.Library
#region 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型,如果获取成功(不为null)会跳过循环
// 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型
- if (pd.ExplicitType.IsEnum && pd.DataType != pd.ExplicitType)
+ if (pd.ExplicitType.IsEnum && argDataType != pd.ExplicitType)
{
var resultEnum = Enum.Parse(pd.ExplicitType, pd.DataValue);
// 获取绑定的类型
@@ -483,7 +495,8 @@ namespace Serein.Library
if (hasParams)
{
// 处理可选参数
- paramsArgs[paramsArgIndex++] = value;
+ paramsArgs.SetValue(value, paramsArgIndex++);
+ //paramsArgs[paramsArgIndex++] = value;
}
else
{
@@ -498,30 +511,30 @@ namespace Serein.Library
#region 对入参数据尝试进行转换
object tmpVaue = null; // 临时存放数据,最后才判断是否放置可选参数数组
- if (inputParameter.GetType() == pd.DataType)
+ if (inputParameter.GetType() == argDataType)
{
tmpVaue = inputParameter; // 类型一致无需转换,直接装入入参数组
}
- else if (pd.DataType.IsValueType)
+ else if (argDataType.IsValueType)
{
// 值类型
var valueStr = inputParameter?.ToString();
- tmpVaue = valueStr.ToValueData(pd.DataType); // 类型不一致,尝试进行转换,如果转换失败返回类型对应的默认值
+ tmpVaue = valueStr.ToValueData(argDataType); // 类型不一致,尝试进行转换,如果转换失败返回类型对应的默认值
}
else
{
// 引用类型
- if (pd.DataType == typeof(string)) // 转为字符串
+ if (argDataType == typeof(string)) // 转为字符串
{
var valueStr = inputParameter?.ToString();
tmpVaue = valueStr;
}
- else if(pd.DataType.IsSubclassOf(inputParameter.GetType())) // 入参类型 是 预入参数据类型 的 子类/实现类
+ else if(argDataType.IsSubclassOf(inputParameter.GetType())) // 入参类型 是 预入参数据类型 的 子类/实现类
{
// 方法入参中,父类不能隐式转为子类,这里需要进行强制转换
- tmpVaue = ObjectConvertHelper.ConvertParentToChild(inputParameter, pd.DataType);
+ tmpVaue = ObjectConvertHelper.ConvertParentToChild(inputParameter, argDataType);
}
- else if(pd.DataType.IsAssignableFrom(inputParameter.GetType())) // 入参类型 是 预入参数据类型 的 父类/接口
+ else if(argDataType.IsAssignableFrom(inputParameter.GetType())) // 入参类型 是 预入参数据类型 的 父类/接口
{
tmpVaue = inputParameter;
}
@@ -530,12 +543,12 @@ namespace Serein.Library
//{
// var enumerableMethods = typeof(Enumerable).GetMethods(); // 获取所有的 Enumerable 扩展方法
// MethodInfo conversionMethod;
- // if (pd.DataType.IsArray) // 转为数组
+ // if (argDataType.IsArray) // 转为数组
// {
// parameters[i] = inputParameter;
// conversionMethod = enumerableMethods.FirstOrDefault(m => m.Name == "ToArray" && m.IsGenericMethodDefinition);
// }
- // else if (pd.DataType.GetGenericTypeDefinition() == typeof(List<>)) // 转为集合
+ // else if (argDataType.GetGenericTypeDefinition() == typeof(List<>)) // 转为集合
// {
// conversionMethod = enumerableMethods.FirstOrDefault(m => m.Name == "ToList" && m.IsGenericMethodDefinition);
// }
@@ -543,7 +556,7 @@ namespace Serein.Library
// {
// throw new InvalidOperationException("输入对象不是集合或目标类型不支持(目前仅支持Array、List的自动转换)");
// }
- // var genericMethod = conversionMethod.MakeGenericMethod(pd.DataType);
+ // var genericMethod = conversionMethod.MakeGenericMethod(argDataType);
// var result = genericMethod.Invoke(null, new object[] { collection });
// parameters[i] = result;
//}
@@ -556,7 +569,8 @@ namespace Serein.Library
if (hasParams)
{
// 处理可选参数
- paramsArgs[paramsArgIndex++] = tmpVaue;
+ paramsArgs.SetValue(tmpVaue, paramsArgIndex++);
+ //paramsArgs[paramsArgIndex++] = tmpVaue;
}
else
{
diff --git a/Library/FlowNode/ParameterDetails.cs b/Library/FlowNode/ParameterDetails.cs
index 83a6ad6..d92b483 100644
--- a/Library/FlowNode/ParameterDetails.cs
+++ b/Library/FlowNode/ParameterDetails.cs
@@ -133,6 +133,7 @@ namespace Serein.Library
ExplicitType = Type.GetType(info.ExplicitTypeFullName);
ExplicitTypeName = info.ExplicitTypeName;
Items = info.Items;
+ IsParams = info.IsParams;
}
///
@@ -144,6 +145,7 @@ namespace Serein.Library
return new ParameterDetailsInfo
{
Index = this.Index,
+ IsParams = this.IsParams,
DataTypeFullName = this.DataType.FullName,
Name = this.Name,
ExplicitTypeFullName = this.ExplicitType.FullName,
@@ -170,6 +172,7 @@ namespace Serein.Library
Name = this.Name,
DataValue = string.IsNullOrEmpty(DataValue) ? string.Empty : DataValue,
Items = this.Items?.Select(it => it).ToArray(),
+ IsParams = this.IsParams,
};
return pd;
diff --git a/Library/FlowNode/ParameterDetailsInfo.cs b/Library/FlowNode/ParameterDetailsInfo.cs
index c1feb54..4fc6905 100644
--- a/Library/FlowNode/ParameterDetailsInfo.cs
+++ b/Library/FlowNode/ParameterDetailsInfo.cs
@@ -17,6 +17,11 @@ namespace Serein.Library
///
public int Index { get; set; }
+ ///
+ /// 是否为可变参数
+ ///
+ public bool IsParams { get; set; }
+
///
/// 方法需要的类型
///
diff --git a/Library/Utils/EmitHelper.cs b/Library/Utils/EmitHelper.cs
index 1ab5e89..0cf481a 100644
--- a/Library/Utils/EmitHelper.cs
+++ b/Library/Utils/EmitHelper.cs
@@ -29,6 +29,7 @@ namespace Serein.Library.Utils
///
HasResultTask,
}
+
public static bool IsGenericTask(Type returnType, out Type taskResult)
{
// 判断是否为 Task 类型或泛型 Task
@@ -51,6 +52,8 @@ namespace Serein.Library.Utils
}
}
+
+
///
/// 根据方法信息创建动态调用的委托,返回方法类型,以及传出一个委托
///
@@ -82,6 +85,8 @@ namespace Serein.Library.Utils
}
}
+
+
dynamicMethod = new DynamicMethod(
name: methodInfo.Name + "_DynamicEmitMethod",
returnType: returnType,
@@ -89,8 +94,6 @@ namespace Serein.Library.Utils
restrictedSkipVisibility: true // 跳过私有方法访问限制
);
-
-
var il = dynamicMethod.GetILGenerator();
// 加载实例 (this)
@@ -110,10 +113,16 @@ namespace Serein.Library.Utils
{
il.Emit(OpCodes.Unbox_Any, paramType);
}
+ //else if (paramType.IsGenericParameter) // 如果是泛型参数,直接转换
+ //{
+ // il.Emit(OpCodes.Castclass, paramType);
+ //}
else // 如果是引用类型,直接转换
{
il.Emit(OpCodes.Castclass, paramType);
}
+
+
}
// 调用方法
diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs
index 064e39d..ab303cc 100644
--- a/NodeFlow/Env/FlowEnvironment.cs
+++ b/NodeFlow/Env/FlowEnvironment.cs
@@ -1316,7 +1316,28 @@ namespace Serein.NodeFlow.Env
}
-
+ ///
+ /// 改变可选参数的数目
+ ///
+ /// 对应的节点Guid
+ /// true,增加参数;false,减少参数
+ /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)
+ ///
+ public async Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
+ {
+ var nodeModel = GuidToModel(nodeGuid);
+ if (nodeModel is null) return false;
+ bool isPass;
+ if (isAdd)
+ {
+ isPass = nodeModel.MethodDetails.AddParamsArg(paramIndex);
+ }
+ else
+ {
+ isPass = nodeModel.MethodDetails.RemoveParamsArg(paramIndex);
+ }
+ return isPass;
+ }
///
diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs
index 142ca55..1b802ad 100644
--- a/NodeFlow/Env/FlowEnvironmentDecorator.cs
+++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs
@@ -425,7 +425,17 @@ namespace Serein.NodeFlow.Env
}
-
+ ///
+ /// 改变可选参数的数目
+ ///
+ /// 对应的节点Guid
+ /// true,增加参数;false,减少参数
+ /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)
+ ///
+ public async Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
+ {
+ return await currentFlowEnvironment.ChangeParameter(nodeGuid, isAdd, paramIndex);
+ }
#region 流程依赖类库的接口
diff --git a/NodeFlow/Env/FlowFunc.cs b/NodeFlow/Env/FlowFunc.cs
index cb4c2c3..46f7421 100644
--- a/NodeFlow/Env/FlowFunc.cs
+++ b/NodeFlow/Env/FlowFunc.cs
@@ -52,7 +52,7 @@ namespace Serein.NodeFlow.Env
{
methodDetails = new MethodDetails();
}
- var md = methodDetails.CloneOfNode(nodeModel.Env, nodeModel);
+ var md = methodDetails.CloneOfNode(nodeModel);
nodeModel.DisplayName = md.MethodAnotherName;
nodeModel.MethodDetails = md;
nodeModel.OnCreating();
diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs
index 2312ae8..87a94f0 100644
--- a/NodeFlow/Env/RemoteFlowEnvironment.cs
+++ b/NodeFlow/Env/RemoteFlowEnvironment.cs
@@ -819,6 +819,7 @@ namespace Serein.NodeFlow.Env
UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)));
}
+
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
{
if(IsLoadingProject || IsLoadingNode)
@@ -836,6 +837,18 @@ namespace Serein.NodeFlow.Env
}
+ ///
+ /// 改变可选参数的数目
+ ///
+ /// 对应的节点Guid
+ /// true,增加参数;false,减少参数
+ /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)
+ ///
+ public async Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
+ {
+ Console.WriteLine("远程环境尚未实现的接口:ChangeParameter");
+ return false;
+ }
#region 流程依赖类库的接口
diff --git a/NodeFlow/Tool/NodeMethodDetailsHelper.cs b/NodeFlow/Tool/NodeMethodDetailsHelper.cs
index 1b5a371..18c61e4 100644
--- a/NodeFlow/Tool/NodeMethodDetailsHelper.cs
+++ b/NodeFlow/Tool/NodeMethodDetailsHelper.cs
@@ -4,6 +4,7 @@ using Serein.Library;
using System.Collections.Concurrent;
using System.Reflection;
using Serein.Library.FlowNode;
+using System.Diagnostics;
namespace Serein.NodeFlow.Tool;
@@ -31,8 +32,12 @@ public static class NodeMethodDetailsHelper
}
//var dllTypeName = $"{assemblyName}.{type.Name}";
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
-
+ Console.WriteLine("loading method : " +dllTypeMethodName);
+ Debug.WriteLine("loading method : " +dllTypeMethodName);
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
+
+
+
//// 通过表达式树生成委托
//var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
// method, // 方法信息
@@ -92,9 +97,13 @@ public static class NodeMethodDetailsHelper
var asyncPrefix = "[异步]"; // IsGenericTask(returnType) ? "[async]" : ;
var methodMethodAnotherName = isTask ? asyncPrefix + attribute.AnotherName : attribute.AnotherName;
+ bool hasParamsArg = false;
+ if (explicitDataOfParameters.Length > 0)
+ {
+ hasParamsArg = explicitDataOfParameters[^1].IsParams; // 取最后一个参数描述,判断是否为params 入参
+ }
-
- var md = new MethodDetails() // 从DLL生成方法描述
+ var md = new MethodDetails() // 从DLL生成方法描述(元数据)
{
ActingInstanceType = type,
// ActingInstance = instance,
@@ -104,6 +113,8 @@ public static class NodeMethodDetailsHelper
MethodAnotherName = methodMethodAnotherName,
ParameterDetailss = explicitDataOfParameters,
ReturnType = returnType,
+ // 如果存在可变参数,取最后一个元素的下标,否则为-1;
+ ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length-1 : -1,
};
var dd = new DelegateDetails(emitMethodType, methodDelegate) ;
return (md, dd);
@@ -144,16 +155,19 @@ public static class NodeMethodDetailsHelper
private static ParameterDetails[] GetExplicitDataOfParameters(ParameterInfo[] parameters)
{
- return parameters.Select((it, index) =>
+ var tempParams = parameters.Select((it, index) =>
{
Type paremType;
-
- if (it.GetCustomAttribute() is EnumTypeConvertorAttribute attribute1 && attribute1 is not null)
+
+ #region 存在“枚举=>类型”转换器
+ if (it.GetCustomAttribute() is EnumTypeConvertorAttribute attribute1 && attribute1 is not null)
{
// 存在类型选择器
paremType = attribute1.EnumType;
- return GetExplicitDataOfParameter(it, index, paremType, true);
+ return GetExplicitDataOfParameter(it, index, paremType, true); // “枚举=>类型”转换器 获取参数
}
+ #endregion
+ #region 存在自定义的转换器
else if (it.GetCustomAttribute() is BindConvertorAttribute attribute2 && attribute2 is not null)
{
paremType = attribute2.EnumType;
@@ -180,29 +194,22 @@ public static class NodeMethodDetailsHelper
return methodInfo?.Invoke(obj, [enumValue]);
}
// 确保实例实现了所需接口
- ParameterDetails ed = GetExplicitDataOfParameter(it, index, paremType, true, func);
+ ParameterDetails ed = GetExplicitDataOfParameter(it, index, paremType, true, func); // 自定义的转换器 获取参数
return ed;
}
+ #endregion
+ #region 常规方法的获取参数
else
{
- return GetExplicitDataOfParameter(it, index, it.ParameterType, it.HasDefaultValue);
- }
- //string explicitTypeName = GetExplicitTypeName(paremType);
- //var items = GetExplicitItems(paremType, explicitTypeName);
- //if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
- //return new ExplicitData
- //{
- // IsExplicitData = attribute is null ? it.HasDefaultValue: true,
- // Index = index,
- // ExplicitTypeName = explicitTypeName,
- // ExplicitType = paremType,
- // DataType = it.ParameterType,
- // ParameterName = it.Name,
- // DataValue = it.HasDefaultValue ? it?.DefaultValue?.ToString() : "",
- // Items = items.ToArray(),
- //};
+ var tmp = GetExplicitDataOfParameter(it, index, it.ParameterType, it.HasDefaultValue); // 常规方法的获取参数
+ return tmp;
+ }
+ #endregion
}).ToArray();
+
+
+ return tempParams;
}
private static ParameterDetails GetExplicitDataOfParameter(ParameterInfo parameterInfo,
@@ -213,7 +220,6 @@ public static class NodeMethodDetailsHelper
{
bool hasParams = parameterInfo.IsDefined(typeof(ParamArrayAttribute)); // 判断是否为可变参数
-
string explicitTypeName = GetExplicitTypeName(paremType);
var items = GetExplicitItems(paremType, explicitTypeName);
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
diff --git a/WorkBench/Themes/MethodDetailsControl.xaml b/WorkBench/Themes/MethodDetailsControl.xaml
index 8e53246..de53695 100644
--- a/WorkBench/Themes/MethodDetailsControl.xaml
+++ b/WorkBench/Themes/MethodDetailsControl.xaml
@@ -2,130 +2,125 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Serein.Workbench.Themes"
xmlns:view="clr-namespace:Serein.Workbench.Node.View"
- xmlns:sys="clr-namespace:System;assembly=mscorlib" >
+ xmlns:sys="clr-namespace:System;assembly=mscorlib"
+ xmlns:converters="clr-namespace:Serein.Workbench.Tool.Converters">
-
+
+
-
-
+
+
+
+
-
+
diff --git a/WorkBench/Themes/MethodDetailsControl.xaml.cs b/WorkBench/Themes/MethodDetailsControl.xaml.cs
index 270614e..32caab5 100644
--- a/WorkBench/Themes/MethodDetailsControl.xaml.cs
+++ b/WorkBench/Themes/MethodDetailsControl.xaml.cs
@@ -1,9 +1,11 @@
using Serein.Library;
+using Serein.Workbench.Node;
using System.Collections;
using System.Globalization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
+using System.Windows.Input;
namespace Serein.Workbench.Themes
{
@@ -15,10 +17,12 @@ namespace Serein.Workbench.Themes
{
if (isEnabled)
{
+ // 返回文本框
if (valueType == typeof(string) || valueType == typeof(int) || valueType == typeof(double))
{
return "TextBoxTemplate";
}
+ // 返回可选列表框
else if (typeof(IEnumerable).IsAssignableFrom(valueType))
{
return "ComboBoxTemplate";
@@ -44,23 +48,43 @@ namespace Serein.Workbench.Themes
static MethodDetailsControl()
{
DefaultStyleKeyProperty.OverrideMetadata(typeof(MethodDetailsControl), new FrameworkPropertyMetadata(typeof(MethodDetailsControl)));
+
}
-
+ #region 绑定的方法信息
public MethodDetails MethodDetails
{
get { return (MethodDetails)GetValue(MethodDetailsProperty); }
set { SetValue(MethodDetailsProperty, value); }
}
- public static readonly DependencyProperty MethodDetailsProperty = DependencyProperty.Register("MethodDetails", typeof(MethodDetails),
+ public static readonly DependencyProperty MethodDetailsProperty = DependencyProperty.Register(nameof(MethodDetails), typeof(MethodDetails),
typeof(MethodDetailsControl), new PropertyMetadata(null, new PropertyChangedCallback(OnPropertyChange)));
+ #endregion
+
+
static void OnPropertyChange(DependencyObject sender, DependencyPropertyChangedEventArgs args)
{
-
- var MethodDetails = (MethodDetails)args.NewValue;
+ //var MethodDetails = (MethodDetails)args.NewValue;
//MethodDetails.ExplicitDatas[0].
}
+
+
+ public ICommand CommandAddParams { get; }
+
+ public MethodDetailsControl()
+ {
+ CommandAddParams = new RelayCommand(ExecuteAddParams);
+ }
+
+ private void ExecuteAddParams(object parameter)
+ {
+ // 方法逻辑
+ this.MethodDetails.AddParamsArg();
+ }
+
+
+
}
}
diff --git a/Workbench/Node/Junction/JunctionControlBase.cs b/Workbench/Node/Junction/JunctionControlBase.cs
index af31965..db4159a 100644
--- a/Workbench/Node/Junction/JunctionControlBase.cs
+++ b/Workbench/Node/Junction/JunctionControlBase.cs
@@ -9,11 +9,141 @@ using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
+using System.Windows.Media.Media3D;
+using System.Windows.Documents;
+using System.Threading;
namespace Serein.Workbench.Node.View
{
+ public class ParamsArgControl: Shape
+ {
+
+
+ public ParamsArgControl()
+ {
+ this.MouseDown += ParamsArg_OnMouseDown; // 增加或删除
+ this.MouseMove += ParamsArgControl_MouseMove;
+ this.MouseLeave += ParamsArgControl_MouseLeave;
+ AddOrRemoveParamsAction = Add;
+
+
+ }
+
+
+
+ protected readonly StreamGeometry StreamGeometry = new StreamGeometry();
+ protected override Geometry DefiningGeometry => StreamGeometry;
+
+
+ #region 控件属性,所在的节点
+ public static readonly DependencyProperty NodeProperty =
+ DependencyProperty.Register(nameof(MyNode), typeof(NodeModelBase), typeof(ParamsArgControl), new PropertyMetadata(default(NodeModelBase)));
+ //public NodeModelBase NodeModel;
+
+ ///
+ /// 所在的节点
+ ///
+ public NodeModelBase MyNode
+ {
+ get { return (NodeModelBase)GetValue(NodeProperty); }
+ set { SetValue(NodeProperty, value); }
+ }
+ #endregion
+
+ #region 控件属性,连接器类型
+ public static readonly DependencyProperty ArgIndexProperty =
+ DependencyProperty.Register(nameof(ArgIndex), typeof(int), typeof(ParamsArgControl), new PropertyMetadata(default(int)));
+
+ ///
+ /// 参数的索引
+ ///
+ public int ArgIndex
+ {
+ get { return (int)GetValue(ArgIndexProperty); }
+ set { SetValue(ArgIndexProperty, value.ToString()); }
+ }
+ #endregion
+
+
+ ///
+ /// 控件重绘事件
+ ///
+ ///
+ protected override void OnRender(DrawingContext drawingContext)
+ {
+ Brush brush = isMouseOver ? Brushes.Red : Brushes.Green;
+ double height = ActualHeight;
+ // 定义圆形的大小和位置
+ 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(brush, new Pen(Brushes.Black, 1), ellipse);
+ }
+
+
+ private bool isMouseOver; // 鼠标悬停状态
+
+ private Action AddOrRemoveParamsAction; // 增加或删除参数
+
+ public void ParamsArg_OnMouseDown(object sender, MouseButtonEventArgs e)
+ {
+ AddOrRemoveParamsAction?.Invoke();
+ }
+
+ private void ParamsArgControl_MouseMove(object sender, MouseEventArgs e)
+ {
+ isMouseOver = true;
+ if (cancellationTokenSource.IsCancellationRequested) {
+ cancellationTokenSource = new CancellationTokenSource();
+ Task.Run(async () =>
+ {
+ await Task.Delay(500);
+
+ }, cancellationTokenSource.Token).ContinueWith((t) =>
+ {
+ // 如果焦点仍在控件上时,则改变点击事件
+ if (isMouseOver)
+ {
+ AddOrRemoveParamsAction = Remove;
+ this.Dispatcher.Invoke(InvalidateVisual);// 触发一次重绘
+
+ }
+ });
+ }
+
+ }
+ private CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
+
+
+ private void ParamsArgControl_MouseLeave(object sender, MouseEventArgs e)
+ {
+ isMouseOver = false;
+ AddOrRemoveParamsAction = Add; // 鼠标焦点离开时恢复点击事件
+ cancellationTokenSource?.Cancel();
+ this.Dispatcher.Invoke(InvalidateVisual);// 触发一次重绘
+
+ }
+
+
+ private void Add()
+ {
+ this.MyNode.Env.ChangeParameter(MyNode.Guid, true, ArgIndex);
+ }
+ private void Remove()
+ {
+ this.MyNode.Env.ChangeParameter(MyNode.Guid, false, ArgIndex);
+ }
+
+ }
+
+
public abstract class JunctionControlBase : Shape
{
@@ -141,7 +271,11 @@ namespace Serein.Workbench.Node.View
private object lockObj = new object();
- // 控件获得鼠标焦点事件
+ ///
+ /// 控件获得鼠标焦点事件
+ ///
+ ///
+ ///
private void JunctionControlBase_MouseMove(object sender, MouseEventArgs e)
{
//if (!GlobalJunctionData.MyGlobalConnectingData.IsCreateing) return;
@@ -150,10 +284,13 @@ namespace Serein.Workbench.Node.View
IsMouseOver = true;
//this.InvalidateVisual();
-
-
}
- // 控件失去鼠标焦点事件
+
+ ///
+ /// 控件失去鼠标焦点事件
+ ///
+ ///
+ ///
private void JunctionControlBase_MouseLeave(object sender, MouseEventArgs e)
{
IsMouseOver = false;
diff --git a/Workbench/Node/RelayCommand.cs b/Workbench/Node/RelayCommand.cs
new file mode 100644
index 0000000..a19b531
--- /dev/null
+++ b/Workbench/Node/RelayCommand.cs
@@ -0,0 +1,26 @@
+using System.Windows.Input;
+
+namespace Serein.Workbench.Node
+{
+
+ public class RelayCommand : ICommand
+ {
+ private readonly Action