mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-29 11:03:23 +08:00
新增了对NodeaAction特性标记方法中,对params可变参数的支持
This commit is contained in:
@@ -844,6 +844,15 @@ namespace Serein.Library.Api
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value);
|
Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 改变可选参数的数目
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeGuid">对应的节点Guid</param>
|
||||||
|
/// <param name="isAdd">true,增加参数;false,减少参数</param>
|
||||||
|
/// <param name="paramIndex">以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex);
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 获取方法描述信息
|
/// 获取方法描述信息
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ namespace Serein.Library
|
|||||||
/// <para>0表示第一个参数是可选参数</para>
|
/// <para>0表示第一个参数是可选参数</para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo]
|
[PropertyInfo]
|
||||||
private int _isParamsArgIndex = -1;
|
private int _paramsArgIndex = -1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 出参类型
|
/// 出参类型
|
||||||
@@ -89,42 +89,72 @@ namespace Serein.Library
|
|||||||
public partial class MethodDetails
|
public partial class MethodDetails
|
||||||
{
|
{
|
||||||
|
|
||||||
#region 新增可选参数
|
#region 更改可变参数
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 新增可选参数
|
/// 是否存在可变参数(-1表示不存在)
|
||||||
|
/// </summary>
|
||||||
|
public bool HasParamsArg => _paramsArgIndex >= 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 新增可变参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index"></param>
|
/// <param name="index"></param>
|
||||||
public void AddParamsArg(int index = 0)
|
public bool AddParamsArg(int index = 0)
|
||||||
{
|
{
|
||||||
if (IsParamsArgIndex >= 0 // 方法是否包含可选参数
|
if (ParamsArgIndex >= 0 // 方法是否包含可变参数
|
||||||
&& index >= 0 // 如果包含,则判断从哪个参数赋值
|
&& index >= 0 // 如果包含,则判断从哪个参数赋值
|
||||||
&& index >= IsParamsArgIndex // 需要判断是否为可选参数的部分
|
&& index >= ParamsArgIndex // 需要判断是否为可选参数的部分
|
||||||
&& index < ParameterDetailss.Length) // 防止下标越界
|
&& index < ParameterDetailss.Length) // 防止下标越界
|
||||||
{
|
{
|
||||||
var newPd = ParameterDetailss[index].CloneOfModel(this.NodeModel); // 复制出属于本身节点的参数描述
|
var newPd = ParameterDetailss[index].CloneOfModel(this.NodeModel); // 复制出属于本身节点的参数描述
|
||||||
newPd.Index = ParameterDetailss.Length; // 更新索引
|
newPd.Index = ParameterDetailss.Length; // 更新索引
|
||||||
newPd.IsParams = true;
|
newPd.IsParams = true;
|
||||||
ParameterDetailss = AddToArray(ParameterDetailss, newPd); // 新增
|
ParameterDetailss = AddToArray(ParameterDetailss, newPd); // 新增
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 移除可选参数
|
/// 移除可变参数
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="index"></param>
|
/// <param name="index"></param>
|
||||||
public void RemoveParamsArg(int index = 0)
|
public bool RemoveParamsArg(int index = 0)
|
||||||
{
|
{
|
||||||
if (IsParamsArgIndex >= 0 // 方法是否包含可选参数
|
if (ParamsArgIndex >= 0 // 方法是否包含可变参数
|
||||||
&& index >= 0 // 如果包含,则判断从哪个参数赋值
|
&& index >= 0 // 如果包含,则判断从哪个参数赋值
|
||||||
&& index >= IsParamsArgIndex // 需要判断是否为可选参数的部分
|
&& index > ParamsArgIndex // 需要判断是否为可选参数的部分,并且不能删除原始的可变参数描述
|
||||||
&& index < ParameterDetailss.Length) // 防止下标越界
|
&& index < ParameterDetailss.Length) // 防止下标越界
|
||||||
{
|
{
|
||||||
//var newPd = ParameterDetailss[index].CloneOfModel(this.NodeModel); // 复制出属于本身节点的参数描述
|
|
||||||
//newPd.Index = ParameterDetailss.Length; // 更新索引
|
|
||||||
ParameterDetailss[index] = null; // 释放对象引用
|
ParameterDetailss[index] = null; // 释放对象引用
|
||||||
ParameterDetailss = RemoteToArray(ParameterDetailss, index); // 新增
|
var tmp = RemoteToArray(ParameterDetailss, index); // 新增;
|
||||||
|
UpdateParamIndex(ref tmp);
|
||||||
|
ParameterDetailss = tmp; // 新增
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 更新参数的索引
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parameterDetails"></param>
|
||||||
|
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>(T[] original, T newObject)
|
public static T[] AddToArray<T>(T[] original, T newObject)
|
||||||
{
|
{
|
||||||
// 创建一个新数组,比原数组大1
|
// 创建一个新数组,比原数组大1
|
||||||
@@ -197,7 +227,7 @@ namespace Serein.Library
|
|||||||
MethodDynamicType = nodeType;
|
MethodDynamicType = nodeType;
|
||||||
ReturnType = Type.GetType(Info.ReturnTypeFullName);
|
ReturnType = Type.GetType(Info.ReturnTypeFullName);
|
||||||
ParameterDetailss = Info.ParameterDetailsInfos.Select(pinfo => new ParameterDetails(pinfo)).ToArray();
|
ParameterDetailss = Info.ParameterDetailsInfos.Select(pinfo => new ParameterDetails(pinfo)).ToArray();
|
||||||
IsParamsArgIndex = Info.IsParamsArgIndex;
|
ParamsArgIndex = Info.IsParamsArgIndex;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -213,7 +243,7 @@ namespace Serein.Library
|
|||||||
NodeType = this.MethodDynamicType.ToString(),
|
NodeType = this.MethodDynamicType.ToString(),
|
||||||
ParameterDetailsInfos = this.ParameterDetailss.Select(p => p.ToInfo()).ToArray(),
|
ParameterDetailsInfos = this.ParameterDetailss.Select(p => p.ToInfo()).ToArray(),
|
||||||
ReturnTypeFullName = this.ReturnType.FullName,
|
ReturnTypeFullName = this.ReturnType.FullName,
|
||||||
IsParamsArgIndex = this.IsParamsArgIndex,
|
IsParamsArgIndex = this.ParamsArgIndex,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -234,7 +264,7 @@ namespace Serein.Library
|
|||||||
MethodName = this.MethodName,
|
MethodName = this.MethodName,
|
||||||
MethodLockName = this.MethodLockName,
|
MethodLockName = this.MethodLockName,
|
||||||
IsProtectionParameter = this.IsProtectionParameter,
|
IsProtectionParameter = this.IsProtectionParameter,
|
||||||
IsParamsArgIndex= this.IsParamsArgIndex,
|
ParamsArgIndex = this.ParamsArgIndex,
|
||||||
};
|
};
|
||||||
md.ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(); // 拷贝属于节点方法的新入参描述
|
md.ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(); // 拷贝属于节点方法的新入参描述
|
||||||
return md;
|
return md;
|
||||||
@@ -251,6 +281,7 @@ namespace Serein.Library
|
|||||||
for (int i = 0; i < ParameterDetailss.Length; i++)
|
for (int i = 0; i < ParameterDetailss.Length; i++)
|
||||||
{
|
{
|
||||||
ParameterDetails arg = this.ParameterDetailss[i];
|
ParameterDetails arg = this.ParameterDetailss[i];
|
||||||
|
sb.AppendLine(arg.ToString());
|
||||||
}
|
}
|
||||||
sb.AppendLine($"");
|
sb.AppendLine($"");
|
||||||
sb.AppendLine($"返回值信息:");
|
sb.AppendLine($"返回值信息:");
|
||||||
|
|||||||
@@ -323,28 +323,35 @@ namespace Serein.Library
|
|||||||
return null;// md.ActingInstance
|
return null;// md.ActingInstance
|
||||||
}
|
}
|
||||||
|
|
||||||
object[] parameters = new object[md.ParameterDetailss.Length];
|
object[] parameters;
|
||||||
|
|
||||||
//var previousFlowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据
|
Array paramsArgs = null; // 初始化可选参数
|
||||||
|
int paramsArgIndex = 0; // 可选参数下标,与 object[] paramsArgs 一起使用
|
||||||
|
Type paramsArgType = null; // 可变参数的参数类型
|
||||||
object[] paramsArgs = null; // 初始化可选参数
|
if (md.ParamsArgIndex >= 0)
|
||||||
int paramsArgIndex = 0; // 可选参数下标
|
|
||||||
if (md.IsParamsArgIndex >= 0) // 是否存在入参参数
|
|
||||||
{
|
{
|
||||||
// 可选参数数组长度 = 方法参数个数 - ( 可选入参下标 + 1 )
|
// 存在可变入参参数
|
||||||
int paramsLength = md.ParameterDetailss.Length - md.IsParamsArgIndex;
|
paramsArgType = md.ParameterDetailss[md.ParamsArgIndex].DataType.GetElementType(); // 获取可变参数的参数类型
|
||||||
paramsArgs = new object[paramsLength]; // 重新实例化可选参数
|
// 可变参数数组长度 = 方法参数个数 - ( 可选入参下标 + 1 )
|
||||||
parameters[md.ParameterDetailss.Length-1] = paramsArgs; // 如果存在可选参数,入参参数最后一项则为可选参数
|
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;
|
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]; // 方法入参描述
|
var pd = md.ParameterDetailss[i]; // 方法入参描述
|
||||||
|
|
||||||
// 入参参数下标循环到可选参数时,开始写入到可选参数数组
|
// 入参参数下标循环到可选参数时,开始写入到可选参数数组
|
||||||
if(i >= md.IsParamsArgIndex)
|
if(paramsArgs != null && i >= md.ParamsArgIndex)
|
||||||
{
|
{
|
||||||
// 控制参数赋值方向:
|
// 控制参数赋值方向:
|
||||||
// true => paramsArgs
|
// true => paramsArgs
|
||||||
@@ -352,13 +359,17 @@ namespace Serein.Library
|
|||||||
hasParams = true;
|
hasParams = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 可选参数为 Array 类型,所以需要获取子项类型
|
||||||
|
// 如果 hasParams 为 true ,说明一定存在可选参数,所以 paramsArgType 一定不为 null
|
||||||
|
Type argDataType = hasParams ? paramsArgType : pd.DataType;
|
||||||
|
|
||||||
#region 获取基础的上下文数据
|
#region 获取基础的上下文数据
|
||||||
if (pd.DataType == typeof(IFlowEnvironment)) // 获取流程上下文
|
if (argDataType == typeof(IFlowEnvironment)) // 获取流程上下文
|
||||||
{
|
{
|
||||||
parameters[i] = nodeModel.Env;
|
parameters[i] = nodeModel.Env;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (pd.DataType == typeof(IDynamicContext)) // 获取流程上下文
|
if (argDataType == typeof(IDynamicContext)) // 获取流程上下文
|
||||||
{
|
{
|
||||||
parameters[i] = context;
|
parameters[i] = context;
|
||||||
continue;
|
continue;
|
||||||
@@ -367,7 +378,7 @@ namespace Serein.Library
|
|||||||
|
|
||||||
#region 确定[预入参]数据
|
#region 确定[预入参]数据
|
||||||
object inputParameter; // 存放解析的临时参数
|
object inputParameter; // 存放解析的临时参数
|
||||||
if (pd.IsExplicitData) // 判断是否使用显示的输入参数
|
if (pd.IsExplicitData && !pd.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase)) // 判断是否使用显示的输入参数
|
||||||
{
|
{
|
||||||
// 使用输入的固定值
|
// 使用输入的固定值
|
||||||
inputParameter = pd.DataValue;
|
inputParameter = pd.DataValue;
|
||||||
@@ -415,10 +426,10 @@ namespace Serein.Library
|
|||||||
}
|
}
|
||||||
|
|
||||||
#region 对于非值类型的null检查
|
#region 对于非值类型的null检查
|
||||||
if (!pd.DataType.IsValueType && inputParameter is null)
|
if (!argDataType.IsValueType && inputParameter is null)
|
||||||
{
|
{
|
||||||
parameters[i] = 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;
|
// continue;
|
||||||
}
|
}
|
||||||
#endregion
|
#endregion
|
||||||
@@ -452,8 +463,9 @@ namespace Serein.Library
|
|||||||
{
|
{
|
||||||
if (hasParams)
|
if (hasParams)
|
||||||
{
|
{
|
||||||
|
paramsArgs.SetValue(value, paramsArgIndex++);
|
||||||
// 处理可选参数
|
// 处理可选参数
|
||||||
paramsArgs[paramsArgIndex++] = value;
|
//paramsArgs[paramsArgIndex++] = value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -466,7 +478,7 @@ namespace Serein.Library
|
|||||||
|
|
||||||
#region 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型,如果获取成功(不为null)会跳过循环
|
#region 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型,如果获取成功(不为null)会跳过循环
|
||||||
// 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型
|
// 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型
|
||||||
if (pd.ExplicitType.IsEnum && pd.DataType != pd.ExplicitType)
|
if (pd.ExplicitType.IsEnum && argDataType != pd.ExplicitType)
|
||||||
{
|
{
|
||||||
var resultEnum = Enum.Parse(pd.ExplicitType, pd.DataValue);
|
var resultEnum = Enum.Parse(pd.ExplicitType, pd.DataValue);
|
||||||
// 获取绑定的类型
|
// 获取绑定的类型
|
||||||
@@ -483,7 +495,8 @@ namespace Serein.Library
|
|||||||
if (hasParams)
|
if (hasParams)
|
||||||
{
|
{
|
||||||
// 处理可选参数
|
// 处理可选参数
|
||||||
paramsArgs[paramsArgIndex++] = value;
|
paramsArgs.SetValue(value, paramsArgIndex++);
|
||||||
|
//paramsArgs[paramsArgIndex++] = value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -498,30 +511,30 @@ namespace Serein.Library
|
|||||||
|
|
||||||
#region 对入参数据尝试进行转换
|
#region 对入参数据尝试进行转换
|
||||||
object tmpVaue = null; // 临时存放数据,最后才判断是否放置可选参数数组
|
object tmpVaue = null; // 临时存放数据,最后才判断是否放置可选参数数组
|
||||||
if (inputParameter.GetType() == pd.DataType)
|
if (inputParameter.GetType() == argDataType)
|
||||||
{
|
{
|
||||||
tmpVaue = inputParameter; // 类型一致无需转换,直接装入入参数组
|
tmpVaue = inputParameter; // 类型一致无需转换,直接装入入参数组
|
||||||
}
|
}
|
||||||
else if (pd.DataType.IsValueType)
|
else if (argDataType.IsValueType)
|
||||||
{
|
{
|
||||||
// 值类型
|
// 值类型
|
||||||
var valueStr = inputParameter?.ToString();
|
var valueStr = inputParameter?.ToString();
|
||||||
tmpVaue = valueStr.ToValueData(pd.DataType); // 类型不一致,尝试进行转换,如果转换失败返回类型对应的默认值
|
tmpVaue = valueStr.ToValueData(argDataType); // 类型不一致,尝试进行转换,如果转换失败返回类型对应的默认值
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// 引用类型
|
// 引用类型
|
||||||
if (pd.DataType == typeof(string)) // 转为字符串
|
if (argDataType == typeof(string)) // 转为字符串
|
||||||
{
|
{
|
||||||
var valueStr = inputParameter?.ToString();
|
var valueStr = inputParameter?.ToString();
|
||||||
tmpVaue = valueStr;
|
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;
|
tmpVaue = inputParameter;
|
||||||
}
|
}
|
||||||
@@ -530,12 +543,12 @@ namespace Serein.Library
|
|||||||
//{
|
//{
|
||||||
// var enumerableMethods = typeof(Enumerable).GetMethods(); // 获取所有的 Enumerable 扩展方法
|
// var enumerableMethods = typeof(Enumerable).GetMethods(); // 获取所有的 Enumerable 扩展方法
|
||||||
// MethodInfo conversionMethod;
|
// MethodInfo conversionMethod;
|
||||||
// if (pd.DataType.IsArray) // 转为数组
|
// if (argDataType.IsArray) // 转为数组
|
||||||
// {
|
// {
|
||||||
// parameters[i] = inputParameter;
|
// parameters[i] = inputParameter;
|
||||||
// conversionMethod = enumerableMethods.FirstOrDefault(m => m.Name == "ToArray" && m.IsGenericMethodDefinition);
|
// 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);
|
// conversionMethod = enumerableMethods.FirstOrDefault(m => m.Name == "ToList" && m.IsGenericMethodDefinition);
|
||||||
// }
|
// }
|
||||||
@@ -543,7 +556,7 @@ namespace Serein.Library
|
|||||||
// {
|
// {
|
||||||
// throw new InvalidOperationException("输入对象不是集合或目标类型不支持(目前仅支持Array、List的自动转换)");
|
// throw new InvalidOperationException("输入对象不是集合或目标类型不支持(目前仅支持Array、List的自动转换)");
|
||||||
// }
|
// }
|
||||||
// var genericMethod = conversionMethod.MakeGenericMethod(pd.DataType);
|
// var genericMethod = conversionMethod.MakeGenericMethod(argDataType);
|
||||||
// var result = genericMethod.Invoke(null, new object[] { collection });
|
// var result = genericMethod.Invoke(null, new object[] { collection });
|
||||||
// parameters[i] = result;
|
// parameters[i] = result;
|
||||||
//}
|
//}
|
||||||
@@ -556,7 +569,8 @@ namespace Serein.Library
|
|||||||
if (hasParams)
|
if (hasParams)
|
||||||
{
|
{
|
||||||
// 处理可选参数
|
// 处理可选参数
|
||||||
paramsArgs[paramsArgIndex++] = tmpVaue;
|
paramsArgs.SetValue(tmpVaue, paramsArgIndex++);
|
||||||
|
//paramsArgs[paramsArgIndex++] = tmpVaue;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -133,6 +133,7 @@ namespace Serein.Library
|
|||||||
ExplicitType = Type.GetType(info.ExplicitTypeFullName);
|
ExplicitType = Type.GetType(info.ExplicitTypeFullName);
|
||||||
ExplicitTypeName = info.ExplicitTypeName;
|
ExplicitTypeName = info.ExplicitTypeName;
|
||||||
Items = info.Items;
|
Items = info.Items;
|
||||||
|
IsParams = info.IsParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -144,6 +145,7 @@ namespace Serein.Library
|
|||||||
return new ParameterDetailsInfo
|
return new ParameterDetailsInfo
|
||||||
{
|
{
|
||||||
Index = this.Index,
|
Index = this.Index,
|
||||||
|
IsParams = this.IsParams,
|
||||||
DataTypeFullName = this.DataType.FullName,
|
DataTypeFullName = this.DataType.FullName,
|
||||||
Name = this.Name,
|
Name = this.Name,
|
||||||
ExplicitTypeFullName = this.ExplicitType.FullName,
|
ExplicitTypeFullName = this.ExplicitType.FullName,
|
||||||
@@ -170,6 +172,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(),
|
||||||
|
IsParams = this.IsParams,
|
||||||
|
|
||||||
};
|
};
|
||||||
return pd;
|
return pd;
|
||||||
|
|||||||
@@ -17,6 +17,11 @@ namespace Serein.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int Index { get; set; }
|
public int Index { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为可变参数
|
||||||
|
/// </summary>
|
||||||
|
public bool IsParams { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 方法需要的类型
|
/// 方法需要的类型
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ namespace Serein.Library.Utils
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
HasResultTask,
|
HasResultTask,
|
||||||
}
|
}
|
||||||
|
|
||||||
public static bool IsGenericTask(Type returnType, out Type taskResult)
|
public static bool IsGenericTask(Type returnType, out Type taskResult)
|
||||||
{
|
{
|
||||||
// 判断是否为 Task 类型或泛型 Task<T>
|
// 判断是否为 Task 类型或泛型 Task<T>
|
||||||
@@ -51,6 +52,8 @@ namespace Serein.Library.Utils
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 根据方法信息创建动态调用的委托,返回方法类型,以及传出一个委托
|
/// 根据方法信息创建动态调用的委托,返回方法类型,以及传出一个委托
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -82,6 +85,8 @@ namespace Serein.Library.Utils
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
dynamicMethod = new DynamicMethod(
|
dynamicMethod = new DynamicMethod(
|
||||||
name: methodInfo.Name + "_DynamicEmitMethod",
|
name: methodInfo.Name + "_DynamicEmitMethod",
|
||||||
returnType: returnType,
|
returnType: returnType,
|
||||||
@@ -89,8 +94,6 @@ namespace Serein.Library.Utils
|
|||||||
restrictedSkipVisibility: true // 跳过私有方法访问限制
|
restrictedSkipVisibility: true // 跳过私有方法访问限制
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var il = dynamicMethod.GetILGenerator();
|
var il = dynamicMethod.GetILGenerator();
|
||||||
|
|
||||||
// 加载实例 (this)
|
// 加载实例 (this)
|
||||||
@@ -110,10 +113,16 @@ namespace Serein.Library.Utils
|
|||||||
{
|
{
|
||||||
il.Emit(OpCodes.Unbox_Any, paramType);
|
il.Emit(OpCodes.Unbox_Any, paramType);
|
||||||
}
|
}
|
||||||
|
//else if (paramType.IsGenericParameter) // 如果是泛型参数,直接转换
|
||||||
|
//{
|
||||||
|
// il.Emit(OpCodes.Castclass, paramType);
|
||||||
|
//}
|
||||||
else // 如果是引用类型,直接转换
|
else // 如果是引用类型,直接转换
|
||||||
{
|
{
|
||||||
il.Emit(OpCodes.Castclass, paramType);
|
il.Emit(OpCodes.Castclass, paramType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 调用方法
|
// 调用方法
|
||||||
|
|||||||
@@ -1316,7 +1316,28 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 改变可选参数的数目
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeGuid">对应的节点Guid</param>
|
||||||
|
/// <param name="isAdd">true,增加参数;false,减少参数</param>
|
||||||
|
/// <param name="paramIndex">以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -425,7 +425,17 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 改变可选参数的数目
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeGuid">对应的节点Guid</param>
|
||||||
|
/// <param name="isAdd">true,增加参数;false,减少参数</param>
|
||||||
|
/// <param name="paramIndex">以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
||||||
|
{
|
||||||
|
return await currentFlowEnvironment.ChangeParameter(nodeGuid, isAdd, paramIndex);
|
||||||
|
}
|
||||||
|
|
||||||
#region 流程依赖类库的接口
|
#region 流程依赖类库的接口
|
||||||
|
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
methodDetails = new MethodDetails();
|
methodDetails = new MethodDetails();
|
||||||
}
|
}
|
||||||
var md = methodDetails.CloneOfNode(nodeModel.Env, nodeModel);
|
var md = methodDetails.CloneOfNode(nodeModel);
|
||||||
nodeModel.DisplayName = md.MethodAnotherName;
|
nodeModel.DisplayName = md.MethodAnotherName;
|
||||||
nodeModel.MethodDetails = md;
|
nodeModel.MethodDetails = md;
|
||||||
nodeModel.OnCreating();
|
nodeModel.OnCreating();
|
||||||
|
|||||||
@@ -819,6 +819,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)));
|
UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
||||||
{
|
{
|
||||||
if(IsLoadingProject || IsLoadingNode)
|
if(IsLoadingProject || IsLoadingNode)
|
||||||
@@ -836,6 +837,18 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 改变可选参数的数目
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="nodeGuid">对应的节点Guid</param>
|
||||||
|
/// <param name="isAdd">true,增加参数;false,减少参数</param>
|
||||||
|
/// <param name="paramIndex">以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public async Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
||||||
|
{
|
||||||
|
Console.WriteLine("远程环境尚未实现的接口:ChangeParameter");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
#region 流程依赖类库的接口
|
#region 流程依赖类库的接口
|
||||||
|
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Serein.Library;
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Serein.Library.FlowNode;
|
using Serein.Library.FlowNode;
|
||||||
|
using System.Diagnostics;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool;
|
namespace Serein.NodeFlow.Tool;
|
||||||
|
|
||||||
@@ -31,8 +32,12 @@ public static class NodeMethodDetailsHelper
|
|||||||
}
|
}
|
||||||
//var dllTypeName = $"{assemblyName}.{type.Name}";
|
//var dllTypeName = $"{assemblyName}.{type.Name}";
|
||||||
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
|
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
|
||||||
|
Console.WriteLine("loading method : " +dllTypeMethodName);
|
||||||
|
Debug.WriteLine("loading method : " +dllTypeMethodName);
|
||||||
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
|
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//// 通过表达式树生成委托
|
//// 通过表达式树生成委托
|
||||||
//var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
|
//var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
|
||||||
// method, // 方法信息
|
// method, // 方法信息
|
||||||
@@ -92,9 +97,13 @@ public static class NodeMethodDetailsHelper
|
|||||||
var asyncPrefix = "[异步]"; // IsGenericTask(returnType) ? "[async]" : ;
|
var asyncPrefix = "[异步]"; // IsGenericTask(returnType) ? "[async]" : ;
|
||||||
var methodMethodAnotherName = isTask ? asyncPrefix + attribute.AnotherName : attribute.AnotherName;
|
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,
|
ActingInstanceType = type,
|
||||||
// ActingInstance = instance,
|
// ActingInstance = instance,
|
||||||
@@ -104,6 +113,8 @@ public static class NodeMethodDetailsHelper
|
|||||||
MethodAnotherName = methodMethodAnotherName,
|
MethodAnotherName = methodMethodAnotherName,
|
||||||
ParameterDetailss = explicitDataOfParameters,
|
ParameterDetailss = explicitDataOfParameters,
|
||||||
ReturnType = returnType,
|
ReturnType = returnType,
|
||||||
|
// 如果存在可变参数,取最后一个元素的下标,否则为-1;
|
||||||
|
ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length-1 : -1,
|
||||||
};
|
};
|
||||||
var dd = new DelegateDetails(emitMethodType, methodDelegate) ;
|
var dd = new DelegateDetails(emitMethodType, methodDelegate) ;
|
||||||
return (md, dd);
|
return (md, dd);
|
||||||
@@ -144,16 +155,19 @@ public static class NodeMethodDetailsHelper
|
|||||||
private static ParameterDetails[] GetExplicitDataOfParameters(ParameterInfo[] parameters)
|
private static ParameterDetails[] GetExplicitDataOfParameters(ParameterInfo[] parameters)
|
||||||
{
|
{
|
||||||
|
|
||||||
return parameters.Select((it, index) =>
|
var tempParams = parameters.Select((it, index) =>
|
||||||
{
|
{
|
||||||
Type paremType;
|
Type paremType;
|
||||||
|
|
||||||
if (it.GetCustomAttribute<EnumTypeConvertorAttribute>() is EnumTypeConvertorAttribute attribute1 && attribute1 is not null)
|
#region 存在“枚举=>类型”转换器
|
||||||
|
if (it.GetCustomAttribute<EnumTypeConvertorAttribute>() is EnumTypeConvertorAttribute attribute1 && attribute1 is not null)
|
||||||
{
|
{
|
||||||
// 存在类型选择器
|
// 存在类型选择器
|
||||||
paremType = attribute1.EnumType;
|
paremType = attribute1.EnumType;
|
||||||
return GetExplicitDataOfParameter(it, index, paremType, true);
|
return GetExplicitDataOfParameter(it, index, paremType, true); // “枚举=>类型”转换器 获取参数
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
#region 存在自定义的转换器
|
||||||
else if (it.GetCustomAttribute<BindConvertorAttribute>() is BindConvertorAttribute attribute2 && attribute2 is not null)
|
else if (it.GetCustomAttribute<BindConvertorAttribute>() is BindConvertorAttribute attribute2 && attribute2 is not null)
|
||||||
{
|
{
|
||||||
paremType = attribute2.EnumType;
|
paremType = attribute2.EnumType;
|
||||||
@@ -180,29 +194,22 @@ public static class NodeMethodDetailsHelper
|
|||||||
return methodInfo?.Invoke(obj, [enumValue]);
|
return methodInfo?.Invoke(obj, [enumValue]);
|
||||||
}
|
}
|
||||||
// 确保实例实现了所需接口
|
// 确保实例实现了所需接口
|
||||||
ParameterDetails ed = GetExplicitDataOfParameter(it, index, paremType, true, func);
|
ParameterDetails ed = GetExplicitDataOfParameter(it, index, paremType, true, func); // 自定义的转换器 获取参数
|
||||||
|
|
||||||
return ed;
|
return ed;
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
#region 常规方法的获取参数
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
return GetExplicitDataOfParameter(it, index, it.ParameterType, it.HasDefaultValue);
|
var tmp = GetExplicitDataOfParameter(it, index, it.ParameterType, it.HasDefaultValue); // 常规方法的获取参数
|
||||||
|
return tmp;
|
||||||
}
|
}
|
||||||
//string explicitTypeName = GetExplicitTypeName(paremType);
|
#endregion
|
||||||
//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(),
|
|
||||||
//};
|
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
|
|
||||||
|
|
||||||
|
return tempParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ParameterDetails GetExplicitDataOfParameter(ParameterInfo parameterInfo,
|
private static ParameterDetails GetExplicitDataOfParameter(ParameterInfo parameterInfo,
|
||||||
@@ -213,7 +220,6 @@ public static class NodeMethodDetailsHelper
|
|||||||
{
|
{
|
||||||
|
|
||||||
bool hasParams = parameterInfo.IsDefined(typeof(ParamArrayAttribute)); // 判断是否为可变参数
|
bool hasParams = parameterInfo.IsDefined(typeof(ParamArrayAttribute)); // 判断是否为可变参数
|
||||||
|
|
||||||
string explicitTypeName = GetExplicitTypeName(paremType);
|
string explicitTypeName = GetExplicitTypeName(paremType);
|
||||||
var items = GetExplicitItems(paremType, explicitTypeName);
|
var items = GetExplicitItems(paremType, explicitTypeName);
|
||||||
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
|
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
|
||||||
|
|||||||
@@ -2,125 +2,120 @@
|
|||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:local="clr-namespace:Serein.Workbench.Themes"
|
xmlns:local="clr-namespace:Serein.Workbench.Themes"
|
||||||
xmlns:view="clr-namespace:Serein.Workbench.Node.View"
|
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">
|
||||||
|
|
||||||
|
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
<converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
||||||
|
|
||||||
<Style TargetType="{x:Type local:MethodDetailsControl}">
|
<Style TargetType="{x:Type local:MethodDetailsControl}">
|
||||||
<Setter Property="Template">
|
<Setter Property="Template">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<ControlTemplate TargetType="{x:Type local:MethodDetailsControl}">
|
<ControlTemplate TargetType="{x:Type local:MethodDetailsControl}">
|
||||||
|
|
||||||
<!--根据方法入参数量生成相应的控件-->
|
<!--根据方法入参数量生成相应的控件-->
|
||||||
<ItemsControl ItemsSource="{Binding MethodDetails.ParameterDetailss, RelativeSource={RelativeSource TemplatedParent}}">
|
<ItemsControl ItemsSource="{Binding MethodDetails.ParameterDetailss, RelativeSource={RelativeSource TemplatedParent}}">
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Background="#E3FDFD" >
|
<Grid Background="#E3FDFD" >
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
<ColumnDefinition Width="auto"/>
|
<ColumnDefinition Width="auto"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<!--连接控制器-->
|
<!--连接控制器-->
|
||||||
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
|
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
|
||||||
<ContentControl Content="{Binding}" Grid.Column="1">
|
<!--参数索引提示-->
|
||||||
<ContentControl.Style>
|
<TextBlock Grid.Column="1" Text="{Binding Index,StringFormat=agr{0}}" Margin="2,0,2,0" VerticalAlignment="Center"/>
|
||||||
<Style TargetType="ContentControl">
|
<!--是否设置为显式参数-->
|
||||||
<Style.Triggers>
|
<CheckBox Grid.Column="2" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Margin="2,0,2,0" VerticalContentAlignment="Center"/>
|
||||||
|
<!--入参参数名称-->
|
||||||
<!--无须指定参数-->
|
<TextBlock Grid.Column="3" MinWidth="50" Text="{Binding Name}" Margin="2,0,2,0" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
<MultiDataTrigger>
|
<!--增加可选参数(如果有)-->
|
||||||
<MultiDataTrigger.Conditions>
|
<view:ParamsArgControl
|
||||||
<Condition Binding="{Binding IsExplicitData}" Value="false" />
|
x:Name="ParamsArgControl"
|
||||||
</MultiDataTrigger.Conditions>
|
ArgIndex="{Binding Index}"
|
||||||
<Setter Property="ContentTemplate">
|
MyNode="{Binding NodeModel}"
|
||||||
<Setter.Value>
|
Width="12"
|
||||||
|
Grid.Column="5" Margin="2,0,2,0" HorizontalAlignment="Right" VerticalAlignment="Center"
|
||||||
|
Visibility="{Binding IsParams, Mode=OneWay,
|
||||||
|
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}"
|
||||||
|
/>
|
||||||
|
<ContentControl Content="{Binding}" Grid.Column="4" VerticalAlignment="Center">
|
||||||
|
<ContentControl.Style>
|
||||||
|
<Style TargetType="ContentControl">
|
||||||
|
<Style.Triggers>
|
||||||
|
<!--无须指定参数-->
|
||||||
|
<MultiDataTrigger>
|
||||||
|
<MultiDataTrigger.Conditions>
|
||||||
|
<Condition Binding="{Binding IsExplicitData}" Value="false" />
|
||||||
|
</MultiDataTrigger.Conditions>
|
||||||
|
<Setter Property="ContentTemplate">
|
||||||
|
<Setter.Value>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="50"/>
|
|
||||||
<ColumnDefinition Width="30"/>
|
|
||||||
<ColumnDefinition Width="50"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<TextBlock Grid.Column="0" MinWidth="50" Text="无须指定参数"/>
|
||||||
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
|
||||||
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
|
||||||
<TextBlock Grid.Column="3" MinWidth="50" Text="无须指定参数"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
</DataTemplate>
|
||||||
|
</Setter.Value>
|
||||||
|
</Setter>
|
||||||
|
</MultiDataTrigger>
|
||||||
|
|
||||||
|
<!--指定参数:选项类型-->
|
||||||
</DataTemplate>
|
<MultiDataTrigger>
|
||||||
</Setter.Value>
|
<MultiDataTrigger.Conditions>
|
||||||
</Setter>
|
<Condition Binding="{Binding IsExplicitData}" Value="true" />
|
||||||
</MultiDataTrigger>
|
<Condition Binding="{Binding ExplicitTypeName}" Value="Select" />
|
||||||
|
</MultiDataTrigger.Conditions>
|
||||||
<!--指定参数:选项类型-->
|
<Setter Property="ContentTemplate">
|
||||||
<MultiDataTrigger>
|
<Setter.Value>
|
||||||
<MultiDataTrigger.Conditions>
|
<DataTemplate>
|
||||||
<Condition Binding="{Binding IsExplicitData}" Value="true" />
|
|
||||||
<Condition Binding="{Binding ExplicitTypeName}" Value="Select" />
|
|
||||||
</MultiDataTrigger.Conditions>
|
|
||||||
<Setter Property="ContentTemplate">
|
|
||||||
<Setter.Value>
|
|
||||||
<DataTemplate>
|
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<!--<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />-->
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="50"/>
|
|
||||||
<ColumnDefinition Width="30"/>
|
|
||||||
<ColumnDefinition Width="50"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<ComboBox Grid.Column="0"
|
||||||
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
MinWidth="50"
|
||||||
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
ItemsSource="{Binding Items}"
|
||||||
<ComboBox Grid.Column="3"
|
SelectedItem="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
MinWidth="50"
|
|
||||||
ItemsSource="{Binding Items}"
|
|
||||||
SelectedItem="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</DataTemplate>
|
||||||
</DataTemplate>
|
</Setter.Value>
|
||||||
</Setter.Value>
|
</Setter>
|
||||||
</Setter>
|
</MultiDataTrigger>
|
||||||
</MultiDataTrigger>
|
|
||||||
|
|
||||||
<!--指定参数:文本类型(可输入)-->
|
<!--指定参数:文本类型(可输入)-->
|
||||||
<MultiDataTrigger>
|
<MultiDataTrigger>
|
||||||
<MultiDataTrigger.Conditions>
|
<MultiDataTrigger.Conditions>
|
||||||
<Condition Binding="{Binding IsExplicitData}" Value="true" />
|
<Condition Binding="{Binding IsExplicitData}" Value="true" />
|
||||||
<Condition Binding="{Binding ExplicitTypeName}" Value="Value" />
|
<Condition Binding="{Binding ExplicitTypeName}" Value="Value" />
|
||||||
</MultiDataTrigger.Conditions>
|
</MultiDataTrigger.Conditions>
|
||||||
<Setter Property="ContentTemplate">
|
<Setter Property="ContentTemplate">
|
||||||
<Setter.Value>
|
<Setter.Value>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<StackPanel Orientation="Horizontal">
|
|
||||||
<!--<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />-->
|
|
||||||
<Grid>
|
<Grid>
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="50"/>
|
|
||||||
<ColumnDefinition Width="30"/>
|
|
||||||
<ColumnDefinition Width="50"/>
|
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
|
<TextBox Grid.Column="0" MinWidth="50" Text="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
|
||||||
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
|
||||||
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
|
||||||
<TextBox Grid.Column="3" MinWidth="50" Text="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</StackPanel>
|
</DataTemplate>
|
||||||
</DataTemplate>
|
</Setter.Value>
|
||||||
</Setter.Value>
|
</Setter>
|
||||||
</Setter>
|
</MultiDataTrigger>
|
||||||
</MultiDataTrigger>
|
|
||||||
|
|
||||||
</Style.Triggers>
|
</Style.Triggers>
|
||||||
</Style>
|
</Style>
|
||||||
</ContentControl.Style>
|
</ContentControl.Style>
|
||||||
</ContentControl>
|
</ContentControl>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
|
using Serein.Workbench.Node;
|
||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
using System.Windows.Data;
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
namespace Serein.Workbench.Themes
|
namespace Serein.Workbench.Themes
|
||||||
{
|
{
|
||||||
@@ -15,10 +17,12 @@ namespace Serein.Workbench.Themes
|
|||||||
{
|
{
|
||||||
if (isEnabled)
|
if (isEnabled)
|
||||||
{
|
{
|
||||||
|
// 返回文本框
|
||||||
if (valueType == typeof(string) || valueType == typeof(int) || valueType == typeof(double))
|
if (valueType == typeof(string) || valueType == typeof(int) || valueType == typeof(double))
|
||||||
{
|
{
|
||||||
return "TextBoxTemplate";
|
return "TextBoxTemplate";
|
||||||
}
|
}
|
||||||
|
// 返回可选列表框
|
||||||
else if (typeof(IEnumerable).IsAssignableFrom(valueType))
|
else if (typeof(IEnumerable).IsAssignableFrom(valueType))
|
||||||
{
|
{
|
||||||
return "ComboBoxTemplate";
|
return "ComboBoxTemplate";
|
||||||
@@ -44,23 +48,43 @@ namespace Serein.Workbench.Themes
|
|||||||
static MethodDetailsControl()
|
static MethodDetailsControl()
|
||||||
{
|
{
|
||||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(MethodDetailsControl), new FrameworkPropertyMetadata(typeof(MethodDetailsControl)));
|
DefaultStyleKeyProperty.OverrideMetadata(typeof(MethodDetailsControl), new FrameworkPropertyMetadata(typeof(MethodDetailsControl)));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region 绑定的方法信息
|
||||||
public MethodDetails MethodDetails
|
public MethodDetails MethodDetails
|
||||||
{
|
{
|
||||||
get { return (MethodDetails)GetValue(MethodDetailsProperty); }
|
get { return (MethodDetails)GetValue(MethodDetailsProperty); }
|
||||||
set { SetValue(MethodDetailsProperty, value); }
|
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)));
|
typeof(MethodDetailsControl), new PropertyMetadata(null, new PropertyChangedCallback(OnPropertyChange)));
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
static void OnPropertyChange(DependencyObject sender, DependencyPropertyChangedEventArgs args)
|
static void OnPropertyChange(DependencyObject sender, DependencyPropertyChangedEventArgs args)
|
||||||
{
|
{
|
||||||
|
//var MethodDetails = (MethodDetails)args.NewValue;
|
||||||
var MethodDetails = (MethodDetails)args.NewValue;
|
|
||||||
//MethodDetails.ExplicitDatas[0].
|
//MethodDetails.ExplicitDatas[0].
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ICommand CommandAddParams { get; }
|
||||||
|
|
||||||
|
public MethodDetailsControl()
|
||||||
|
{
|
||||||
|
CommandAddParams = new RelayCommand(ExecuteAddParams);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ExecuteAddParams(object parameter)
|
||||||
|
{
|
||||||
|
// 方法逻辑
|
||||||
|
this.MethodDetails.AddParamsArg();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,11 +9,141 @@ using System.Windows.Controls;
|
|||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
|
using System.Windows.Media.Media3D;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
namespace Serein.Workbench.Node.View
|
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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 所在的节点
|
||||||
|
/// </summary>
|
||||||
|
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)));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 参数的索引
|
||||||
|
/// </summary>
|
||||||
|
public int ArgIndex
|
||||||
|
{
|
||||||
|
get { return (int)GetValue(ArgIndexProperty); }
|
||||||
|
set { SetValue(ArgIndexProperty, value.ToString()); }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控件重绘事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawingContext"></param>
|
||||||
|
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
|
public abstract class JunctionControlBase : Shape
|
||||||
{
|
{
|
||||||
@@ -141,7 +271,11 @@ namespace Serein.Workbench.Node.View
|
|||||||
|
|
||||||
private object lockObj = new object();
|
private object lockObj = new object();
|
||||||
|
|
||||||
// 控件获得鼠标焦点事件
|
/// <summary>
|
||||||
|
/// 控件获得鼠标焦点事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
private void JunctionControlBase_MouseMove(object sender, MouseEventArgs e)
|
private void JunctionControlBase_MouseMove(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
//if (!GlobalJunctionData.MyGlobalConnectingData.IsCreateing) return;
|
//if (!GlobalJunctionData.MyGlobalConnectingData.IsCreateing) return;
|
||||||
@@ -150,10 +284,13 @@ namespace Serein.Workbench.Node.View
|
|||||||
IsMouseOver = true;
|
IsMouseOver = true;
|
||||||
|
|
||||||
//this.InvalidateVisual();
|
//this.InvalidateVisual();
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// 控件失去鼠标焦点事件
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控件失去鼠标焦点事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
private void JunctionControlBase_MouseLeave(object sender, MouseEventArgs e)
|
private void JunctionControlBase_MouseLeave(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
IsMouseOver = false;
|
IsMouseOver = false;
|
||||||
|
|||||||
26
Workbench/Node/RelayCommand.cs
Normal file
26
Workbench/Node/RelayCommand.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.Windows.Input;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node
|
||||||
|
{
|
||||||
|
|
||||||
|
public class RelayCommand : ICommand
|
||||||
|
{
|
||||||
|
private readonly Action<object> _execute;
|
||||||
|
private readonly Func<object, bool> _canExecute;
|
||||||
|
|
||||||
|
public RelayCommand(Action<object> execute, Func<object, bool> canExecute = null)
|
||||||
|
{
|
||||||
|
_execute = execute;
|
||||||
|
_canExecute = canExecute;
|
||||||
|
}
|
||||||
|
|
||||||
|
public event EventHandler CanExecuteChanged;
|
||||||
|
|
||||||
|
public bool CanExecute(object parameter) => _canExecute == null || _canExecute(parameter);
|
||||||
|
|
||||||
|
public void Execute(object parameter) => _execute(parameter);
|
||||||
|
|
||||||
|
public void RaiseCanExecuteChanged() => CanExecuteChanged?.Invoke(this, EventArgs.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user