mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-20 00:06:45 +08:00
新增了对NodeaAction特性标记方法中,对params可变参数的支持
This commit is contained in:
@@ -844,6 +844,15 @@ namespace Serein.Library.Api
|
||||
/// <returns></returns>
|
||||
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>
|
||||
/// 获取方法描述信息
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace Serein.Library
|
||||
/// <para>0表示第一个参数是可选参数</para>
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private int _isParamsArgIndex = -1;
|
||||
private int _paramsArgIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 出参类型
|
||||
@@ -89,42 +89,72 @@ namespace Serein.Library
|
||||
public partial class MethodDetails
|
||||
{
|
||||
|
||||
#region 新增可选参数
|
||||
#region 更改可变参数
|
||||
/// <summary>
|
||||
/// 新增可选参数
|
||||
/// 是否存在可变参数(-1表示不存在)
|
||||
/// </summary>
|
||||
public bool HasParamsArg => _paramsArgIndex >= 0;
|
||||
|
||||
/// <summary>
|
||||
/// 新增可变参数
|
||||
/// </summary>
|
||||
/// <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 >= 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;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 移除可选参数
|
||||
/// 移除可变参数
|
||||
/// </summary>
|
||||
/// <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 >= 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <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)
|
||||
{
|
||||
// 创建一个新数组,比原数组大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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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($"返回值信息:");
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -133,6 +133,7 @@ namespace Serein.Library
|
||||
ExplicitType = Type.GetType(info.ExplicitTypeFullName);
|
||||
ExplicitTypeName = info.ExplicitTypeName;
|
||||
Items = info.Items;
|
||||
IsParams = info.IsParams;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -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;
|
||||
|
||||
@@ -17,6 +17,11 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
public int Index { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为可变参数
|
||||
/// </summary>
|
||||
public bool IsParams { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 方法需要的类型
|
||||
/// </summary>
|
||||
|
||||
@@ -29,6 +29,7 @@ namespace Serein.Library.Utils
|
||||
/// </summary>
|
||||
HasResultTask,
|
||||
}
|
||||
|
||||
public static bool IsGenericTask(Type returnType, out Type taskResult)
|
||||
{
|
||||
// 判断是否为 Task 类型或泛型 Task<T>
|
||||
@@ -51,6 +52,8 @@ namespace Serein.Library.Utils
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据方法信息创建动态调用的委托,返回方法类型,以及传出一个委托
|
||||
/// </summary>
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 调用方法
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 流程依赖类库的接口
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
/// <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 流程依赖类库的接口
|
||||
|
||||
|
||||
@@ -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<EnumTypeConvertorAttribute>() is EnumTypeConvertorAttribute attribute1 && attribute1 is not null)
|
||||
|
||||
#region 存在“枚举=>类型”转换器
|
||||
if (it.GetCustomAttribute<EnumTypeConvertorAttribute>() 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<BindConvertorAttribute>() 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"; // 布尔值 转为 可选类型
|
||||
|
||||
@@ -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">
|
||||
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
||||
|
||||
<Style TargetType="{x:Type local:MethodDetailsControl}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type local:MethodDetailsControl}">
|
||||
|
||||
<!--根据方法入参数量生成相应的控件-->
|
||||
<ItemsControl ItemsSource="{Binding MethodDetails.ParameterDetailss, RelativeSource={RelativeSource TemplatedParent}}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<Grid Background="#E3FDFD" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<!--连接控制器-->
|
||||
<view:ArgJunctionControl x:Name="ArgJunctionControl" Grid.Column="0" ArgIndex="{Binding Index}" MyNode="{Binding NodeModel}" />
|
||||
<ContentControl Content="{Binding}" Grid.Column="1">
|
||||
<ContentControl.Style>
|
||||
<Style TargetType="ContentControl">
|
||||
<Style.Triggers>
|
||||
|
||||
<!--无须指定参数-->
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsExplicitData}" Value="false" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<!--参数索引提示-->
|
||||
<TextBlock Grid.Column="1" Text="{Binding Index,StringFormat=agr{0}}" Margin="2,0,2,0" VerticalAlignment="Center"/>
|
||||
<!--是否设置为显式参数-->
|
||||
<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"/>
|
||||
<!--增加可选参数(如果有)-->
|
||||
<view:ParamsArgControl
|
||||
x:Name="ParamsArgControl"
|
||||
ArgIndex="{Binding Index}"
|
||||
MyNode="{Binding NodeModel}"
|
||||
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>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<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"/>
|
||||
<TextBlock Grid.Column="3" MinWidth="50" Text="无须指定参数"/>
|
||||
<TextBlock Grid.Column="0" MinWidth="50" Text="无须指定参数"/>
|
||||
</Grid>
|
||||
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiDataTrigger>
|
||||
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiDataTrigger>
|
||||
|
||||
<!--指定参数:选项类型-->
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<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}" />-->
|
||||
<!--指定参数:选项类型-->
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsExplicitData}" Value="true" />
|
||||
<Condition Binding="{Binding ExplicitTypeName}" Value="Select" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<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"/>
|
||||
<ComboBox Grid.Column="3"
|
||||
MinWidth="50"
|
||||
ItemsSource="{Binding Items}"
|
||||
SelectedItem="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
<ComboBox Grid.Column="0"
|
||||
MinWidth="50"
|
||||
ItemsSource="{Binding Items}"
|
||||
SelectedItem="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiDataTrigger>
|
||||
|
||||
<!--指定参数:文本类型(可输入)-->
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsExplicitData}" Value="true" />
|
||||
<Condition Binding="{Binding ExplicitTypeName}" Value="Value" />
|
||||
</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}" />-->
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiDataTrigger>
|
||||
|
||||
<!--指定参数:文本类型(可输入)-->
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsExplicitData}" Value="true" />
|
||||
<Condition Binding="{Binding ExplicitTypeName}" Value="Value" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<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}"/>
|
||||
<TextBox Grid.Column="0" MinWidth="50" Text="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</StackPanel>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiDataTrigger>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiDataTrigger>
|
||||
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ContentControl.Style>
|
||||
</ContentControl>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ContentControl.Style>
|
||||
</ContentControl>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <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
|
||||
{
|
||||
@@ -141,7 +271,11 @@ namespace Serein.Workbench.Node.View
|
||||
|
||||
private object lockObj = new object();
|
||||
|
||||
// 控件获得鼠标焦点事件
|
||||
/// <summary>
|
||||
/// 控件获得鼠标焦点事件
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
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();
|
||||
|
||||
|
||||
}
|
||||
// 控件失去鼠标焦点事件
|
||||
|
||||
/// <summary>
|
||||
/// 控件失去鼠标焦点事件
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void JunctionControlBase_MouseLeave(object sender, MouseEventArgs e)
|
||||
{
|
||||
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