diff --git a/Library/Entity/ExplicitData.cs b/Library/Entity/ExplicitData.cs index 8f46811..359c064 100644 --- a/Library/Entity/ExplicitData.cs +++ b/Library/Entity/ExplicitData.cs @@ -16,18 +16,9 @@ namespace Serein.Library.Entity /// public int Index { get; set; } /// - /// 是否为显式参数 + /// 是否为显式参数(固定值/表达式) /// public bool IsExplicitData { get; set; } - /// - /// 显式类型 - /// - public Type ExplicitType { get; set; } - - /// - /// 显示类型编号> - /// - public string ExplicitTypeName { get; set; } /// /// 方法需要的类型 @@ -45,21 +36,14 @@ namespace Serein.Library.Entity public string DataValue { get; set; } - - public string[] Items { get; set; } - - - public ExplicitData Clone() => new ExplicitData() { Index = Index, IsExplicitData = IsExplicitData, - ExplicitType = ExplicitType, DataType = DataType, ParameterName = ParameterName, - ExplicitTypeName = ExplicitTypeName, DataValue = string.IsNullOrEmpty(DataValue) ? string.Empty : DataValue, Items = Items.Select(it => it).ToArray(), }; diff --git a/Library/Ex/FlipflopException.cs b/Library/Ex/FlipflopException.cs index d562da4..6742039 100644 --- a/Library/Ex/FlipflopException.cs +++ b/Library/Ex/FlipflopException.cs @@ -1,4 +1,5 @@ using System; +using System.CodeDom; namespace Serein.Library.Ex { @@ -7,6 +8,7 @@ namespace Serein.Library.Ex /// public class FlipflopException: Exception { + public bool IsCancel { get; } public FlipflopException(string message, bool isCancel = true) :base(message) { diff --git a/Library/Web/WebServer.cs b/Library/Web/WebServer.cs index eb7cf81..436461e 100644 --- a/Library/Web/WebServer.cs +++ b/Library/Web/WebServer.cs @@ -40,8 +40,23 @@ namespace Serein.Library.Web } listener.Prefixes.Add(prefixe); // 添加监听前缀 - - listener.Start(); // 开始监听 + try + { + listener.Start(); // 开始监听 + Task.Run(async () => + { + while (listener.IsListening) + { + var context = await listener.GetContextAsync(); // 获取请求上下文 + ProcessRequestAsync(context); // 处理请求 + } + }); + } + catch(Exception ex) + { + listener = null; + Console.WriteLine(ex); + } //_ = Task.Run(async () => //{ @@ -58,14 +73,7 @@ namespace Serein.Library.Web // } //}); - Task.Run(async () => - { - while (listener.IsListening) - { - var context = await listener.GetContextAsync(); // 获取请求上下文 - ProcessRequestAsync(context); // 处理请求 - } - }); + return this; } @@ -115,8 +123,8 @@ namespace Serein.Library.Web // 停止服务器 public void Stop() { - listener.Stop(); // 停止监听 - listener.Close(); // 关闭监听器 + listener?.Stop(); // 停止监听 + listener?.Close(); // 关闭监听器 } } diff --git a/NodeFlow/Base/NodeModelBaseData.cs b/NodeFlow/Base/NodeModelBaseData.cs index c4e9db5..395d18f 100644 --- a/NodeFlow/Base/NodeModelBaseData.cs +++ b/NodeFlow/Base/NodeModelBaseData.cs @@ -23,6 +23,12 @@ namespace Serein.NodeFlow.Base SuccessorNodes[ctType] = []; } } + + /// + /// 是否中断(调试中断功能) + /// + public bool IsInterrupt { get; set; } + /// /// 节点对应的控件类型 /// @@ -65,11 +71,6 @@ namespace Serein.NodeFlow.Base public ConnectionType NextOrientation { get; set; } = ConnectionType.None; - /// - /// 当前执行状态(进入真分支还是假分支,异常分支在异常中确定) - /// - // public FlowStateType FlowState { get; set; } = FlowStateType.Cancel; - /// /// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值) /// @@ -80,10 +81,17 @@ namespace Serein.NodeFlow.Base /// public object? FlowData { get; set; } = null; - // public NodeModelBaseBuilder Build() => new NodeModelBaseBuilder(this); } + public class DebugInfo + { + /// + /// 是否中断 + /// + public bool IsInterrupt { get;set; } + } + /// /// 节点基类(数据):条件控件,动作控件,条件区域,动作区域 diff --git a/NodeFlow/Base/NodeModelBaseFunc.cs b/NodeFlow/Base/NodeModelBaseFunc.cs index 540971a..951b3fc 100644 --- a/NodeFlow/Base/NodeModelBaseFunc.cs +++ b/NodeFlow/Base/NodeModelBaseFunc.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json; +using Newtonsoft.Json.Linq; using Serein.Library.Api; using Serein.Library.Entity; using Serein.Library.Enums; @@ -7,6 +8,7 @@ using Serein.NodeFlow.Tool.SereinExpression; using System; using System.Collections.Generic; using System.Linq; +using System.Net.Http.Headers; using System.Text; using System.Threading.Tasks; @@ -18,6 +20,8 @@ namespace Serein.NodeFlow.Base /// public abstract partial class NodeModelBase : IDynamicFlowNode { + #region 导出/导入项目文件节点信息 + internal abstract Parameterdata[] GetParameterdatas(); internal virtual NodeInfo ToInfo() { @@ -42,7 +46,7 @@ namespace Serein.NodeFlow.Base UpstreamNodes = upstreamNodes.ToArray(), ParameterData = parameterData.ToArray(), ErrorNodes = errorNodes.ToArray(), - + }; } @@ -78,6 +82,7 @@ namespace Serein.NodeFlow.Base return this; } + #endregion /// @@ -89,7 +94,7 @@ namespace Serein.NodeFlow.Base { var cts = context.SereinIoc.GetOrRegisterInstantiate(); - Stack stack = []; + Stack stack = new Stack(); stack.Push(this); while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环 @@ -98,31 +103,28 @@ namespace Serein.NodeFlow.Base var currentNode = stack.Pop(); // 设置方法执行的对象 - if (currentNode.MethodDetails is not null && currentNode.MethodDetails.ActingInstanceType is not null) + if (currentNode.MethodDetails?.ActingInstance == null && currentNode.MethodDetails?.ActingInstanceType is not null) { - // currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrInstantiate(MethodDetails.ActingInstanceType); - // currentNode.MethodDetails.ActingInstance = context.SereinIoc.GetOrInstantiate(MethodDetails.ActingInstanceType); - - currentNode.MethodDetails.ActingInstance = context.SereinIoc.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType); + currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType); } - // 获取上游分支,首先执行一次 + // 首先执行上游分支 var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream]; for (int i = upstreamNodes.Count - 1; i >= 0; i--) { upstreamNodes[i].PreviousNode = currentNode; - await upstreamNodes[i].StartExecution(context); + await upstreamNodes[i].StartExecution(context); // 执行上游分支 } + // 判断是否为触发器节点,如果是,则开始等待。 if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == NodeType.Flipflop) { - // 触发器节点 - currentNode.FlowData = await currentNode.ExecuteAsync(context); + + currentNode.FlowData = await currentNode.ExecuteAsync(context); // 流程中遇到了触发器 } else { - // 动作节点 - currentNode.FlowData = currentNode.Execute(context); + currentNode.FlowData = currentNode.Execute(context); // 流程中正常执行 } if(currentNode.NextOrientation == ConnectionType.None) @@ -131,6 +133,7 @@ namespace Serein.NodeFlow.Base break; } + // 获取下一分支 var nextNodes = currentNode.SuccessorNodes[currentNode.NextOrientation]; // 将下一个节点集合中的所有节点逆序推入栈中 @@ -151,35 +154,21 @@ namespace Serein.NodeFlow.Base public virtual object? Execute(IDynamicContext context) { MethodDetails md = MethodDetails; - object? result = null; var del = md.MethodDelegate; + object instance = md.ActingInstance; + + var haveParameter = md.ExplicitDatas.Length > 0; + var haveResult = md.ReturnType != typeof(void); try { - if (md.ExplicitDatas.Length == 0) + // Action/Func([方法作用的实例],[可能的参数值],[可能的返回值]) + object? result = (haveParameter, haveResult) switch { - if (md.ReturnType == typeof(void)) - { - ((Action)del).Invoke(md.ActingInstance); - } - else - { - result = ((Func)del).Invoke(md.ActingInstance); - } - } - else - { - object?[]? parameters = GetParameters(context, MethodDetails); - if (md.ReturnType == typeof(void)) - { - ((Action)del).Invoke(md.ActingInstance, parameters); - } - else - { - var func = del as Func; - //result = ((Func)del).Invoke(md.ActingInstance, parameters); - result = func?.Invoke(md.ActingInstance, parameters); - } - } + (false, false) => Execution((Action)del, instance), // 调用节点方法,返回null + (true, false) => Execution((Action)del, instance, GetParameters(context, md)), // 调用节点方法,返回null + (false, true) => Execution((Func)del, instance), // 调用节点方法,返回方法传回类型 + (true, true) => Execution((Func)del, instance, GetParameters(context, md)), // 调用节点方法,获取入参参数,返回方法忏悔类型 + }; NextOrientation = ConnectionType.IsSucceed; return result; } @@ -187,9 +176,8 @@ namespace Serein.NodeFlow.Base { NextOrientation = ConnectionType.IsError; RuningException = ex; + return null; } - - return result; } /// @@ -201,41 +189,63 @@ namespace Serein.NodeFlow.Base public virtual async Task ExecuteAsync(IDynamicContext context) { MethodDetails md = MethodDetails; - object? result = null; - - IFlipflopContext flipflopContext = null; + Delegate del = md.MethodDelegate; + object instance = md.ActingInstance; + var haveParameter = md.ExplicitDatas.Length >= 0; try { // 调用委托并获取结果 - if (md.ExplicitDatas.Length == 0) + Task flipflopTask = haveParameter switch { - flipflopContext = await ((Func>)md.MethodDelegate).Invoke(MethodDetails.ActingInstance); - } - else - { - object?[]? parameters = GetParameters(context, MethodDetails); - flipflopContext = await ((Func>)md.MethodDelegate).Invoke(MethodDetails.ActingInstance, parameters); - } - if (flipflopContext == null) - { - throw new FlipflopException("没有返回上下文"); - } + true => ((Func>)del).Invoke(instance, GetParameters(context, md)), // 执行流程中的触发器方法时获取入参参数 + false => ((Func>)del).Invoke(instance), + }; + + IFlipflopContext flipflopContext = (await flipflopTask) ?? throw new FlipflopException("没有返回上下文"); NextOrientation = flipflopContext.State.ToContentType(); - result = flipflopContext.Data; + return flipflopContext.Data; } + //catch(FlipflopException ex) + //{ + // NextOrientation = ConnectionType.IsError; + // RuningException = ex; + // return null; + //} catch (Exception ex) { NextOrientation = ConnectionType.IsError; RuningException = ex; + return null; } - - return result; } + + #region 节点转换的委托类型 + public static object? Execution(Action del, object instance) + { + del?.Invoke(instance); + return null; + } + public static object? Execution(Action del, object instance, object?[]? parameters) + { + del?.Invoke(instance, parameters); + return null; + } + public static object? Execution(Func del, object instance) + { + return del?.Invoke(instance); + } + public static object? Execution(Func del, object instance, object?[]? parameters) + { + return del?.Invoke(instance, parameters); + } + #endregion + + /// /// 获取对应的参数数组 /// - public object[]? GetParameters(IDynamicContext context, MethodDetails md) + public object?[]? GetParameters(IDynamicContext context, MethodDetails md) { // 用正确的大小初始化参数数组 var types = md.ExplicitDatas.Select(it => it.DataType).ToArray(); @@ -244,151 +254,67 @@ namespace Serein.NodeFlow.Base return [md.ActingInstance]; } - object[]? parameters = new object[types.Length]; + object?[]? parameters = new object[types.Length]; + var flowData = PreviousNode?.FlowData; // 当前传递的数据 + var previousDataType = flowData?.GetType(); for (int i = 0; i < types.Length; i++) { - - var mdEd = md.ExplicitDatas[i]; - Type type = mdEd.DataType; - - var f1 = PreviousNode?.FlowData?.GetType(); - var f2 = mdEd.DataType; - if (type == typeof(IDynamicContext)) + if (flowData is null) { - parameters[i] = context; - } - else if (type == typeof(MethodDetails)) - { - parameters[i] = md; - } - else if (type == typeof(NodeModelBase)) - { - parameters[i] = this; - } - else if (mdEd.IsExplicitData) // 显式参数 - { - // 判断是否使用表达式解析 - if (mdEd.DataValue[0] == '@') + parameters[i] = md.ExplicitDatas[i].DataType switch { - var expResult = SerinExpressionEvaluator.Evaluate(mdEd.DataValue, PreviousNode?.FlowData, out bool isChange); - - - if (mdEd.DataType.IsEnum) - { - var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue); - parameters[i] = enumValue; - } - else if (mdEd.ExplicitType == typeof(string)) - { - parameters[i] = Convert.ChangeType(expResult, typeof(string)); - } - else if (mdEd.ExplicitType == typeof(bool)) - { - parameters[i] = Convert.ChangeType(expResult, typeof(bool)); - } - else if (mdEd.ExplicitType == typeof(int)) - { - parameters[i] = Convert.ChangeType(expResult, typeof(int)); - } - else if (mdEd.ExplicitType == typeof(double)) - { - parameters[i] = Convert.ChangeType(expResult, typeof(double)); - } - else - { - parameters[i] = expResult; - //parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType); - } - } - else - { - if (mdEd.DataType.IsEnum) - { - var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue); - parameters[i] = enumValue; - } - else if (mdEd.ExplicitType == typeof(string)) - { - parameters[i] = mdEd.DataValue; - } - else if (mdEd.ExplicitType == typeof(bool)) - { - parameters[i] = bool.Parse(mdEd.DataValue); - } - else if (mdEd.ExplicitType == typeof(int)) - { - parameters[i] = int.Parse(mdEd.DataValue); - } - else if (mdEd.ExplicitType == typeof(double)) - { - parameters[i] = double.Parse(mdEd.DataValue); - } - else - { - parameters[i] = ""; - - //parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType); - } - } - - + Type t when t == typeof(IDynamicContext) => context, // 上下文 + Type t when t == typeof(MethodDetails) => md, // 节点方法描述 + Type t when t == typeof(NodeModelBase) => this, // 节点实体类 + _ => null, + }; + continue; // 上一节点数据为空,提前跳过 } - else if (f1 != null && f2 != null) - { - if (f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName)) - { - parameters[i] = PreviousNode?.FlowData; + object? inputParameter; // + var ed = md.ExplicitDatas[i]; // 方法入参描述 - } + // 检测是否为表达式 + if (ed.IsExplicitData && ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase)) + { + inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, flowData, out _); // 执行表达式从上一节点获取对象 } else { - - - var tmpParameter = PreviousNode?.FlowData?.ToString(); - if (mdEd.DataType.IsEnum) - { - - var enumValue = Enum.Parse(mdEd.DataType, tmpParameter); - - parameters[i] = enumValue; - } - else if (mdEd.DataType == typeof(string)) - { - - parameters[i] = tmpParameter; - - } - else if (mdEd.DataType == typeof(bool)) - { - - parameters[i] = bool.Parse(tmpParameter); - - } - else if (mdEd.DataType == typeof(int)) - { - - parameters[i] = int.Parse(tmpParameter); - - } - else if (mdEd.DataType == typeof(double)) - { - - parameters[i] = double.Parse(tmpParameter); - - } - else - { - if (tmpParameter != null && mdEd.DataType != null) - { - - parameters[i] = ConvertValue(tmpParameter, mdEd.DataType); - - } - } + inputParameter = flowData; // 使用上一节点的对象 } + try + { + parameters[i] = ed.DataType switch + { + Type t when t == previousDataType => context, // 上下文 + Type t when t == typeof(IDynamicContext) => context, // 上下文 + Type t when t == typeof(MethodDetails) => md, // 节点方法描述 + Type t when t == typeof(NodeModelBase) => this, // 节点实体类 + Type t when t == typeof(Guid) => new Guid(inputParameter?.ToString()), + Type t when t == typeof(decimal) => decimal.Parse(inputParameter?.ToString()), + Type t when t == typeof(string) => inputParameter?.ToString(), + Type t when t == typeof(char) => char.Parse(inputParameter?.ToString()), + Type t when t == typeof(bool) => bool.Parse(inputParameter?.ToString()), + Type t when t == typeof(byte) => byte.Parse(inputParameter?.ToString()), + Type t when t == typeof(int) => int.Parse(inputParameter?.ToString()), + Type t when t == typeof(long) => long.Parse(inputParameter?.ToString()), + Type t when t == typeof(DateTime) => DateTime.Parse(inputParameter?.ToString()), + Type t when t == typeof(float) => float.Parse(inputParameter?.ToString()), + Type t when t == typeof(double) => double.Parse(inputParameter?.ToString()), + Type t when t.IsEnum => Enum.Parse(ed.DataType, ed.DataValue),// 需要枚举 + Type t when t.IsArray => (inputParameter as Array)?.Cast().ToList(), + Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter, + Type t when Nullable.GetUnderlyingType(t) != null => inputParameter == null ? null : Convert.ChangeType(inputParameter, Nullable.GetUnderlyingType(t)), + _ => inputParameter, + }; + } + catch (Exception ex) // 节点参数类型转换异常 + { + parameters[i] = null; + Console.WriteLine(ex); + } } return parameters; } @@ -431,92 +357,5 @@ namespace Serein.NodeFlow.Base return value; } } - - - - - - #region 完整的ExecuteAsync调用方法(不要删除) - //public virtual async Task ExecuteAsync(DynamicContext context) - //{ - // MethodDetails md = MethodDetails; - // object? result = null; - // if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del)) - // { - // if (md.ExplicitDatas.Length == 0) - // { - // if (md.ReturnType == typeof(void)) - // { - // ((Action)del).Invoke(md.ActingInstance); - // } - // else if (md.ReturnType == typeof(Task)) - // { - // // 调用委托并获取结果 - // FlipflopContext flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance); - - // if (flipflopContext != null) - // { - // if (flipflopContext.State == FfState.Cancel) - // { - // throw new Exception("this async task is cancel."); - // } - // else - // { - // if (flipflopContext.State == FfState.Succeed) - // { - // CurrentState = true; - // result = flipflopContext.Data; - // } - // else - // { - // CurrentState = false; - // } - // } - // } - // } - // else - // { - // result = ((Func)del).Invoke(md.ActingInstance); - // } - // } - // else - // { - // object?[]? parameters = GetParameters(context, MethodDetails); - // if (md.ReturnType == typeof(void)) - // { - // ((Action)del).Invoke(md.ActingInstance, parameters); - // } - // else if (md.ReturnType == typeof(Task)) - // { - // // 调用委托并获取结果 - // FlipflopContext flipflopContext = await ((Func>)del).Invoke(MethodDetails.ActingInstance, parameters); - - // if (flipflopContext != null) - // { - // if (flipflopContext.State == FfState.Cancel) - // { - // throw new Exception("取消此异步"); - // } - // else - // { - // CurrentState = flipflopContext.State == FfState.Succeed; - // result = flipflopContext.Data; - // } - // } - // } - // else - // { - // result = ((Func)del).Invoke(md.ActingInstance, parameters); - // } - // } - // context.SetFlowData(result); - // } - // return result; - //} - #endregion - - - - } } diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs index 5b96a10..fa0d86d 100644 --- a/NodeFlow/FlowStarter.cs +++ b/NodeFlow/FlowStarter.cs @@ -6,6 +6,7 @@ using Serein.Library.Utils; using Serein.Library.Web; using Serein.NodeFlow.Base; using Serein.NodeFlow.Model; +using System.ComponentModel.Design; namespace Serein.NodeFlow { @@ -107,10 +108,10 @@ namespace Serein.NodeFlow #region 初始化运行环境的Ioc容器 // 清除节点使用的对象 var thisRuningMds = new List(); - thisRuningMds.AddRange(runNodeMd); - thisRuningMds.AddRange(initMethods); - thisRuningMds.AddRange(loadingMethods); - thisRuningMds.AddRange(exitMethods); + thisRuningMds.AddRange(runNodeMd.Where(md => md is not null)); + thisRuningMds.AddRange(initMethods.Where(md => md is not null)); + thisRuningMds.AddRange(loadingMethods.Where(md => md is not null)); + thisRuningMds.AddRange(exitMethods.Where(md => md is not null)); // .AddRange(initMethods).AddRange(loadingMethods).a foreach (var nodeMd in thisRuningMds) @@ -216,7 +217,7 @@ namespace Serein.NodeFlow }).ToArray(); _ = Task.WhenAll(tasks); } - await startNode.StartExecution(Context); + await startNode.StartExecution(Context); // 从起始节点开始运行 // 等待结束 if (FlipFlopCts != null) { @@ -242,63 +243,73 @@ namespace Serein.NodeFlow await FlipflopExecute(singleFlipFlopNode, flowEnvironment); // 启动触发器 }); } - + /// - /// 启动触发器 + /// 启动全局触发器 /// private async Task FlipflopExecute(SingleFlipflopNode singleFlipFlopNode, IFlowEnvironment flowEnvironment) { - DynamicContext context = new DynamicContext(SereinIOC, flowEnvironment); + var context = new DynamicContext(SereinIOC, flowEnvironment); MethodDetails md = singleFlipFlopNode.MethodDetails; var del = md.MethodDelegate; + + // 设置方法执行的对象 + if (md?.ActingInstance == null && md?.ActingInstanceType is not null) + { + md.ActingInstance ??= context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType); + } + // 设置委托对象 + var func = md.ExplicitDatas.Length == 0 ? + (Func>)del : + (Func>)del; try { - //var func = md.ExplicitDatas.Length == 0 ? (Func>>)del : (Func>>)del; - var func = md.ExplicitDatas.Length == 0 ? (Func>)del : (Func>)del; - - while (!FlipFlopCts.IsCancellationRequested) // 循环中直到栈为空才会退出 + while (!FlipFlopCts.IsCancellationRequested) { - if(singleFlipFlopNode.NotExitPreviousNode() == false) - { - // 存在上级节点时,退出触发器 - break; - } - object?[]? parameters = singleFlipFlopNode.GetParameters(context, md); - // 调用委托并获取结果 - md.ActingInstance = context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType); - - IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters); - - ConnectionType connection = flipflopContext.State.ToContentType(); - - - if (connection != ConnectionType.None) - { - singleFlipFlopNode.NextOrientation = connection; - singleFlipFlopNode.FlowData = flipflopContext.Data; - - var upstreamNodeTasks = singleFlipFlopNode.SuccessorNodes[ConnectionType.Upstream].Select(nextNode => - { - var context = new DynamicContext(SereinIOC, flowEnvironment); - nextNode.PreviousNode = singleFlipFlopNode; - return nextNode.StartExecution(context); - }).ToArray(); - - var tmpTasks = singleFlipFlopNode.SuccessorNodes[connection].Select(nextNode => - { - var context = new DynamicContext(SereinIOC,flowEnvironment); - nextNode.PreviousNode = singleFlipFlopNode; - return nextNode.StartExecution(context); - }).ToArray(); - Task[] tasks = [..upstreamNodeTasks, .. tmpTasks]; - Task.WaitAll(tasks); - } - else - { - break; - } + object?[]? parameters = singleFlipFlopNode.GetParameters(context, singleFlipFlopNode.MethodDetails); // 启动全局触发器时获取入参参数 + IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters);// 首先开始等待触发器 + _ = GlobalFlipflopExecute(singleFlipFlopNode, context); } + + + //while (!FlipFlopCts.IsCancellationRequested) + //{ + // if (singleFlipFlopNode.NotExitPreviousNode() == false) + // { + // break; + // } + + // object?[]? parameters = singleFlipFlopNode.GetParameters(context, md); + // if (md.ActingInstance == null) + // { + // md.ActingInstance = context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType); + // } + + // IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters); + // ConnectionType connection = flipflopContext.State.ToContentType(); + + // if (connection != ConnectionType.None) + // { + // singleFlipFlopNode.NextOrientation = connection; + // singleFlipFlopNode.FlowData = flipflopContext.Data; + + // var tasks = singleFlipFlopNode.SuccessorNodes.Values + // .SelectMany(nodeList => nodeList) + // .Select(nextNode => + // { + // var nextContext = new DynamicContext(SereinIOC, flowEnvironment); + // nextNode.PreviousNode = singleFlipFlopNode; + // return nextNode.StartExecution(nextContext); // 全局触发器收到信号,开始执行 + // }).ToArray(); + + // await Task.WhenAll(tasks); + // } + // else + // { + // break; + // } + //} } catch (Exception ex) { @@ -306,6 +317,73 @@ namespace Serein.NodeFlow } } + public async Task GlobalFlipflopExecute(SingleFlipflopNode singleFlipFlopNode, IDynamicContext context) + { + if (FlipFlopCts.IsCancellationRequested) + { + return; + } + bool skip = true; + var cts = context.SereinIoc.GetOrRegisterInstantiate(); + Stack stack = new Stack(); + stack.Push(singleFlipFlopNode); + + ConnectionType connectionType = ConnectionType.IsSucceed; + + while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环 + { + // 从栈中弹出一个节点作为当前节点进行处理 + var currentNode = stack.Pop(); + + // 设置方法执行的对象 + if (currentNode.MethodDetails?.ActingInstance == null && currentNode.MethodDetails?.ActingInstanceType is not null) + { + currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrRegisterInstantiate(currentNode.MethodDetails.ActingInstanceType); + } + + // 首先执行上游分支 + var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream]; + for (int i = upstreamNodes.Count - 1; i >= 0; i--) + { + upstreamNodes[i].PreviousNode = currentNode; + await upstreamNodes[i].StartExecution(context); // 执行上游分支 + } + + // 当前节点是已经触发了的全局触发器,所以跳过,难道每次都要判断一次? + if (skip) + { + skip = false; + } + else + { + // 判断是否为触发器节点,如果是,则开始等待。 + if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == NodeType.Flipflop) + { + currentNode.FlowData = await currentNode.ExecuteAsync(context); // 流程中遇到了触发器 + } + else + { + currentNode.FlowData = currentNode.Execute(context); // 流程中正常执行 + } + if (currentNode.NextOrientation == ConnectionType.None) + { + break; // 不再执行 + } + connectionType = currentNode.NextOrientation; + } + + // 获取下一分支 + var nextNodes = currentNode.SuccessorNodes[connectionType]; + + // 将下一个节点集合中的所有节点逆序推入栈中 + for (int i = nextNodes.Count - 1; i >= 0; i--) + { + nextNodes[i].PreviousNode = currentNode; + stack.Push(nextNodes[i]); + } + } + } + public void Exit() { diff --git a/NodeFlow/Model/CompositeActionNode.cs b/NodeFlow/Model/CompositeActionNode.cs index 9bac9fa..e422a44 100644 --- a/NodeFlow/Model/CompositeActionNode.cs +++ b/NodeFlow/Model/CompositeActionNode.cs @@ -1,4 +1,5 @@ -using Serein.Library.Entity; +using Serein.Library.Api; +using Serein.Library.Entity; using Serein.Library.Enums; using Serein.NodeFlow.Base; @@ -19,7 +20,10 @@ namespace Serein.NodeFlow.Model ActionNodes = actionNodes; } - + public override object? Execute(IDynamicContext context) + { + throw new NotImplementedException("动作区域暂未实现"); + } internal override Parameterdata[] GetParameterdatas() { diff --git a/NodeFlow/Model/CompositeConditionNode.cs b/NodeFlow/Model/CompositeConditionNode.cs index c18e1d1..ee9ba04 100644 --- a/NodeFlow/Model/CompositeConditionNode.cs +++ b/NodeFlow/Model/CompositeConditionNode.cs @@ -26,8 +26,6 @@ namespace Serein.NodeFlow.Model /// public override object? Execute(IDynamicContext context) { - // NextOrientation = ConnectionType.IsSucceed; - // 条件区域中遍历每个条件节点 foreach (SingleConditionNode? node in ConditionNodes) { @@ -39,9 +37,7 @@ namespace Serein.NodeFlow.Model break; } } - return PreviousNode?.FlowData; - } diff --git a/NodeFlow/Model/SingleActionNode.cs b/NodeFlow/Model/SingleActionNode.cs index c9ff4e1..b4b5323 100644 --- a/NodeFlow/Model/SingleActionNode.cs +++ b/NodeFlow/Model/SingleActionNode.cs @@ -1,4 +1,5 @@ -using Serein.Library.Entity; +using Serein.Library.Api; +using Serein.Library.Entity; using Serein.NodeFlow.Base; namespace Serein.NodeFlow.Model @@ -8,62 +9,7 @@ namespace Serein.NodeFlow.Model /// public class SingleActionNode : NodeModelBase { - //public override void Execute(DynamicContext context) - //{ - // try - // { - // Execute(context, base.MethodDetails); - // CurrentState = true; - // } - // catch (Exception ex) - // { - // Debug.Write(ex.Message); - // CurrentState = false; - // } - //} - //public void Execute(DynamicContext context, MethodDetails md) - //{ - // if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del)) - // { - - // object? result = null; - - // if (md.ExplicitDatas.Length == 0) - // { - // if (md.ReturnType == typeof(void)) - // { - // ((Action)del).Invoke(md.ActingInstance); - // } - // else - // { - // result = ((Func)del).Invoke(md.ActingInstance); - // } - // } - // else - // { - // object?[]? parameters = GetParameters(context, MethodDetails); - // if (md.ReturnType == typeof(void)) - // { - // ((Action)del).Invoke(md.ActingInstance, parameters); - // } - // else - // { - // result = ((Func)del).Invoke(md.ActingInstance, parameters); - // } - // } - - // // 根据 ExplicitDatas.Length 判断委托类型 - // //var action = (Action)del; - - // // 调用委托并获取结果 - // // action.Invoke(MethodDetails.ActingInstance, parameters); - - // //parameters = [md.ActingInstance, "", 123, ""]; - - // context.SetFlowData(result); - // } - //} internal override Parameterdata[] GetParameterdatas() { if (base.MethodDetails.ExplicitDatas.Length > 0) diff --git a/NodeFlow/Tool/MethodDetailsHelper.cs b/NodeFlow/Tool/MethodDetailsHelper.cs index ab695e7..3f49b0c 100644 --- a/NodeFlow/Tool/MethodDetailsHelper.cs +++ b/NodeFlow/Tool/MethodDetailsHelper.cs @@ -114,8 +114,6 @@ public static class MethodDetailsHelperTmp { IsExplicitData = it.HasDefaultValue, Index = index, - ExplicitType = it.ParameterType, - ExplicitTypeName = explicitTypeName, DataType = it.ParameterType, ParameterName = it.Name, DataValue = it.HasDefaultValue ? it.DefaultValue.ToString() : "", diff --git a/WorkBench/MainWindow.xaml.cs b/WorkBench/MainWindow.xaml.cs index 2c74b18..c249a58 100644 --- a/WorkBench/MainWindow.xaml.cs +++ b/WorkBench/MainWindow.xaml.cs @@ -7,6 +7,7 @@ using Serein.Library.Utils; using Serein.NodeFlow; using Serein.NodeFlow.Base; using Serein.NodeFlow.Model; +using Serein.WorkBench.Node; using Serein.WorkBench.Node.View; using Serein.WorkBench.Node.ViewModel; using Serein.WorkBench.Themes; @@ -66,27 +67,35 @@ namespace Serein.WorkBench /// private List Connections { get; } = []; - - #region 与画布相关的字段 + + /// + /// 标记是否正在尝试选取控件 + /// + private bool IsSelectControl; + /// + /// 标记是否正在进行连接操作 + /// + private bool IsConnecting; + /// + /// 标记是否正在拖动控件 + /// + private bool IsControlDragging; + /// + /// 标记是否正在拖动画布 + /// + private bool IsCanvasDragging; + /// /// 当前选取的控件 /// - private readonly List selectControls = []; - - /// - /// 拖动创建节点控件时的鼠标位置 - /// - // private Point canvasDropPosition; + private readonly List selectNodeControls = []; /// /// 记录拖动开始时的鼠标位置 /// private Point startPoint; - /// - /// 流程图起点的控件 - /// - // private NodeControlBase? flowStartBlock; + /// /// 记录开始连接的文本块 /// @@ -99,22 +108,8 @@ namespace Serein.WorkBench /// 当前正在绘制的真假分支属性 /// private ConnectionType currentConnectionType; - /// - /// 标记是否正在进行连接操作 - /// - private bool IsConnecting; - /// - /// 标记是否正在尝试选取控件 - /// - private bool IsSelectControl; - /// - /// 标记是否正在拖动控件 - /// - private bool IsControlDragging; - /// - /// 标记是否正在拖动画布 - /// - private bool IsCanvasDragging; + + /// /// 组合变换容器 /// @@ -165,7 +160,6 @@ namespace Serein.WorkBench } - private void InitUI() { canvasTransformGroup = new TransformGroup(); @@ -176,8 +170,9 @@ namespace Serein.WorkBench canvasTransformGroup.Children.Add(translateTransform); FlowChartCanvas.RenderTransform = canvasTransformGroup; - FlowChartCanvas.RenderTransformOrigin = new Point(0.5, 0.5); + //FlowChartCanvas.RenderTransformOrigin = new Point(0.5, 0.5); } + #region Main窗体加载方法 private void Window_Loaded(object sender, RoutedEventArgs e) { @@ -210,7 +205,6 @@ namespace Serein.WorkBench Console.WriteLine((FlowChartStackPanel.ActualWidth, FlowChartStackPanel.ActualHeight)); } - /// /// 运行完成 /// @@ -345,13 +339,25 @@ namespace Serein.WorkBench /// private void FlowEnvironment_NodeRemoteEvent(NodeRemoteEventArgs eventArgs) { + var nodeGuid = eventArgs.NodeGuid; + if (!NodeControls.TryGetValue(nodeGuid, out NodeControlBase nodeControl)) + { + return; + } + if(nodeControl is null) + { + return; + } + if (selectNodeControls.Count > 0) + { + if (selectNodeControls.Contains(nodeControl)) + { + selectNodeControls.Remove(nodeControl); + } + } this.Dispatcher.Invoke(() => { - var nodeGuid = eventArgs.NodeGuid; - if (!NodeControls.TryGetValue(nodeGuid, out var nodeControl)) - { - return; - } + FlowChartCanvas.Children.Remove(nodeControl); NodeControls.Remove(nodeControl.ViewModel.Node.Guid); }); @@ -643,10 +649,6 @@ namespace Serein.WorkBench } - - #endregion - - #region 右键菜单事件 /// /// 开始创建连接 True线 操作,设置起始块和绘制连接线。 /// @@ -676,6 +678,10 @@ namespace Serein.WorkBench this.KeyDown += MainWindow_KeyDown; } + #endregion + + #region 右键菜单事件 + /// /// 配置连接曲线的右键菜单 /// @@ -683,7 +689,6 @@ namespace Serein.WorkBench private void ConfigureLineContextMenu(Connection connection) { var contextMenu = new ContextMenu(); - contextMenu.Items.Add(CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection))); connection.ArrowPath.ContextMenu = contextMenu; connection.BezierPath.ContextMenu = contextMenu; @@ -705,7 +710,6 @@ namespace Serein.WorkBench FlowEnvironment.RemoteConnect(fromNodeGuid, toNodeGuid, connection.Type); } - /// /// 查看返回类型(树形结构展开类型的成员) /// @@ -762,9 +766,67 @@ namespace Serein.WorkBench #endregion - #region 与流程图相关的拖拽操作 + /// + /// 鼠标在画布移动。 + /// 选择控件状态下,调整选择框大小 + /// 连接状态下,实时更新连接线的终点位置。 + /// 移动画布状态下,移动画布。 + /// + private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e) + { + if (IsSelectControl && e.LeftButton == MouseButtonState.Pressed) // 正在选取节点 + { + // 获取当前鼠标位置 + Point currentPoint = e.GetPosition(FlowChartCanvas); + + // 更新选取矩形的位置和大小 + double x = Math.Min(currentPoint.X, startPoint.X); + double y = Math.Min(currentPoint.Y, startPoint.Y); + double width = Math.Abs(currentPoint.X - startPoint.X); + double height = Math.Abs(currentPoint.Y - startPoint.Y); + + Canvas.SetLeft(SelectionRectangle, x); + Canvas.SetTop(SelectionRectangle, y); + SelectionRectangle.Width = width; + SelectionRectangle.Height = height; + } + + + if (IsConnecting) // 正在连接节点 + { + Point position = e.GetPosition(FlowChartCanvas); + if (currentLine == null || startConnectNodeControl == null) + { + return; + } + currentLine.X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2; + currentLine.Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2; + currentLine.X2 = position.X; + currentLine.Y2 = position.Y; + } + if (IsCanvasDragging) // 正在移动画布 + { + Point currentMousePosition = e.GetPosition(this); + double deltaX = currentMousePosition.X - startPoint.X; + double deltaY = currentMousePosition.Y - startPoint.Y; + + translateTransform.X += deltaX; + translateTransform.Y += deltaY; + + startPoint = currentMousePosition; + + foreach (var line in Connections) + { + line.Refresh(); + } + + e.Handled = true; // 防止事件传播影响其他控件 + } + + } + /// /// 基础节点的拖拽放置创建 /// @@ -886,7 +948,6 @@ namespace Serein.WorkBench } } - /// /// 拖动效果,根据拖放数据是否为指定类型设置拖放效果 /// @@ -1093,139 +1154,7 @@ namespace Serein.WorkBench } #endregion - #region 画布中框选节点控件动作 - - /// - /// 在画布中尝试选取控件 - /// - /// - /// - private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) - { - if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) - { - IsSelectControl = true; - - // 开始选取时,记录鼠标起始点 - startPoint = e.GetPosition(FlowChartCanvas); - - // 初始化选取矩形的位置和大小 - Canvas.SetLeft(SelectionRectangle, startPoint.X); - Canvas.SetTop(SelectionRectangle, startPoint.Y); - SelectionRectangle.Width = 0; - SelectionRectangle.Height = 0; - - // 显示选取矩形 - SelectionRectangle.Visibility = Visibility.Visible; - - // 捕获鼠标,以便在鼠标移动到Canvas外部时仍能处理事件 - FlowChartCanvas.CaptureMouse(); - } - } - - /// - /// 在画布中释放鼠标按下,结束选取状态 - /// - /// - /// - private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) - { - if (IsSelectControl) - { - - IsSelectControl = false; - // 释放鼠标捕获 - FlowChartCanvas.ReleaseMouseCapture(); - - // 隐藏选取矩形(如果需要保持选取状态,可以删除此行) - SelectionRectangle.Visibility = Visibility.Collapsed; - - // 处理选取区域内的元素(例如,获取选取范围内的控件) - Rect selectionArea = new Rect(Canvas.GetLeft(SelectionRectangle), - Canvas.GetTop(SelectionRectangle), - SelectionRectangle.Width, - SelectionRectangle.Height); - - - selectControls.Clear(); - // 在此处处理选取的逻辑 - foreach (UIElement element in FlowChartCanvas.Children) - { - Rect elementBounds = new Rect(Canvas.GetLeft(element), Canvas.GetTop(element), - element.RenderSize.Width, element.RenderSize.Height); - - if (selectionArea.Contains(elementBounds)) - { - // 选中元素,执行相应操作 - if (element is NodeControlBase control) - { - selectControls.Add(control); - } - } - } - Console.WriteLine($"一共选取了{selectControls.Count}个控件"); - } - } - - /// - /// 鼠标在画布移动。 - /// 选择控件状态下,调整选择框大小 - /// 连接状态下,实时更新连接线的终点位置。 - /// 移动画布状态下,移动画布。 - /// - private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e) - { - if (IsSelectControl && e.LeftButton == MouseButtonState.Pressed) // 正在选取节点 - { - // 获取当前鼠标位置 - Point currentPoint = e.GetPosition(FlowChartCanvas); - - // 更新选取矩形的位置和大小 - double x = Math.Min(currentPoint.X, startPoint.X); - double y = Math.Min(currentPoint.Y, startPoint.Y); - double width = Math.Abs(currentPoint.X - startPoint.X); - double height = Math.Abs(currentPoint.Y - startPoint.Y); - - Canvas.SetLeft(SelectionRectangle, x); - Canvas.SetTop(SelectionRectangle, y); - SelectionRectangle.Width = width; - SelectionRectangle.Height = height; - } - - - if (IsConnecting) // 正在连接节点 - { - Point position = e.GetPosition(FlowChartCanvas); - if (currentLine == null || startConnectNodeControl == null) - { - return; - } - currentLine.X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2; - currentLine.Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2; - currentLine.X2 = position.X; - currentLine.Y2 = position.Y; - } - if (IsCanvasDragging) // 正在移动画布 - { - Point currentMousePosition = e.GetPosition(this); - double deltaX = currentMousePosition.X - startPoint.X; - double deltaY = currentMousePosition.Y - startPoint.Y; - - translateTransform.X += deltaX; - translateTransform.Y += deltaY; - - startPoint = currentMousePosition; - - foreach (var line in Connections) - { - line.Refresh(); - } - - e.Handled = true; // 防止事件传播影响其他控件 - } - - } - #endregion + #region 拖动画布实现缩放平移效果 private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e) @@ -1251,25 +1180,32 @@ namespace Serein.WorkBench // 单纯缩放画布,不改变画布大小 private void FlowChartCanvas_MouseWheel(object sender, MouseWheelEventArgs e) { - //var w = (int)(FlowChartCanvas.Width * scaleTransform.ScaleX); - //var h = (int)(FlowChartCanvas.Height * scaleTransform.ScaleY); - - //var TMP1 = w / FlowChartStackPanel.ActualWidth < 0.9; - //var TMP2 = h / FlowChartStackPanel.ActualHeight < 0.9; - - //Console.WriteLine("w"+(w, FlowChartStackPanel.ActualWidth, TMP1)); - //Console.WriteLine("h"+(h, FlowChartStackPanel.ActualHeight, TMP2)); - - if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) { if (e.Delta < 0 && scaleTransform.ScaleX < 0.2) return; if (e.Delta > 0 && scaleTransform.ScaleY > 1.5) return; - double scale = e.Delta > 0 ? 0.1 : -0.1; + // 获取鼠标在 Canvas 内的相对位置 + var mousePosition = e.GetPosition(FlowChartCanvas); - scaleTransform.ScaleX += scale; - scaleTransform.ScaleY += scale; + // 缩放因子,根据滚轮方向调整 + double zoomFactor = e.Delta > 0 ? 0.1 : -0.1; + //double zoomFactor = e.Delta > 0 ? 1.1 : 0.9; + // 当前缩放比例 + double oldScale = scaleTransform.ScaleX; + // double newScale = oldScale * zoomFactor; + double newScale = oldScale + zoomFactor; + // 更新缩放比例 + scaleTransform.ScaleX = newScale; + scaleTransform.ScaleY = newScale; + + // 计算缩放前后鼠标相对于 Canvas 的位置差异 + // double offsetX = mousePosition.X - (mousePosition.X * zoomFactor); + // double offsetY = mousePosition.Y - (mousePosition.Y * zoomFactor); + + // 更新 TranslateTransform,确保以鼠标位置为中心进行缩放 + translateTransform.X -= (mousePosition.X * (newScale - oldScale)); + translateTransform.Y -= (mousePosition.Y * (newScale - oldScale)); } } @@ -1278,8 +1214,6 @@ namespace Serein.WorkBench { FlowChartCanvas.Width = width; FlowChartCanvas.Height = height; - //FlowChartStackPanel.Width = width; - //FlowChartStackPanel.Height = height; } @@ -1331,21 +1265,10 @@ namespace Serein.WorkBench double newWidth = Math.Max(FlowChartCanvas.ActualWidth + horizontalChange, 400); double newHeight = Math.Max(FlowChartCanvas.ActualHeight + verticalChange, 400); - // 更新 Canvas 大小 - FlowChartCanvas.Width = newWidth; - FlowChartCanvas.Height = newHeight; + newHeight = newHeight < 400 ? 400 : newHeight; + newWidth = newWidth < 400 ? 400 : newWidth; - // 如果宽度和高度超过400,调整TranslateTransform以保持左上角不动 - if (newWidth > 400 && newHeight > 400) - { - // 计算平移的变化,保持左上角不动 - double deltaX = -horizontalChange / 2; // 水平方向的平移 - double deltaY = -verticalChange / 2; // 垂直方向的平移 - - // 调整TranslateTransform以补偿尺寸变化 - translateTransform.X += deltaX; - translateTransform.Y += deltaY; - } + InitializeCanvas(newWidth, newHeight); //// 从右下角调整大小 //double newWidth = Math.Max(FlowChartCanvas.ActualWidth + e.HorizontalChange * scaleTransform.ScaleX, 0); @@ -1380,45 +1303,14 @@ namespace Serein.WorkBench private void Thumb_DragDelta_Right(object sender, DragDeltaEventArgs e) { //从右侧调整大小 - //double newWidth = Math.Max(FlowChartCanvas.ActualWidth + e.HorizontalChange * scaleTransform.ScaleX, 0); - //newWidth = newWidth < 400 ? 400 : newWidth; - //if (newWidth > 400) - //{ - // FlowChartCanvas.Width = newWidth; - - // double x = e.HorizontalChange > 0 ? -0.5 : 0.5; - // double y = 0; - - // double deltaX = x * scaleTransform.ScaleX; - // double deltaY = y * 0; - // Test(deltaX, deltaY); - //} - - - - // 获取缩放后的水平和垂直变化 + // 获取缩放后的水平变化 double horizontalChange = e.HorizontalChange * scaleTransform.ScaleX; - //double verticalChange = e.VerticalChange * scaleTransform.ScaleY; - // 计算新的宽度和高度,确保不会小于400 + // 计算新的宽度,确保不会小于400 double newWidth = Math.Max(FlowChartCanvas.ActualWidth + horizontalChange, 400); - //double newHeight = Math.Max(FlowChartCanvas.ActualHeight + verticalChange, 400); - - // 更新 Canvas 大小 - FlowChartCanvas.Width = newWidth; - //FlowChartCanvas.Height = newHeight; - - // 如果宽度和高度超过400,调整TranslateTransform以保持左上角不动 - if (newWidth > 400 /*&& newHeight > 400*/) - { - // 计算平移的变化,保持左上角不动 - double deltaX = -horizontalChange / 2; // 水平方向的平移 - //double deltaY = -verticalChange / 2; // 垂直方向的平移 - - // 调整TranslateTransform以补偿尺寸变化 - translateTransform.X += deltaX; - //translateTransform.Y += deltaY; - } + + newWidth = newWidth < 400 ? 400 : newWidth; + InitializeCanvas(newWidth, FlowChartCanvas.Height); } @@ -1433,58 +1325,20 @@ namespace Serein.WorkBench private void Thumb_DragDelta_Bottom(object sender, DragDeltaEventArgs e) { - //// 从底部调整大小 - //double oldHeight = FlowChartCanvas.Height; - - //double newHeight = Math.Max(FlowChartCanvas.ActualHeight + e.VerticalChange * scaleTransform.ScaleY, 0); - ////newHeight = newHeight < 400 ? 400 : newHeight; - //if(newHeight > 400) - //{ - // FlowChartCanvas.Height = newHeight; - - // double x = 0; - // double y = e.VerticalChange > 0 ? -0.5 : 0.5 ; - - // double deltaX = x * 0; - // double deltaY = y * (scaleTransform.ScaleY); - - // Test(deltaX, deltaY); - //} - - - // 获取缩放后的水平和垂直变化 - //double horizontalChange = e.HorizontalChange * scaleTransform.ScaleX; + // 获取缩放后的垂直变化 double verticalChange = e.VerticalChange * scaleTransform.ScaleY; - - // 计算新的宽度和高度,确保不会小于400 - //double newWidth = Math.Max(FlowChartCanvas.ActualWidth + horizontalChange, 400); + // 计算新的高度,确保不会小于400 double newHeight = Math.Max(FlowChartCanvas.ActualHeight + verticalChange, 400); - - // 更新 Canvas 大小 - //FlowChartCanvas.Width = newWidth; - FlowChartCanvas.Height = newHeight; - - // 如果宽度和高度超过400,调整TranslateTransform以保持左上角不动 - if (/*newWidth > 400 &&*/ newHeight > 400) - { - // 计算平移的变化,保持左上角不动 - //double deltaX = -horizontalChange / 2; // 水平方向的平移 - double deltaY = -verticalChange / 2; // 垂直方向的平移 - - // 调整TranslateTransform以补偿尺寸变化 - //translateTransform.X += deltaX; - translateTransform.Y += deltaY; - } - - + newHeight = newHeight < 400 ? 400 : newHeight; + InitializeCanvas(FlowChartCanvas.Width, newHeight); } private void Test(double deltaX, double deltaY) { - translateTransform.X += deltaX; - translateTransform.Y += deltaY; //Console.WriteLine((translateTransform.X, translateTransform.Y)); + //translateTransform.X += deltaX; + //translateTransform.Y += deltaY; } #endregion @@ -1492,6 +1346,131 @@ namespace Serein.WorkBench #endregion + #region 画布中框选节点控件动作 + + /// + /// 在画布中尝试选取控件 + /// + /// + /// + private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) + { + if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) + { + IsSelectControl = true; + + // 开始选取时,记录鼠标起始点 + startPoint = e.GetPosition(FlowChartCanvas); + + // 初始化选取矩形的位置和大小 + Canvas.SetLeft(SelectionRectangle, startPoint.X); + Canvas.SetTop(SelectionRectangle, startPoint.Y); + SelectionRectangle.Width = 0; + SelectionRectangle.Height = 0; + + // 显示选取矩形 + SelectionRectangle.Visibility = Visibility.Visible; + SelectionRectangle.ContextMenu ??= ConfiguerSelectionRectangle(); + + // 捕获鼠标,以便在鼠标移动到Canvas外部时仍能处理事件 + FlowChartCanvas.CaptureMouse(); + } + } + + private ContextMenu ConfiguerSelectionRectangle() + { + var contextMenu = new ContextMenu(); + contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => + { + if(selectNodeControls.Count > 0) + { + foreach(var node in selectNodeControls.ToArray()) + { + var guid = node?.ViewModel?.Node?.Guid; + if (!string.IsNullOrEmpty(guid)) + { + FlowEnvironment.RemoteNode(guid); + } + } + } + SelectionRectangle.Visibility = Visibility.Collapsed; + })); + return contextMenu; + // nodeControl.ContextMenu = contextMenu; + } + + /// + /// 在画布中释放鼠标按下,结束选取状态 + /// + /// + /// + private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e) + { + if (IsSelectControl) + { + CancelSelectNode(); // 取消之前选择的控件 + IsSelectControl = false; + // 释放鼠标捕获 + FlowChartCanvas.ReleaseMouseCapture(); + + // 隐藏选取矩形(如果需要保持选取状态显示,可以删除此行) + // SelectionRectangle.Visibility = Visibility.Collapsed; + + // 处理选取区域内的元素(例如,获取选取范围内的控件) + Rect selectionArea = new Rect(Canvas.GetLeft(SelectionRectangle), + Canvas.GetTop(SelectionRectangle), + SelectionRectangle.Width, + SelectionRectangle.Height); + + + + // 在此处处理选取的逻辑 + foreach (UIElement element in FlowChartCanvas.Children) + { + Rect elementBounds = new Rect(Canvas.GetLeft(element), Canvas.GetTop(element), + element.RenderSize.Width, element.RenderSize.Height); + + if (selectionArea.Contains(elementBounds)) + { + // 选中元素,执行相应操作 + if (element is NodeControlBase control) + { + selectNodeControls.Add(control); + } + } + } + SelectedNode();// 选择之后需要执行的操作 + } + } + + private void SelectedNode() + { + if(selectNodeControls.Count == 0) + { + Console.WriteLine($"没有选择控件"); + return; + } + Console.WriteLine($"一共选取了{selectNodeControls.Count}个控件"); + foreach (var node in selectNodeControls) + { + node.ViewModel.Selected(); + node.ViewModel.CancelSelect(); + } + } + private void CancelSelectNode() + { + foreach (var node in selectNodeControls) + { + node.ViewModel.CancelSelect(); + } + selectNodeControls.Clear(); + } + #endregion + + + + + /// /// 卸载DLL文件,清空当前项目 diff --git a/WorkBench/Node/NodeControlViewModelBase.cs b/WorkBench/Node/NodeControlViewModelBase.cs new file mode 100644 index 0000000..5976375 --- /dev/null +++ b/WorkBench/Node/NodeControlViewModelBase.cs @@ -0,0 +1,73 @@ +using Serein.Library.Entity; +using Serein.NodeFlow.Base; +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Text; +using System.Threading.Tasks; + +namespace Serein.WorkBench.Node.ViewModel +{ + public abstract class NodeControlViewModelBase : INotifyPropertyChanged + { + public NodeControlViewModelBase(NodeModelBase node) + { + Node = node; + MethodDetails = Node.MethodDetails; + } + + /// + /// 对应的节点实体类 + /// + internal NodeModelBase Node { get; } + + private bool isSelect; + /// + /// 表示节点控件是否被选中 + /// + internal bool IsSelect + { + get => isSelect; + set + { + isSelect = value; + // OnPropertyChanged(); + } + } + + private MethodDetails methodDetails; + + + public MethodDetails MethodDetails + { + get => methodDetails; + set + { + methodDetails = value; + OnPropertyChanged(); + } + } + + public event PropertyChangedEventHandler PropertyChanged; + protected void OnPropertyChanged([CallerMemberName] string propertyName = null) + + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + + /// + /// + /// + public void Selected() + { + IsSelect = true; + } + + public void CancelSelect() + { + IsSelect = false; + } + } +} diff --git a/WorkBench/Node/View/NodeControlBase.cs b/WorkBench/Node/View/NodeControlBase.cs index 5a30a67..aff2a55 100644 --- a/WorkBench/Node/View/NodeControlBase.cs +++ b/WorkBench/Node/View/NodeControlBase.cs @@ -1,6 +1,7 @@ using Serein.Library.Api; using Serein.Library.Entity; using Serein.NodeFlow.Base; +using Serein.WorkBench.Node.ViewModel; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.ComponentModel; @@ -33,44 +34,7 @@ namespace Serein.WorkBench.Node.View - public abstract class NodeControlViewModelBase : INotifyPropertyChanged - { - public NodeControlViewModelBase(NodeModelBase node) - { - this.Node = node; - MethodDetails = this.Node.MethodDetails; - } - - /// - /// 对应的节点实体类 - /// - public NodeModelBase Node { get; } - - /// - /// 表示节点控件是否被选中 - /// - public bool IsSelect { get; set; } = false; - - private MethodDetails methodDetails; - - - public MethodDetails MethodDetails - { - get => methodDetails; - set - { - methodDetails = value; - OnPropertyChanged(); - } - } - - public event PropertyChangedEventHandler PropertyChanged; - protected void OnPropertyChanged([CallerMemberName] string propertyName = null) - - { - PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); - } - } +