From c2d7ef408345536b746fb89d70ed9a478970c6d1 Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Tue, 17 Sep 2024 14:20:27 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=97=A0=E6=B3=95?=
=?UTF-8?q?=E4=BF=9D=E5=AD=98=E9=A1=B9=E7=9B=AE=E6=96=87=E4=BB=B6=E7=9A=84?=
=?UTF-8?q?bug?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
Library.Core/Http/Router.cs | 11 +-
Library.Core/NodeFlow/DynamicContext.cs | 2 +-
Library.Framework/NodeFlow/DynamicContext.cs | 2 +-
.../Serein.Library.Framework.csproj | 2 +-
Library/Api/IFlowEnvironment.cs | 63 +-
Library/Api/ISereinIoc.cs | 4 +-
Library/Entity/MethodDetails.cs | 2 +-
Library/Entity/SereinOutputFileData.cs | 27 +-
Library/Http/Attribute.cs | 116 +++
Library/Http/ControllerBase.cs | 19 +
Library/Http/Router.cs | 768 ++++++++++++++++++
Library/Http/WebAPIAttribute.cs | 190 +++++
Library/Serein.Library.csproj | 6 +-
Library/Utils/SereinIoc.cs | 157 ++--
Library/Web/Attribute.cs | 86 ++
Library/Web/ControllerBase.cs | 19 +
Library/Web/Router.cs | 650 +++++++++++++++
Library/Web/WebAPIAttribute.cs | 154 ++++
NodeFlow/Base/NodeModelBaseFunc.cs | 48 +-
NodeFlow/FlowEnvironment.cs | 318 ++++++--
NodeFlow/FlowStarter.cs | 87 +-
NodeFlow/Model/CompositeActionNode.cs | 12 +-
NodeFlow/Model/CompositeConditionNode.cs | 12 +-
NodeFlow/Model/SingleActionNode.cs | 6 +-
NodeFlow/Model/SingleConditionNode.cs | 54 +-
NodeFlow/Model/SingleExpOpNode.cs | 30 +-
NodeFlow/Model/SingleFlipflopNode.cs | 6 +-
NodeFlow/NodeStaticConfig.cs | 16 +
.../Resolver/MemberConditionResolver.cs | 2 +
.../Resolver/MemberStringConditionResolver.cs | 11 +-
.../SereinExpression/SereinConditionParser.cs | 18 +-
WorkBench/App.xaml.cs | 8 +-
WorkBench/LogWindow.xaml | 1 +
WorkBench/MainWindow.xaml | 3 +-
WorkBench/MainWindow.xaml.cs | 606 +++++++++-----
.../ConditionNodeControlViewModel.cs | 6 +-
36 files changed, 3005 insertions(+), 517 deletions(-)
create mode 100644 Library/Http/Attribute.cs
create mode 100644 Library/Http/ControllerBase.cs
create mode 100644 Library/Http/Router.cs
create mode 100644 Library/Http/WebAPIAttribute.cs
create mode 100644 Library/Web/Attribute.cs
create mode 100644 Library/Web/ControllerBase.cs
create mode 100644 Library/Web/Router.cs
create mode 100644 Library/Web/WebAPIAttribute.cs
create mode 100644 NodeFlow/NodeStaticConfig.cs
diff --git a/Library.Core/Http/Router.cs b/Library.Core/Http/Router.cs
index 61ad03b..1272568 100644
--- a/Library.Core/Http/Router.cs
+++ b/Library.Core/Http/Router.cs
@@ -14,7 +14,17 @@ using Type = System.Type;
namespace Serein.Library.Core.Http
{
+ /*
+ Router类负责解析请求的url,url参数,boby参数
+ 根据url
+ web server 监听类,监听外部的请求
+ router 选择对应的控制器
+ agent 负责传入对应的参数,注入依赖
+
+
+
+ */
///
@@ -22,7 +32,6 @@ namespace Serein.Library.Core.Http
///
public class Router
{
-
private readonly ConcurrentDictionary _controllerAutoHosting; // 存储是否实例化
private readonly ConcurrentDictionary _controllerTypes; // 存储控制器类型
private readonly ConcurrentDictionary _controllerInstances; // 存储控制器实例对象
diff --git a/Library.Core/NodeFlow/DynamicContext.cs b/Library.Core/NodeFlow/DynamicContext.cs
index 0a62ad4..be81db1 100644
--- a/Library.Core/NodeFlow/DynamicContext.cs
+++ b/Library.Core/NodeFlow/DynamicContext.cs
@@ -22,7 +22,7 @@ namespace Serein.Library.Core.NodeFlow
public Task CreateTimingTask(Action action, int time = 100, int count = -1)
{
- NodeRunCts ??= SereinIoc.GetOrInstantiate();
+ NodeRunCts ??= SereinIoc.GetOrRegisterInstantiate();
return Task.Factory.StartNew(async () =>
{
for (int i = 0; i < count; i++)
diff --git a/Library.Framework/NodeFlow/DynamicContext.cs b/Library.Framework/NodeFlow/DynamicContext.cs
index 4ae4329..deb4fae 100644
--- a/Library.Framework/NodeFlow/DynamicContext.cs
+++ b/Library.Framework/NodeFlow/DynamicContext.cs
@@ -25,7 +25,7 @@ namespace Serein.Library.Framework.NodeFlow
if(NodeRunCts == null)
{
- NodeRunCts = SereinIoc.GetOrInstantiate();
+ NodeRunCts = SereinIoc.GetOrRegisterInstantiate();
}
return Task.Factory.StartNew(async () =>
{
diff --git a/Library.Framework/Serein.Library.Framework.csproj b/Library.Framework/Serein.Library.Framework.csproj
index 3a690f9..6bbbe58 100644
--- a/Library.Framework/Serein.Library.Framework.csproj
+++ b/Library.Framework/Serein.Library.Framework.csproj
@@ -18,7 +18,7 @@
true
full
false
- bin\Debug\
+ ..\.Output\Debug\net8.0-windows7.0\
DEBUG;TRACE
prompt
4
diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs
index b0b603c..a15ed24 100644
--- a/Library/Api/IFlowEnvironment.cs
+++ b/Library/Api/IFlowEnvironment.cs
@@ -15,9 +15,9 @@ namespace Serein.Library.Api
public delegate void FlowRunCompleteHandler(FlowEventArgs eventArgs);
///
- /// 加载项目文件时成功加载了节点
+ /// 项目加载完成
///
- public delegate void LoadNodeHandler(LoadNodeEventArgs eventArgs);
+ public delegate void ProjectLoadedHandler(ProjectLoadedEventArgs eventArgs);
///
/// 加载项目文件时成功加载了DLL文件
@@ -63,23 +63,30 @@ namespace Serein.Library.Api
///
public string ErrorTips { get; protected set; } = string.Empty;
}
- public class LoadNodeEventArgs : FlowEventArgs
- {
- public LoadNodeEventArgs(NodeInfo NodeInfo, MethodDetails MethodDetailss)
- {
- this.NodeInfo = NodeInfo;
- this.MethodDetailss = MethodDetailss;
- }
- ///
- /// 项目文件节点信息参数
- ///
- public NodeInfo NodeInfo { get; protected set; }
- ///
- /// 已加载在环境中的方法描述
- ///
- public MethodDetails MethodDetailss { get; protected set; }
- }
+ //public class LoadNodeEventArgs : FlowEventArgs
+ //{
+ // public LoadNodeEventArgs(NodeInfo NodeInfo, MethodDetails MethodDetailss)
+ // {
+ // this.NodeInfo = NodeInfo;
+ // this.MethodDetailss = MethodDetailss;
+ // }
+ // ///
+ // /// 项目文件节点信息参数
+ // ///
+ // public NodeInfo NodeInfo { get; protected set; }
+ // ///
+ // /// 已加载在环境中的方法描述
+ // ///
+ // public MethodDetails MethodDetailss { get; protected set; }
+ //}
+
+ public class ProjectLoadedEventArgs : FlowEventArgs
+ {
+ public ProjectLoadedEventArgs()
+ {
+ }
+ }
public class LoadDLLEventArgs : FlowEventArgs
{
@@ -143,14 +150,25 @@ namespace Serein.Library.Api
public class NodeCreateEventArgs : FlowEventArgs
{
- public NodeCreateEventArgs(object nodeModel)
+ public NodeCreateEventArgs(object nodeModel, Position position)
{
this.NodeModel = nodeModel;
+ this.Position = position;
}
+ public NodeCreateEventArgs(object nodeModel, bool isAddInRegion, string regeionGuid)
+ {
+ this.NodeModel = nodeModel;
+ this.RegeionGuid = regeionGuid;
+ this.IsAddInRegion = isAddInRegion;
+ }
+
///
/// 节点Model对象,目前需要手动转换对应的类型
///
public object NodeModel { get; private set; }
+ public Position Position { get; private set; }
+ public bool IsAddInRegion { get; private set; }
+ public string RegeionGuid { get; private set; }
}
///
@@ -189,13 +207,14 @@ namespace Serein.Library.Api
/// 新的起始节点Guid
///
public string NewNodeGuid { get; private set; }
- }
+ }
#endregion
public interface IFlowEnvironment
{
+
event FlowRunCompleteHandler OnFlowRunComplete;
- event LoadNodeHandler OnLoadNode;
+ event ProjectLoadedHandler OnProjectLoaded;
event LoadDLLHandler OnDllLoad;
event NodeConnectChangeHandler OnNodeConnectChange;
event NodeCreateHandler OnNodeCreate;
@@ -256,7 +275,7 @@ namespace Serein.Library.Api
///
/// 节点/区域/基础控件
/// 节点绑定的方法说明(
- void CreateNode(NodeControlType nodeBase, MethodDetails methodDetails = null);
+ void CreateNode(NodeControlType nodeBase, Position position, MethodDetails methodDetails = null);
///
/// 移除两个节点之间的连接关系
///
diff --git a/Library/Api/ISereinIoc.cs b/Library/Api/ISereinIoc.cs
index 06ef569..a5be469 100644
--- a/Library/Api/ISereinIoc.cs
+++ b/Library/Api/ISereinIoc.cs
@@ -20,11 +20,11 @@ namespace Serein.Library.Api
///
/// 获取或创建并注入目标类型
///
- T GetOrInstantiate();
+ T GetOrRegisterInstantiate();
///
/// 获取或创建并注入目标类型
///
- object GetOrInstantiate(Type type);
+ object GetOrRegisterInstantiate(Type type);
///
/// 创建目标类型的对象, 并注入依赖项
diff --git a/Library/Entity/MethodDetails.cs b/Library/Entity/MethodDetails.cs
index 270db53..71c3d61 100644
--- a/Library/Entity/MethodDetails.cs
+++ b/Library/Entity/MethodDetails.cs
@@ -28,7 +28,7 @@ namespace Serein.Library.Entity
MethodName = MethodName,
MethodLockName = MethodLockName,
IsNetFramework = IsNetFramework,
- ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(),
+ ExplicitDatas = ExplicitDatas?.Select(it => it.Clone()).ToArray(),
};
}
diff --git a/Library/Entity/SereinOutputFileData.cs b/Library/Entity/SereinOutputFileData.cs
index 12e4421..264b9f5 100644
--- a/Library/Entity/SereinOutputFileData.cs
+++ b/Library/Entity/SereinOutputFileData.cs
@@ -1,7 +1,9 @@
using Serein.Library.Api;
using System;
using System.Collections.Generic;
+using System.Drawing;
using System.Linq;
+using System.Reflection;
using System.Text;
using System.Threading.Tasks;
@@ -54,13 +56,13 @@ namespace Serein.Library.Entity
/// 画布
///
- public FlowCanvas canvas { get; set; }
+ public FlowCanvas Canvas { get; set; }
///
/// 版本
///
- public string versions { get; set; }
+ public string Versions { get; set; }
// 预览位置
@@ -74,11 +76,11 @@ namespace Serein.Library.Entity
///
/// 宽度
///
- public float width { get; set; }
+ public float Width { get; set; }
///
/// 高度
///
- public float lenght { get; set; }
+ public float Lenght { get; set; }
}
///
@@ -156,7 +158,7 @@ namespace Serein.Library.Entity
///
/// 如果是区域控件,则会存在子项。
///
- public NodeInfo[] ChildNodes { get; set; }
+ public string[] ChildNodeGuids { get; set; }
///
@@ -173,9 +175,9 @@ namespace Serein.Library.Entity
public class Parameterdata
{
- public bool state { get; set; }
- public string value { get; set; }
- public string expression { get; set; }
+ public bool State { get; set; }
+ public string Value { get; set; }
+ public string Expression { get; set; }
}
@@ -185,8 +187,13 @@ namespace Serein.Library.Entity
///
public class Position
{
- public float X { get; set; }
- public float Y { get; set; }
+ public Position(double x, double y)
+ {
+ this.X = x; this.Y = y;
+ }
+
+ public double X { get; set; } = 0;
+ public double Y { get; set; } = 0;
}
diff --git a/Library/Http/Attribute.cs b/Library/Http/Attribute.cs
new file mode 100644
index 0000000..259bdfc
--- /dev/null
+++ b/Library/Http/Attribute.cs
@@ -0,0 +1,116 @@
+using System;
+
+namespace Serein.Library.Http
+{
+ ///
+ /// 表示参数为url中的数据(Get请求中不需要显式标注)
+ ///
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public sealed class IsUrlDataAttribute : Attribute
+ {
+
+ }
+
+ ///
+ /// 表示入参参数为整个boby的数据
+ ///
+ /// 例如:User类型含有int id、string name字段
+ ///
+ /// ① Add(User user)
+ /// 请求需要传入的json为
+ /// {"user":{
+ /// "id":2,
+ /// "name":"李志忠"}}
+ ///
+ /// ② Add([Boby]User user)
+ /// 请求需要传入的json为
+ /// {"id":2,"name":"李志忠"}
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public sealed class IsBobyDataAttribute : Attribute
+ {
+
+ }
+
+ ///
+ /// 表示该控制器会被自动注册(与程序集同一命名空间,暂时不支持运行时自动加载DLL,需要手动注册)
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ public sealed class AutoHostingAttribute(string url = "") : Attribute
+ {
+ public string Url { get; } = url;
+ }
+ ///
+ /// 表示该属性为自动注入依赖项
+ ///
+ [AttributeUsage(AttributeTargets.Property)]
+ public sealed class AutoInjectionAttribute : Attribute
+ {
+ }
+
+
+ ///
+ /// 方法的接口类型与附加URL
+ ///
+ ///
+ /// 假设UserController.Add()的WebAPI特性中
+ /// http是HTTP.POST
+ /// url被显示标明“temp”
+ /// 那么请求的接口是POST,URL是
+ /// [http://localhost:8080]/user/add/temp
+ ///
+ ///
+ ///
+ [AttributeUsage(AttributeTargets.Method)]
+
+ public sealed class WebApiAttribute() : Attribute
+
+ {
+ public API Type ;
+ public string Url ;
+ ///
+ /// 方法名称不作为url的部分
+ ///
+ public bool IsUrl;
+ }
+ [AttributeUsage(AttributeTargets.Method)]
+
+ public sealed class ApiPostAttribute() : Attribute
+
+ {
+ public string Url;
+ ///
+ /// 方法名称不作为url的部分
+ ///
+ public bool IsUrl = true;
+ }
+ [AttributeUsage(AttributeTargets.Method)]
+
+ public sealed class ApiGetAttribute() : Attribute
+
+ {
+ public string Url;
+ ///
+ /// 方法名称不作为url的部分
+ ///
+ public bool IsUrl = true;
+ }
+
+ /*public sealed class WebApiAttribute(API http, bool isUrl = true, string url = "") : Attribute
+ {
+ public API Http { get; } = http;
+ public string Url { get; } = url;
+ ///
+ /// 方法名称不作为url的部分
+ ///
+ public bool IsUrl { get; } = isUrl;
+ }*/
+ public enum API
+ {
+ POST,
+ GET,
+ //PUT,
+ //DELETE
+ }
+}
diff --git a/Library/Http/ControllerBase.cs b/Library/Http/ControllerBase.cs
new file mode 100644
index 0000000..251b100
--- /dev/null
+++ b/Library/Http/ControllerBase.cs
@@ -0,0 +1,19 @@
+using System;
+
+namespace Serein.Library.Http
+{
+ public class ControllerBase
+ {
+
+ public string Url { get; set; }
+
+ public string BobyData { get; set; }
+
+ public string GetLog(Exception ex)
+ {
+ return "Url : " + Url + Environment.NewLine +
+ "Ex : " + ex.Message + Environment.NewLine +
+ "Data : " + BobyData + Environment.NewLine;
+ }
+ }
+}
diff --git a/Library/Http/Router.cs b/Library/Http/Router.cs
new file mode 100644
index 0000000..0e7d352
--- /dev/null
+++ b/Library/Http/Router.cs
@@ -0,0 +1,768 @@
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+using Serein.Library.Api;
+using Serein.Library.Utils;
+using System;
+using System.Collections;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Net;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+using System.Web;
+using Enum = System.Enum;
+using Type = System.Type;
+
+namespace Serein.Library.Http
+{
+ /*
+ Router类负责解析请求的url,url参数,boby参数
+ 根据url
+
+ web server 监听类,监听外部的请求
+ router 选择对应的控制器
+ agent 负责传入对应的参数,注入依赖
+
+
+
+ */
+
+
+ ///
+ /// 路由注册与解析
+ ///
+ public class Router
+ {
+ private readonly ConcurrentDictionary _controllerAutoHosting; // 存储是否实例化
+ private readonly ConcurrentDictionary _controllerTypes; // 存储控制器类型
+ private readonly ConcurrentDictionary _controllerInstances; // 存储控制器实例对象
+ private readonly ConcurrentDictionary> _routes; // 用于存储路由信息
+
+ private readonly SereinIOC serviceRegistry; // 用于存储路由信息
+
+ //private Type PostRequest;
+
+ public Router(ISereinIOC serviceRegistry) // 构造函数,初始化 Router 类的新实例
+ {
+ this.serviceRegistry = serviceRegistry;
+
+ _routes = new ConcurrentDictionary>(); // 初始化路由字典
+
+ _controllerAutoHosting = new ConcurrentDictionary(); // 初始化控制器实例对象字典
+ _controllerTypes = new ConcurrentDictionary(); // 初始化控制器实例对象字典
+ _controllerInstances = new ConcurrentDictionary(); // 初始化控制器实例对象字典
+
+ foreach (API method in Enum.GetValues(typeof(API))) // 遍历 HTTP 枚举类型的所有值
+ {
+ _routes.TryAdd(method.ToString(), new ConcurrentDictionary()); // 初始化每种 HTTP 方法对应的路由字典
+ }
+
+ // 获取当前程序集
+ Assembly assembly = Assembly.GetExecutingAssembly();
+
+ // 获取包含“Controller”名称的类型
+ var controllerTypes = assembly.GetTypes()
+ .Where(t => t.Name.Contains("Controller"));
+
+ Type baseAttribute = typeof(AutoHostingAttribute);
+ Type baseController = typeof(ControllerBase);
+ foreach (var controllerType in controllerTypes)
+ {
+ if (controllerType.IsSubclassOf(baseController) && controllerType.IsDefined(baseAttribute))
+ {
+
+ // 如果属于控制器,并标记了AutoHosting特性,进行自动注册
+ AutoRegisterAutoController(controllerType);
+ }
+ else
+ {
+ continue;
+ }
+ }
+ }
+
+
+ ///
+ /// 自动注册 自动实例化控制器 类型
+ ///
+ ///
+ public void AutoRegisterAutoController(Type controllerType) // 方法声明,用于注册并实例化控制器类型
+ {
+ if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回
+
+ var autoHostingAttribute = controllerType.GetCustomAttribute();
+ if (autoHostingAttribute != null) {
+ foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
+ {
+ var apiGetAttribute = method.GetCustomAttribute();
+ var apiPostAttribute = method.GetCustomAttribute();
+ if( apiGetAttribute == null && apiPostAttribute == null )
+ {
+ continue;
+ }
+
+
+
+ WebApiAttribute webApiAttribute = new WebApiAttribute()
+ {
+ Type = apiGetAttribute != null ? API.GET : API.POST,
+ Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
+ IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
+ };
+
+
+
+ if (apiPostAttribute != null) // 如果存在 WebAPIAttribute 属性
+ {
+ var url = AddRoutesUrl(autoHostingAttribute,
+ webApiAttribute,
+ controllerType, method);
+ Console.WriteLine(url);
+ if (url == null) continue;
+ _controllerAutoHosting[url] = true;
+ _controllerTypes[url] = controllerType;
+
+ _controllerInstances[url] = null;
+
+ }
+
+
+ /* var routeAttribute = method.GetCustomAttribute(); // 获取方法上的 WebAPIAttribute 自定义属性
+ if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
+ {
+ var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
+ Console.WriteLine(url);
+ if (url == null) continue;
+ _controllerAutoHosting[url] = true;
+ _controllerTypes[url] = controllerType;
+ _controllerInstances[url] = null;
+ }*/
+ }
+ }
+ }
+ ///
+ /// 手动注册 自动实例化控制器实例
+ ///
+ public void RegisterAutoController() // 方法声明,用于动态注册路由
+ {
+ Type controllerType = typeof(T); // 获取控制器实例的类型
+ foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
+ {
+ var apiGetAttribute = method.GetCustomAttribute();
+ var apiPostAttribute = method.GetCustomAttribute();
+ if (apiGetAttribute == null && apiPostAttribute == null)
+ {
+ continue;
+ }
+
+
+
+ WebApiAttribute webApiAttribute = new WebApiAttribute()
+ {
+ Type = apiGetAttribute != null ? API.GET : API.POST,
+ Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
+ IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
+ };
+
+
+
+ var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
+
+ if (url == null) continue;
+ _controllerAutoHosting[url] = true;
+ _controllerTypes[url] = controllerType;
+
+ _controllerInstances[url] = null;
+
+ }
+ }
+
+
+ ///
+ /// 手动注册 实例持久控制器实例
+ ///
+ ///
+ public void RegisterController(TController controllerInstance) where TController : ControllerBase // 方法声明,用于动态注册路由
+ {
+ if(controllerInstance == null) return;
+ Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型
+ foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
+ {
+ var apiGetAttribute = method.GetCustomAttribute();
+ var apiPostAttribute = method.GetCustomAttribute();
+ if (apiGetAttribute == null && apiPostAttribute == null)
+ {
+ continue;
+ }
+
+
+
+ WebApiAttribute webApiAttribute = new WebApiAttribute()
+ {
+ Type = apiGetAttribute != null ? API.GET : API.POST,
+ Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
+ IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
+ };
+
+
+
+ var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
+
+ if (url == null) continue;
+ _controllerInstances[url] = controllerInstance;
+ _controllerAutoHosting[url] = false;
+ }
+ }
+
+ ///
+ /// 从方法中收集路由信息
+ ///
+ ///
+ public string AddRoutesUrl(AutoHostingAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method)
+ {
+ string controllerName;
+ if (autoHostingAttribute == null || string.IsNullOrWhiteSpace(autoHostingAttribute.Url))
+ {
+ controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
+ }
+ else
+ {
+ controllerName = autoHostingAttribute.Url;
+ }
+
+ var httpMethod = webAttribute.Type; // 获取 HTTP 方法
+ var customUrl = webAttribute.Url; // 获取自定义 URL
+
+ string url;
+
+ if (webAttribute.IsUrl)
+ {
+
+ if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
+ {
+ url = $"/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
+ }
+ else
+ {
+ customUrl = CleanUrl(customUrl);
+ url = $"/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
+ }
+ _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
+ }
+ else
+ {
+ if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
+ {
+ url = $"/{controllerName}".ToLower(); // 构建默认 URL
+ }
+ else
+ {
+ customUrl = CleanUrl(customUrl);
+ url = $"/{controllerName}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
+ }
+ _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
+ }
+
+ return url;
+
+ }
+
+
+ ///
+ /// 收集路由信息
+ ///
+ ///
+ public void CollectRoutes(Type controllerType)
+ {
+ string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
+ foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
+ {
+ var routeAttribute = method.GetCustomAttribute(); // 获取方法上的 WebAPIAttribute 自定义属性
+ if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
+ {
+ var customUrl = routeAttribute.Url; // 获取自定义 URL
+ string url;
+ if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
+ {
+ url = $"/api/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
+ }
+ else
+ {
+ customUrl = CleanUrl(customUrl);
+ url = $"/api/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
+ }
+ var httpMethod = routeAttribute.Type; // 获取 HTTP 方法
+ _routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
+ }
+ }
+ }
+
+
+ ///
+ /// 解析路由,调用对应的方法
+ ///
+ ///
+ ///
+ public async Task RouteAsync(HttpListenerContext context)
+ {
+ var request = context.Request; // 获取请求对象
+ var response = context.Response; // 获取响应对象
+ var url = request.Url; // 获取请求的 URL
+ var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法
+
+ var template = request.Url.AbsolutePath.ToLower();
+
+
+ if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
+ {
+ return false;
+ }
+
+
+
+ var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
+
+ ControllerBase controllerInstance;
+ if (!_controllerAutoHosting[template])
+ {
+ controllerInstance = (ControllerBase)_controllerInstances[template];
+ }
+ else
+ {
+
+ controllerInstance = (ControllerBase)serviceRegistry.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
+
+
+ }
+
+ if (controllerInstance == null)
+ {
+ return false; // 未找到控制器实例
+ }
+
+ controllerInstance.Url = url.AbsolutePath;
+ object result;
+ switch (httpMethod) // 根据请求的 HTTP 方法执行不同的操作
+ {
+ case "GET": // 如果是 GET 请求,传入方法、控制器、url参数
+ result = InvokeControllerMethodWithRouteValues(method, controllerInstance, routeValues);
+ break;
+ case "POST": // POST 请求传入方法、控制器、请求体内容,url参数
+ var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
+ controllerInstance.BobyData = requestBody;
+ var requestJObject = requestBody.FromJSON
public class FlowEnvironment : IFlowEnvironment
{
+ ///
+ /// 节点的命名空间
+ ///
+ public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}";
+
///
/// 加载Dll
///
public event LoadDLLHandler OnDllLoad;
///
- /// 加载节点事件
+ /// 项目加载完成
///
- public event LoadNodeHandler OnLoadNode;
+ public event ProjectLoadedHandler OnProjectLoaded;
///
/// 节点连接属性改变事件
///
public event NodeConnectChangeHandler OnNodeConnectChange;
///
- /// 节点创建时间
+ /// 节点创建事件
///
public event NodeCreateHandler OnNodeCreate;
///
@@ -75,11 +83,6 @@ namespace Serein.NodeFlow
private FlowStarter? nodeFlowStarter = null;
- ///
- /// 节点的命名空间
- ///
- public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}";
-
///
/// 一种轻量的IOC容器
///
@@ -103,7 +106,7 @@ namespace Serein.NodeFlow
public Dictionary Nodes { get; } = [];
- // public List Regions { get; } = [];
+ public List Regions { get; } = [];
///
/// 存放触发器节点(运行时全部调用)
@@ -149,6 +152,8 @@ namespace Serein.NodeFlow
var initMethods = MethodDetailss.Where(it => it.MethodDynamicType == NodeType.Init).ToList();
var loadingMethods = MethodDetailss.Where(it => it.MethodDynamicType == NodeType.Loading).ToList();
var exitMethods = MethodDetailss.Where(it => it.MethodDynamicType == NodeType.Exit).ToList();
+
+
await nodeFlowStarter.RunAsync(StartNode,
this,
runMethodDetailess,
@@ -180,6 +185,30 @@ namespace Serein.NodeFlow
}
+ ///
+ /// 运行环节加载了项目文件,需要创建节点控件
+ ///
+ ///
+ ///
+ ///
+ ///
+ private NodeControlType GetNodeControlType(NodeInfo nodeInfo)
+ {
+ // 创建控件实例
+ NodeControlType controlType = nodeInfo.Type switch
+ {
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" => NodeControlType.Action,// 动作节点控件
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" => NodeControlType.Flipflop, // 触发器节点控件
+
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" => NodeControlType.ExpCondition,// 条件表达式控件
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" => NodeControlType.ExpOp, // 操作表达式控件
+
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" => NodeControlType.ConditionRegion, // 条件区域控件
+ _ => NodeControlType.None,
+ };
+
+ return controlType;
+ }
#region 对外暴露的接口
@@ -201,59 +230,120 @@ namespace Serein.NodeFlow
(var assembly, var list) = LoadAssembly(dllFilePath);
if (assembly is not null && list.Count > 0)
{
- methodDetailss.AddRange(methodDetailss); // 暂存方法描述
- OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, methodDetailss)); // 通知UI创建dll面板显示
+ MethodDetailss.AddRange(list); // 暂存方法描述
+ OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, list)); // 通知UI创建dll面板显示
}
}
// 方法加载完成,缓存到运行环境中。
- MethodDetailss.AddRange(methodDetailss);
- methodDetailss.Clear();
+ //MethodDetailss.AddRange(methodDetailss);
+ //methodDetailss.Clear();
+ List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
+ List<(NodeModelBase, Position)> ordinaryNodes = new List<(NodeModelBase, Position)>();
// 加载节点
foreach (var nodeInfo in projectFile.Nodes)
{
- if (TryGetMethodDetails(nodeInfo.MethodName, out MethodDetails? methodDetails))
+ var controlType = GetNodeControlType(nodeInfo);
+ if(controlType == NodeControlType.None)
{
- OnLoadNode?.Invoke(new LoadNodeEventArgs(nodeInfo, methodDetails));
+ continue;
+ }
+ else
+ {
+ TryGetMethodDetails(nodeInfo.MethodName, out MethodDetails? methodDetails); // 加载项目时尝试获取方法信息
+ methodDetails ??= new MethodDetails();
+ var nodeModel = CreateNode(controlType, methodDetails);
+ nodeModel.LoadInfo(nodeInfo); // 创建节点model
+ if (nodeModel is null)
+ {
+ continue;
+ }
+ TryAddNode(nodeModel);
+ if(nodeInfo.ChildNodeGuids?.Length > 0)
+ {
+ regionChildNodes.Add((nodeModel,nodeInfo.ChildNodeGuids));
+ OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position));
+ }
+ else
+ {
+ ordinaryNodes.Add((nodeModel, nodeInfo.Position));
+ }
}
}
+ // 加载区域的子项
+ foreach((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes)
+ {
+ foreach (var childNodeGuid in item.childNodeGuids)
+ {
+ Nodes.TryGetValue(childNodeGuid, out NodeModelBase? childNode);
+ if (childNode is null)
+ {
+ // 节点尚未加载
+ continue;
+ }
+ // 存在节点
+ OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid));
+ }
+ }
+ // 加载节点
+ foreach ((NodeModelBase nodeModel, Position position) item in ordinaryNodes)
+ {
+ bool IsContinue = false;
+ foreach ((NodeModelBase region, string[] childNodeGuids) item2 in regionChildNodes)
+ {
+ foreach (var childNodeGuid in item2.childNodeGuids)
+ {
+ if (item.nodeModel.Guid.Equals(childNodeGuid))
+ {
+ IsContinue = true;
+ }
+ }
+ }
+ if (IsContinue) continue;
+ OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position));
+ }
+
+
// 确定节点之间的连接关系
foreach (var nodeInfo in projectFile.Nodes)
{
- if (!Nodes.TryGetValue(nodeInfo.Guid, out NodeModelBase fromNode))
+ if (!Nodes.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
{
// 不存在对应的起始节点
continue;
}
- List<(ConnectionType, string[])> nodeGuids = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
- (ConnectionType.IsFail, nodeInfo.FalseNodes),
- (ConnectionType.IsError, nodeInfo.ErrorNodes),
- (ConnectionType.Upstream, nodeInfo.UpstreamNodes)];
+ List<(ConnectionType connectionType, string[] guids)> allToNodes = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
+ (ConnectionType.IsFail, nodeInfo.FalseNodes),
+ (ConnectionType.IsError, nodeInfo.ErrorNodes),
+ (ConnectionType.Upstream, nodeInfo.UpstreamNodes)];
- List<(ConnectionType, NodeModelBase[])> nodes = nodeGuids.Where(info => info.Item2.Length > 0)
- .Select(info => (info.Item1,
- info.Item2.Select(guid => Nodes[guid])
+ List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
+ .Select(info => (info.connectionType,
+ info.guids.Select(guid => Nodes[guid])
.ToArray()))
.ToList();
// 遍历每种类型的节点分支(四种)
- foreach ((ConnectionType connectionType, NodeModelBase[] nodeBases) item in nodes)
+ foreach ((ConnectionType connectionType, NodeModelBase[] toNodes) item in fromNodes)
{
// 遍历当前类型分支的节点(确认连接关系)
- foreach (var node in item.nodeBases)
+ foreach (var toNode in item.toNodes)
{
- ConnectNode(fromNode, node, item.connectionType); // 加载时确定节点间的连接关系
+ ConnectNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
}
}
-
-
}
+
+ OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs());
}
+
+
+
///
/// 保存项目为项目文件
///
@@ -268,6 +358,7 @@ namespace Serein.NodeFlow
};
return projectData;
}
+
///
/// 从文件路径中加载DLL
///
@@ -281,66 +372,27 @@ namespace Serein.NodeFlow
MethodDetailss.AddRange(list);
OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, list));
}
-
}
///
- /// 创建节点
+ /// 运行时创建节点
///
///
- public void CreateNode(NodeControlType nodeControlType, MethodDetails? methodDetails = null)
+ public void CreateNode(NodeControlType nodeControlType, Position position, MethodDetails? methodDetails = null)
{
- // 确定创建的节点类型
- Type? nodeType = nodeControlType switch
- {
- NodeControlType.Action => typeof(SingleActionNode),
- NodeControlType.Flipflop => typeof(SingleFlipflopNode),
-
- NodeControlType.ExpOp => typeof(SingleExpOpNode),
- NodeControlType.ExpCondition => typeof(SingleConditionNode),
- NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
- _ => null
- };
- if (nodeType == null)
- {
- return;
- }
- // 生成实例
- var nodeObj = Activator.CreateInstance(nodeType);
- if (nodeObj is not NodeModelBase nodeBase)
- {
- return;
- }
-
- // 配置基础的属性
- nodeBase.ControlType = nodeControlType;
- nodeBase.Guid = Guid.NewGuid().ToString();
- if (methodDetails != null)
- {
- var md = methodDetails.Clone();
- nodeBase.DisplayName = md.MethodTips;
- nodeBase.MethodDetails = md;
- }
- Nodes[nodeBase.Guid] = nodeBase;
-
- // 如果是触发器,则需要添加到专属集合中
- if (nodeControlType == NodeControlType.Flipflop && nodeBase is SingleFlipflopNode flipflopNode)
- {
- var guid = flipflopNode.Guid;
- if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid)))
- {
- FlipflopNodes.Add(flipflopNode);
- }
- }
-
+ var nodeModel = CreateNode(nodeControlType, methodDetails);
+ TryAddNode(nodeModel);
// 通知UI更改
- OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeBase));
+ OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position));
// 因为需要UI先布置了元素,才能通知UI变更特效
// 如果不存在流程起始控件,默认设置为流程起始控件
if (StartNode is null)
{
- SetStartNode(nodeBase);
+ SetStartNode(nodeModel);
}
}
+
+
+
///
/// 移除节点
///
@@ -452,12 +504,19 @@ namespace Serein.NodeFlow
///
public bool TryGetMethodDetails(string name, out MethodDetails? md)
{
- md = MethodDetailss.FirstOrDefault(it => it.MethodName == name);
- if (md == null)
+ var isPass = false;
+ if (!string.IsNullOrEmpty(name))
{
+ md = MethodDetailss.FirstOrDefault(it => it.MethodName == name);
+ return md != null;
+ }
+ else
+ {
+ md = null;
return false;
}
- return true;
+
+
}
///
@@ -531,6 +590,65 @@ namespace Serein.NodeFlow
}
}
+
+ ///
+ /// 创建节点
+ ///
+ ///
+ private NodeModelBase CreateNode(NodeControlType nodeControlType,MethodDetails? methodDetails = null)
+ {
+ // 确定创建的节点类型
+ Type? nodeType = nodeControlType switch
+ {
+ NodeControlType.Action => typeof(SingleActionNode),
+ NodeControlType.Flipflop => typeof(SingleFlipflopNode),
+
+ NodeControlType.ExpOp => typeof(SingleExpOpNode),
+ NodeControlType.ExpCondition => typeof(SingleConditionNode),
+ NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
+ _ => null
+ };
+
+ if (nodeType == null)
+ {
+ throw new Exception($"节点类型错误[{nodeControlType}]");
+ }
+ // 生成实例
+ var nodeObj = Activator.CreateInstance(nodeType);
+ if (nodeObj is not NodeModelBase nodeBase)
+ {
+ throw new Exception($"无法创建目标节点类型的实例[{nodeControlType}]");
+ }
+
+ // 配置基础的属性
+ nodeBase.ControlType = nodeControlType;
+ if (methodDetails != null)
+ {
+ var md = methodDetails.Clone();
+ nodeBase.DisplayName = md.MethodTips;
+ nodeBase.MethodDetails = md;
+ }
+
+ // 如果是触发器,则需要添加到专属集合中
+ if (nodeControlType == NodeControlType.Flipflop && nodeBase is SingleFlipflopNode flipflopNode)
+ {
+ var guid = flipflopNode.Guid;
+ if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid)))
+ {
+ FlipflopNodes.Add(flipflopNode);
+ }
+ }
+
+ return nodeBase;
+ }
+
+ private bool TryAddNode(NodeModelBase nodeModel)
+ {
+ nodeModel.Guid ??= Guid.NewGuid().ToString();
+ Nodes[nodeModel.Guid] = nodeModel;
+ return true;
+ }
+
///
/// 连接节点
///
@@ -583,7 +701,6 @@ namespace Serein.NodeFlow
}
-
fromNode.SuccessorNodes[connectionType].Add(toNode); // 添加到起始节点的子分支
toNode.PreviousNodes[connectionType].Add(fromNode); // 添加到目标节点的父分支
OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs(fromNode.Guid,
@@ -603,6 +720,9 @@ namespace Serein.NodeFlow
StartNode = newStartNode;
OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid));
}
+
+
+
#endregion
#region 网络交互
@@ -633,6 +753,42 @@ namespace Serein.NodeFlow
};
}
+
+ public static Type? ControlTypeToModel(this NodeControlType nodeControlType )
+ {
+ // 确定创建的节点类型
+ Type? nodeType = nodeControlType switch
+ {
+ NodeControlType.Action => typeof(SingleActionNode),
+ NodeControlType.Flipflop => typeof(SingleFlipflopNode),
+
+ NodeControlType.ExpOp => typeof(SingleExpOpNode),
+ NodeControlType.ExpCondition => typeof(SingleConditionNode),
+ NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
+ _ => null
+ };
+ return nodeType;
+ }
+ public static NodeControlType ModelToControlType(this NodeControlType nodeControlType)
+ {
+ var type = nodeControlType.GetType();
+ NodeControlType controlType = type switch
+ {
+ Type when type == typeof(SingleActionNode) => NodeControlType.Action,
+ Type when type == typeof(SingleFlipflopNode) => NodeControlType.Flipflop,
+
+ Type when type == typeof(SingleExpOpNode) => NodeControlType.ExpOp,
+ Type when type == typeof(SingleConditionNode) => NodeControlType.ExpCondition,
+ Type when type == typeof(CompositeConditionNode) => NodeControlType.ConditionRegion,
+ _ => NodeControlType.None,
+ };
+ return controlType;
+ }
+
+
+
+
+
public static bool NotExitPreviousNode(this SingleFlipflopNode node)
{
ConnectionType[] ct = [ConnectionType.IsSucceed,
diff --git a/NodeFlow/FlowStarter.cs b/NodeFlow/FlowStarter.cs
index da72a72..2d87596 100644
--- a/NodeFlow/FlowStarter.cs
+++ b/NodeFlow/FlowStarter.cs
@@ -70,12 +70,12 @@ namespace Serein.NodeFlow
///
/// 起始节点
/// 运行环境
- /// 环境中已加载的所有节点方法
+ /// 环境中已加载的所有节点方法
/// 触发器节点
///
public async Task RunAsync(NodeModelBase startNode,
IFlowEnvironment env,
- List runMd,
+ List runNodeMd,
List initMethods,
List loadingMethods,
List exitMethods,
@@ -89,6 +89,8 @@ namespace Serein.NodeFlow
return;
}
+ #region 选择运行环境的上下文
+
// 判断使用哪一种流程上下文
var isNetFramework = true;
if (isNetFramework)
@@ -99,23 +101,38 @@ namespace Serein.NodeFlow
{
Context = new Serein.Library.Core.NodeFlow.DynamicContext(SereinIOC, env);
}
+ #endregion
#region 初始化运行环境的Ioc容器
// 清除节点使用的对象
- foreach (var nodeMd in runMd)
+ var thisRuningMds = new List();
+ thisRuningMds.AddRange(runNodeMd);
+ thisRuningMds.AddRange(initMethods);
+ thisRuningMds.AddRange(loadingMethods);
+ thisRuningMds.AddRange(exitMethods);
+
+ // .AddRange(initMethods).AddRange(loadingMethods).a
+ foreach (var nodeMd in thisRuningMds)
{
nodeMd.ActingInstance = null;
}
+
SereinIOC.Reset(); // 开始运行时清空ioc中注册的实例
// 初始化ioc容器中的类型对象
- foreach (var md in runMd)
+ foreach (var md in thisRuningMds)
{
- SereinIOC.Register(md.ActingInstanceType);
+ if(md.ActingInstanceType != null)
+ {
+ SereinIOC.Register(md.ActingInstanceType);
+ }
}
- SereinIOC.Build();
- foreach (var md in runMd)
+ SereinIOC.Build(); // 流程启动前的初始化
+ foreach (var md in thisRuningMds)
{
- md.ActingInstance = SereinIOC.GetOrInstantiate(md.ActingInstanceType);
+ if (md.ActingInstanceType != null)
+ {
+ md.ActingInstance = SereinIOC.GetOrRegisterInstantiate(md.ActingInstanceType);
+ }
}
//foreach (var md in flipflopNodes.Select(it => it.MethodDetails).ToArray())
@@ -124,23 +141,36 @@ namespace Serein.NodeFlow
//}
#endregion
-
- #region 创建Node中初始化、加载时、退出时调用的方法
-
+ #region 检查并修正初始化、加载时、退出时方法作用的对象,保证后续不会报错
foreach (var md in initMethods) // 初始化
{
- md.ActingInstance ??= Context.SereinIoc.GetOrInstantiate(md.ActingInstanceType);
+ md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
}
foreach (var md in loadingMethods) // 加载
{
- md.ActingInstance ??= Context.SereinIoc.GetOrInstantiate(md.ActingInstanceType);
+ md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
}
foreach (var md in exitMethods) // 初始化
{
- md.ActingInstance ??= Context.SereinIoc.GetOrInstantiate(md.ActingInstanceType);
+ md.ActingInstance ??= Context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
}
+ #endregion
+
+ #region 执行初始化,绑定IOC容器,再执行加载时,设置流程退出时的回调函数
object?[]? args = [Context];
+ foreach (var md in initMethods) // 初始化
+ {
+ object?[]? data = [md.ActingInstance, args];
+ md.MethodDelegate.DynamicInvoke(data);
+ }
+ Context.SereinIoc.Build(); // 绑定初始化时注册的类型
+ foreach (var md in loadingMethods) // 加载
+ {
+ object?[]? data = [md.ActingInstance, args];
+ md.MethodDelegate.DynamicInvoke(data);
+ }
+ Context.SereinIoc.Build(); // 预防有人在加载时才注册类型,再绑定一次
ExitAction = () =>
{
foreach (MethodDetails? md in exitMethods)
@@ -159,34 +189,18 @@ namespace Serein.NodeFlow
FlowState = RunState.Completion;
FlipFlopState = RunState.Completion;
};
- Context.SereinIoc.Build();
#endregion
- #region 执行初始化,然后绑定IOC容器,再执行加载时
-
- foreach (var md in initMethods) // 初始化 - 调用方法
- {
- object?[]? data = [md.ActingInstance, args];
- md.MethodDelegate.DynamicInvoke(data);
- }
- Context.SereinIoc.Build();
- foreach (var md in loadingMethods) // 加载
- {
- object?[]? data = [md.ActingInstance, args];
- md.MethodDelegate.DynamicInvoke(data);
- }
- #endregion
+ #region 开始启动流程
-
- // 节点任务的启动
try
{
-
+
if (flipflopNodes.Count > 0)
{
FlipFlopState = RunState.Running;
// 如果存在需要启动的触发器,则开始启动
- FlipFlopCts = SereinIOC.GetOrInstantiate();
+ FlipFlopCts = SereinIOC.GetOrRegisterInstantiate();
// 使用 TaskCompletionSource 创建未启动的触发器任务
var tasks = flipflopNodes.Select(async node =>
{
@@ -196,7 +210,7 @@ namespace Serein.NodeFlow
}
await startNode.StartExecution(Context);
// 等待结束
- if(FlipFlopCts != null)
+ if (FlipFlopCts != null)
{
while (!FlipFlopCts.IsCancellationRequested)
{
@@ -207,7 +221,8 @@ namespace Serein.NodeFlow
catch (Exception ex)
{
await Console.Out.WriteLineAsync(ex.ToString());
- }
+ }
+ #endregion
}
///
@@ -228,7 +243,7 @@ namespace Serein.NodeFlow
object?[]? parameters = singleFlipFlopNode.GetParameters(context, md);
// 调用委托并获取结果
- md.ActingInstance = context.SereinIoc.GetOrInstantiate(md.ActingInstanceType);
+ md.ActingInstance = context.SereinIoc.GetOrRegisterInstantiate(md.ActingInstanceType);
IFlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters);
diff --git a/NodeFlow/Model/CompositeActionNode.cs b/NodeFlow/Model/CompositeActionNode.cs
index 853d3a9..9bac9fa 100644
--- a/NodeFlow/Model/CompositeActionNode.cs
+++ b/NodeFlow/Model/CompositeActionNode.cs
@@ -19,13 +19,13 @@ namespace Serein.NodeFlow.Model
ActionNodes = actionNodes;
}
-
- public override Parameterdata[] GetParameterdatas()
+
+ internal override Parameterdata[] GetParameterdatas()
{
return [];
}
- public override NodeInfo ToInfo()
+ internal override NodeInfo ToInfo()
{
if (MethodDetails == null) return null;
@@ -35,8 +35,8 @@ namespace Serein.NodeFlow.Model
//var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
- var upstreamNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 上游分支
- var errorNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 异常分支
+ var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
+ var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
// 生成参数列表
Parameterdata[] parameterData = GetParameterdatas();
@@ -51,7 +51,7 @@ namespace Serein.NodeFlow.Model
UpstreamNodes = upstreamNodes.ToArray(),
ParameterData = parameterData.ToArray(),
ErrorNodes = errorNodes.ToArray(),
- ChildNodes = ActionNodes.Select(node => node.ToInfo()).ToArray(),
+ ChildNodeGuids = ActionNodes.Select(node => node.Guid).ToArray(),
};
}
}
diff --git a/NodeFlow/Model/CompositeConditionNode.cs b/NodeFlow/Model/CompositeConditionNode.cs
index 6d5b932..c18e1d1 100644
--- a/NodeFlow/Model/CompositeConditionNode.cs
+++ b/NodeFlow/Model/CompositeConditionNode.cs
@@ -61,14 +61,14 @@ namespace Serein.NodeFlow.Model
}
}
- public override Parameterdata[] GetParameterdatas()
+ internal override Parameterdata[] GetParameterdatas()
{
return [];
}
- public override NodeInfo ToInfo()
+ internal override NodeInfo ToInfo()
{
- if (MethodDetails == null) return null;
+ //if (MethodDetails == null) return null;
//var trueNodes = SucceedBranch.Select(item => item.Guid); // 真分支
//var falseNodes = FailBranch.Select(item => item.Guid);// 假分支
@@ -76,8 +76,8 @@ namespace Serein.NodeFlow.Model
//var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
- var upstreamNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 上游分支
- var errorNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 异常分支
+ var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
+ var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
// 生成参数列表
Parameterdata[] parameterData = GetParameterdatas();
@@ -93,7 +93,7 @@ namespace Serein.NodeFlow.Model
UpstreamNodes = upstreamNodes.ToArray(),
ParameterData = parameterData.ToArray(),
ErrorNodes = errorNodes.ToArray(),
- ChildNodes = ConditionNodes.Select(node => node.ToInfo()).ToArray(),
+ ChildNodeGuids = ConditionNodes.Select(node => node.Guid).ToArray(),
};
}
diff --git a/NodeFlow/Model/SingleActionNode.cs b/NodeFlow/Model/SingleActionNode.cs
index 22219c2..c9ff4e1 100644
--- a/NodeFlow/Model/SingleActionNode.cs
+++ b/NodeFlow/Model/SingleActionNode.cs
@@ -64,15 +64,15 @@ namespace Serein.NodeFlow.Model
// context.SetFlowData(result);
// }
//}
- public override Parameterdata[] GetParameterdatas()
+ internal override Parameterdata[] GetParameterdatas()
{
if (base.MethodDetails.ExplicitDatas.Length > 0)
{
return MethodDetails.ExplicitDatas
.Select(it => new Parameterdata
{
- state = it.IsExplicitData,
- value = it.DataValue,
+ State = it.IsExplicitData,
+ Value = it.DataValue,
})
.ToArray();
}
diff --git a/NodeFlow/Model/SingleConditionNode.cs b/NodeFlow/Model/SingleConditionNode.cs
index 6b4c047..8082fa1 100644
--- a/NodeFlow/Model/SingleConditionNode.cs
+++ b/NodeFlow/Model/SingleConditionNode.cs
@@ -55,31 +55,43 @@ namespace Serein.NodeFlow.Model
return result;
}
- public override Parameterdata[] GetParameterdatas()
+ internal override Parameterdata[] GetParameterdatas()
{
- if (base.MethodDetails.ExplicitDatas.Length > 0)
+ var value = CustomData switch
{
- return MethodDetails.ExplicitDatas
- .Select(it => new Parameterdata
- {
- state = IsCustomData,
- expression = Expression,
- value = CustomData switch
- {
- Type when CustomData.GetType() == typeof(int)
- && CustomData.GetType() == typeof(double)
- && CustomData.GetType() == typeof(float)
- => ((double)CustomData).ToString(),
- Type when CustomData.GetType() == typeof(bool) => ((bool)CustomData).ToString(),
- _ => CustomData?.ToString()!,
- }
- })
- .ToArray();
- }
- else
+ Type when CustomData.GetType() == typeof(int)
+ && CustomData.GetType() == typeof(double)
+ && CustomData.GetType() == typeof(float)
+ => ((double)CustomData).ToString(),
+ Type when CustomData.GetType() == typeof(bool) => ((bool)CustomData).ToString(),
+ _ => CustomData?.ToString()!,
+ };
+ return [new Parameterdata
{
- return [];
+ State = IsCustomData,
+ Expression = Expression,
+ Value = value,
+ }];
+ }
+
+
+
+ internal override NodeModelBase LoadInfo(NodeInfo nodeInfo)
+ {
+ var node = this;
+ if (node != null)
+ {
+ node.Guid = nodeInfo.Guid;
+ for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
+ {
+ Parameterdata? pd = nodeInfo.ParameterData[i];
+ node.IsCustomData = pd.State;
+ node.CustomData = pd.Value;
+ node.Expression = pd.Expression;
+
+ }
}
+ return this;
}
//public override void Execute(DynamicContext context)
diff --git a/NodeFlow/Model/SingleExpOpNode.cs b/NodeFlow/Model/SingleExpOpNode.cs
index 88d0753..0900e5a 100644
--- a/NodeFlow/Model/SingleExpOpNode.cs
+++ b/NodeFlow/Model/SingleExpOpNode.cs
@@ -48,23 +48,25 @@ namespace Serein.NodeFlow.Model
}
- public override Parameterdata[] GetParameterdatas()
+ internal override Parameterdata[] GetParameterdatas()
{
- if (base.MethodDetails.ExplicitDatas.Length > 0)
+ return [new Parameterdata{ Expression = Expression}];
+ }
+
+
+
+ internal override NodeModelBase LoadInfo(NodeInfo nodeInfo)
+ {
+ var node = this;
+ if (node != null)
{
- return MethodDetails.ExplicitDatas
- .Select(it => new Parameterdata
- {
- state = it.IsExplicitData,
- // value = it.DataValue,
- expression = Expression,
- })
- .ToArray();
- }
- else
- {
- return [];
+ node.Guid = nodeInfo.Guid;
+ for (int i = 0; i < nodeInfo.ParameterData.Length; i++)
+ {
+ node.Expression = nodeInfo.ParameterData[i].Expression;
+ }
}
+ return this;
}
}
}
diff --git a/NodeFlow/Model/SingleFlipflopNode.cs b/NodeFlow/Model/SingleFlipflopNode.cs
index 9c2eef5..76ad23b 100644
--- a/NodeFlow/Model/SingleFlipflopNode.cs
+++ b/NodeFlow/Model/SingleFlipflopNode.cs
@@ -15,15 +15,15 @@ namespace Serein.NodeFlow.Model
return null;
}
- public override Parameterdata[] GetParameterdatas()
+ internal override Parameterdata[] GetParameterdatas()
{
if (base.MethodDetails.ExplicitDatas.Length > 0)
{
return MethodDetails.ExplicitDatas
.Select(it => new Parameterdata
{
- state = it.IsExplicitData,
- value = it.DataValue
+ State = it.IsExplicitData,
+ Value = it.DataValue
})
.ToArray();
}
diff --git a/NodeFlow/NodeStaticConfig.cs b/NodeFlow/NodeStaticConfig.cs
new file mode 100644
index 0000000..8933251
--- /dev/null
+++ b/NodeFlow/NodeStaticConfig.cs
@@ -0,0 +1,16 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.NodeFlow
+{
+ public static class NodeStaticConfig
+ {
+ ///
+ /// 节点的命名空间
+ ///
+ public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}";
+ }
+}
diff --git a/NodeFlow/Tool/SereinExpression/Resolver/MemberConditionResolver.cs b/NodeFlow/Tool/SereinExpression/Resolver/MemberConditionResolver.cs
index 9d15635..d75b8ab 100644
--- a/NodeFlow/Tool/SereinExpression/Resolver/MemberConditionResolver.cs
+++ b/NodeFlow/Tool/SereinExpression/Resolver/MemberConditionResolver.cs
@@ -18,6 +18,8 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
public override bool Evaluate(object? obj)
{
//object? memberValue = GetMemberValue(obj, MemberPath);
+
+
if (TargetObj is T typedObj)
{
return new ValueTypeConditionResolver
diff --git a/NodeFlow/Tool/SereinExpression/Resolver/MemberStringConditionResolver.cs b/NodeFlow/Tool/SereinExpression/Resolver/MemberStringConditionResolver.cs
index e75346d..669aa64 100644
--- a/NodeFlow/Tool/SereinExpression/Resolver/MemberStringConditionResolver.cs
+++ b/NodeFlow/Tool/SereinExpression/Resolver/MemberStringConditionResolver.cs
@@ -18,7 +18,16 @@ namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
public override bool Evaluate(object obj)
{
- object memberValue = GetMemberValue(obj, MemberPath);
+ object memberValue;
+ if (!string.IsNullOrWhiteSpace(MemberPath))
+ {
+ memberValue = GetMemberValue(obj, MemberPath);
+ }
+ else
+ {
+ memberValue = obj;
+ }
+
if (memberValue is string strObj)
{
return new StringConditionResolver
diff --git a/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs b/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs
index 0072fc5..1525bbd 100644
--- a/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs
+++ b/NodeFlow/Tool/SereinExpression/SereinConditionParser.cs
@@ -12,7 +12,9 @@ namespace Serein.NodeFlow.Tool.SereinExpression
try
{
- return ConditionParse(data, expression).Evaluate(data);
+ var parse = ConditionParse(data, expression);
+ var result = parse.Evaluate(data);
+ return result;
}
catch (Exception ex)
@@ -24,7 +26,7 @@ namespace Serein.NodeFlow.Tool.SereinExpression
public static SereinConditionResolver ConditionParse(object data, string expression)
{
- if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
+ if (expression.StartsWith('.') || expression.StartsWith('<')) // 表达式前缀属于从上一个节点数据对象获取成员值
{
return ParseObjectExpression(data, expression);
}
@@ -128,9 +130,6 @@ namespace Serein.NodeFlow.Tool.SereinExpression
operatorStr = parts[0].ToLower(); // 操作类型
valueStr = string.Join(' ', parts.Skip(1)); // 表达式值
}
-
- targetObj = GetMemberValue(data, memberPath);// 获取对象成员,作为表达式的目标对象
-
Type? tempType = typeStr switch
{
"int" => typeof(int),
@@ -140,6 +139,15 @@ namespace Serein.NodeFlow.Tool.SereinExpression
_ => Type.GetType(typeStr)
};
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
+ if (string.IsNullOrWhiteSpace(memberPath))
+ {
+ targetObj = Convert.ChangeType(data, type);
+ }
+ else
+ {
+ targetObj = GetMemberValue(data, memberPath);// 获取对象成员,作为表达式的目标对象
+ }
+
}
#region 解析类型 int
diff --git a/WorkBench/App.xaml.cs b/WorkBench/App.xaml.cs
index 73d2c73..f1338a9 100644
--- a/WorkBench/App.xaml.cs
+++ b/WorkBench/App.xaml.cs
@@ -150,13 +150,13 @@ namespace Serein.WorkBench
Shutdown(); // 关闭应用程序
}
}
- else if (1 == 11)
+ else if (1 == 1)
{
- string filePath = @"F:\临时\project\U9 project.dnf";
+ string filePath = @"F:\临时\project\new project.dnf";
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
- FData = JsonConvert.DeserializeObject(content);
- App.FileDataPath = System.IO.Path.GetDirectoryName(filePath);
+ App.FData = JsonConvert.DeserializeObject(content);
+ App.FileDataPath = filePath;//System.IO.Path.GetDirectoryName(filePath)!;
}
}
diff --git a/WorkBench/LogWindow.xaml b/WorkBench/LogWindow.xaml
index 5574680..1ebc21c 100644
--- a/WorkBench/LogWindow.xaml
+++ b/WorkBench/LogWindow.xaml
@@ -5,6 +5,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Serein.WorkBench"
mc:Ignorable="d"
+ Topmost="False"
Title="LogWindow" Height="600" Width="400"
Closing="Window_Closing">
diff --git a/WorkBench/MainWindow.xaml b/WorkBench/MainWindow.xaml
index 9d36334..df74eb6 100644
--- a/WorkBench/MainWindow.xaml
+++ b/WorkBench/MainWindow.xaml
@@ -6,6 +6,7 @@
Title="Dynamic Node Flow" Height="700" Width="1200"
AllowDrop="True" Drop="Window_Drop" DragOver="Window_DragOver"
Loaded="Window_Loaded"
+ ContentRendered="Window_ContentRendered"
Closing="Window_Closing">
@@ -136,7 +137,7 @@
private readonly LogWindow logWindow;
- ///
- /// 节点的命名空间
- ///
- public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}";
-
-
///
/// 流程运行环境
///
@@ -82,7 +77,7 @@ namespace Serein.WorkBench
///
/// 拖动创建节点控件时的鼠标位置
///
- private Point canvasDropPosition;
+ // private Point canvasDropPosition;
///
/// 记录拖动开始时的鼠标位置
@@ -138,6 +133,7 @@ namespace Serein.WorkBench
{
ViewModel = new MainWindowViewModel(this);
FlowEnvironment = ViewModel.FlowEnvironment;
+ InitFlowEvent();
InitializeComponent();
logWindow = new LogWindow();
@@ -145,14 +141,23 @@ namespace Serein.WorkBench
// 重定向 Console 输出
var logTextWriter = new LogTextWriter(WriteLog);
Console.SetOut(logTextWriter);
- InitFlowEvent();
InitUI();
+
+ var project = App.FData;
+ if (project == null)
+ {
+ return;
+ }
+
+ InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Lenght);// 设置画布大小
+ FlowEnvironment.LoadProject(project, App.FileDataPath); // 加载项目
}
private void InitFlowEvent()
{
FlowEnvironment.OnDllLoad += FlowEnvironment_DllLoadEvent;
- FlowEnvironment.OnLoadNode += FlowEnvironment_NodeLoadEvent;
+ // FlowEnvironment.OnLoadNode += FlowEnvironment_NodeLoadEvent;
+ FlowEnvironment.OnProjectLoaded += FlowEnvironment_OnProjectLoaded;
FlowEnvironment.OnStartNodeChange += FlowEnvironment_StartNodeChangeEvent;
FlowEnvironment.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt;
FlowEnvironment.OnNodeCreate += FlowEnvironment_NodeCreateEvent;
@@ -161,6 +166,7 @@ namespace Serein.WorkBench
}
+
private void InitUI()
{
canvasTransformGroup = new TransformGroup();
@@ -171,22 +177,41 @@ namespace Serein.WorkBench
canvasTransformGroup.Children.Add(translateTransform);
FlowChartCanvas.RenderTransform = canvasTransformGroup;
-
FlowChartCanvas.RenderTransformOrigin = new Point(0.5, 0.5);
}
-
+ #region Main窗体加载方法
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ }
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
logWindow.Close();
System.Windows.Application.Current.Shutdown();
}
-
+ private void Window_ContentRendered(object sender, EventArgs e)
+ {
+ foreach (var connection in Connections)
+ {
+ connection.Refresh();
+ }
+ }
+ #endregion
public void WriteLog(string message)
{
logWindow.AppendText(message);
}
#region 运行环境事件
+ private void FlowEnvironment_OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
+ {
+ //foreach(var connection in Connections)
+ //{
+ // connection.Refresh();
+ //}
+ Console.WriteLine((FlowChartStackPanel.ActualWidth, FlowChartStackPanel.ActualHeight));
+ }
+
+
///
/// 运行完成
///
@@ -202,27 +227,30 @@ namespace Serein.WorkBench
///
private void FlowEnvironment_DllLoadEvent(LoadDLLEventArgs eventArgs)
{
- Assembly assembly = eventArgs.Assembly;
- List methodDetailss = eventArgs.MethodDetailss;
+ this.Dispatcher.Invoke(() => {
+ Assembly assembly = eventArgs.Assembly;
+ List methodDetailss = eventArgs.MethodDetailss;
- var dllControl = new DllControl
- {
- Header = "DLL name : " + assembly.GetName().Name // 设置控件标题为程序集名称
- };
-
- foreach (var methodDetails in methodDetailss)
- {
- switch (methodDetails.MethodDynamicType)
+ var dllControl = new DllControl
{
- case Library.Enums.NodeType.Action:
- dllControl.AddAction(methodDetails.Clone()); // 添加动作类型到控件
- break;
- case Library.Enums.NodeType.Flipflop:
- dllControl.AddFlipflop(methodDetails.Clone()); // 添加触发器方法到控件
- break;
+ Header = "DLL name : " + assembly.GetName().Name // 设置控件标题为程序集名称
+ };
+
+ foreach (var methodDetails in methodDetailss)
+ {
+ switch (methodDetails.MethodDynamicType)
+ {
+ case Library.Enums.NodeType.Action:
+ dllControl.AddAction(methodDetails.Clone()); // 添加动作类型到控件
+ break;
+ case Library.Enums.NodeType.Flipflop:
+ dllControl.AddFlipflop(methodDetails.Clone()); // 添加触发器方法到控件
+ break;
+ }
}
- }
- DllStackPanel.Children.Add(dllControl); // 将控件添加到界面上显示
+ DllStackPanel.Children.Add(dllControl); // 将控件添加到界面上显示
+ });
+
}
///
@@ -230,34 +258,35 @@ namespace Serein.WorkBench
///
///
///
- private void FlowEnvironment_NodeLoadEvent(LoadNodeEventArgs eventArgs)
- {
- if (!eventArgs.IsSucceed)
- {
- MessageBox.Show(eventArgs.ErrorTips);
- return;
- }
- NodeInfo nodeInfo = eventArgs.NodeInfo;
- MethodDetails methodDetailss = eventArgs.MethodDetailss;
+ //private void FlowEnvironment_NodeLoadEvent(LoadNodeEventArgs eventArgs)
+ //{
+ // if (!eventArgs.IsSucceed)
+ // {
+ // MessageBox.Show(eventArgs.ErrorTips);
+ // return;
+ // }
- // 创建对应的实例(包含NodeModel,NodeControl,NodeControlViewModel)
- NodeControlBase? nodeControl = CreateNodeControlOfNodeInfo(nodeInfo, methodDetailss);
- if (nodeControl == null)
- {
- WriteLog($"无法为节点类型创建节点控件: {nodeInfo.MethodName}\r\n");
- return;
- // ConfigureNodeControl(nodeInfo, nodeControl, nodeControls, regionControls);
- }
-
- // 判断是否属于区域控件,如果是,则加载区域子项
- if (nodeControl is ActionRegionControl || nodeControl is ConditionRegionControl)
- {
- AddNodeControlInRegeionControl(nodeControl, nodeInfo.ChildNodes);
- }
+ // NodeInfo nodeInfo = eventArgs.NodeInfo;
+ // MethodDetails methodDetailss = eventArgs.MethodDetailss;
- NodeControls.TryAdd(nodeInfo.Guid, nodeControl); // 存放对应的控件
- PlaceNodeOnCanvas(nodeControl, nodeInfo.Position.X, nodeInfo.Position.Y); // 配置节点,并放置在画布上
- }
+ // // 创建对应的实例(包含NodeModel,NodeControl,NodeControlViewModel)
+ // NodeControlBase? nodeControl = CreateNodeControlOfNodeInfo(nodeInfo, methodDetailss);
+ // if (nodeControl == null)
+ // {
+ // WriteLog($"无法为节点类型创建节点控件: {nodeInfo.MethodName}\r\n");
+ // return;
+ // // ConfigureNodeControl(nodeInfo, nodeControl, nodeControls, regionControls);
+ // }
+
+ // // 判断是否属于区域控件,如果是,则加载区域子项
+ // // if (nodeControl is ActionRegionControl || nodeControl is ConditionRegionControl)
+ // // {
+ // // AddNodeControlInRegeionControl(nodeControl, nodeInfo.ChildNodes);
+ // // }
+
+ // NodeControls.TryAdd(nodeInfo.Guid, nodeControl); // 存放对应的控件
+ // PlaceNodeOnCanvas(nodeControl, nodeInfo.Position.X, nodeInfo.Position.Y); // 配置节点,并放置在画布上
+ //}
///
/// 节点连接关系变更
@@ -267,40 +296,48 @@ namespace Serein.WorkBench
///
private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs)
{
- string fromNodeGuid = eventArgs.FromNodeGuid;
- string toNodeGuid = eventArgs.ToNodeGuid;
- if (!NodeControls.TryGetValue(fromNodeGuid, out var fromNode) || !NodeControls.TryGetValue(toNodeGuid, out var toNode))
+ this.Dispatcher.Invoke(() =>
{
- return;
- }
- ConnectionType connectionType = eventArgs.ConnectionType;
- if(eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create)
- {
- // 添加连接
- var connection = new Connection
+ string fromNodeGuid = eventArgs.FromNodeGuid;
+ string toNodeGuid = eventArgs.ToNodeGuid;
+ if (!NodeControls.TryGetValue(fromNodeGuid, out var fromNode) || !NodeControls.TryGetValue(toNodeGuid, out var toNode))
{
- Start = fromNode,
- End = toNode,
- Type = connectionType
- };
-
- BsControl.Draw(FlowChartCanvas, connection); // 添加贝塞尔曲线显示
- ConfigureLineContextMenu(connection); // 设置连接右键事件
- Connections.Add(connection);
- EndConnection();
- }
- else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remote)
- {
- // 需要移除连接
- var removeConnections = Connections.Where(c => c.Start.ViewModel.Node.Guid.Equals(fromNodeGuid)
- && c.End.ViewModel.Node.Guid.Equals(toNodeGuid))
- .ToList();
- foreach(var connection in removeConnections)
- {
- connection.RemoveFromCanvas(FlowChartCanvas);
- Connections.Remove(connection);
+ return;
}
- }
+ ConnectionType connectionType = eventArgs.ConnectionType;
+ if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create)
+ {
+ lock (Connections)
+ {
+ // 添加连接
+ var connection = new Connection
+ {
+ Start = fromNode,
+ End = toNode,
+ Type = connectionType
+ };
+
+ BsControl.Draw(FlowChartCanvas, connection); // 添加贝塞尔曲线显示
+ ConfigureLineContextMenu(connection); // 设置连接右键事件
+ Connections.Add(connection);
+ EndConnection();
+
+ }
+
+ }
+ else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remote)
+ {
+ // 需要移除连接
+ var removeConnections = Connections.Where(c => c.Start.ViewModel.Node.Guid.Equals(fromNodeGuid)
+ && c.End.ViewModel.Node.Guid.Equals(toNodeGuid))
+ .ToList();
+ foreach (var connection in removeConnections)
+ {
+ connection.RemoveFromCanvas(FlowChartCanvas);
+ Connections.Remove(connection);
+ }
+ }
+ });
}
///
@@ -309,14 +346,16 @@ namespace Serein.WorkBench
///
private void FlowEnvironment_NodeRemoteEvent(NodeRemoteEventArgs eventArgs)
{
- var nodeGuid = eventArgs.NodeGuid;
- if (!NodeControls.TryGetValue(nodeGuid, out var nodeControl))
+ this.Dispatcher.Invoke(() =>
{
- return;
- }
- FlowChartCanvas.Children.Remove(nodeControl);
- NodeControls.Remove(nodeControl.ViewModel.Node.Guid);
-
+ var nodeGuid = eventArgs.NodeGuid;
+ if (!NodeControls.TryGetValue(nodeGuid, out var nodeControl))
+ {
+ return;
+ }
+ FlowChartCanvas.Children.Remove(nodeControl);
+ NodeControls.Remove(nodeControl.ViewModel.Node.Guid);
+ });
}
///
@@ -326,35 +365,52 @@ namespace Serein.WorkBench
///
private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs)
{
- if (eventArgs.NodeModel is not NodeModelBase nodeModelBase)
+ this.Dispatcher.Invoke(() =>
{
- return;
- }
+ if (eventArgs.NodeModel is not NodeModelBase nodeModelBase)
+ {
+ return;
+ }
- // 创建对应控件
- NodeControlBase? nodeControl = nodeModelBase.ControlType switch
- {
- NodeControlType.Action => CreateNodeControl(nodeModelBase), //typeof(ActionNodeControl),
- NodeControlType.Flipflop => CreateNodeControl(nodeModelBase),
- NodeControlType.ExpCondition => CreateNodeControl(nodeModelBase),
- NodeControlType.ExpOp => CreateNodeControl(nodeModelBase),
- NodeControlType.ConditionRegion => CreateNodeControl(nodeModelBase),
- _ => null,
- };
- if(nodeControl == null)
- {
- return;
- }
+ // MethodDetails methodDetailss = eventArgs.MethodDetailss;
+ Position position = eventArgs.Position;
-
- NodeControls.TryAdd(nodeModelBase.Guid, nodeControl);
-
- if (!TryPlaceNodeInRegion(nodeControl))
- {
- PlaceNodeOnCanvas(nodeControl, canvasDropPosition.X, canvasDropPosition.Y);
- }
+ // 创建对应控件
+ NodeControlBase? nodeControl = nodeModelBase.ControlType switch
+ {
+ NodeControlType.Action => CreateNodeControl(nodeModelBase), //typeof(ActionNodeControl),
+ NodeControlType.Flipflop => CreateNodeControl(nodeModelBase),
+ NodeControlType.ExpCondition => CreateNodeControl(nodeModelBase),
+ NodeControlType.ExpOp => CreateNodeControl(nodeModelBase),
+ NodeControlType.ConditionRegion => CreateNodeControl(nodeModelBase),
+ _ => null,
+ };
+ if (nodeControl == null)
+ {
+ return;
+ }
+ NodeControls.TryAdd(nodeModelBase.Guid, nodeControl);
+
+ if (eventArgs.IsAddInRegion && NodeControls.TryGetValue(eventArgs.RegeionGuid, out NodeControlBase? regionControl))
+ {
+ if (regionControl is not null)
+ {
+ TryPlaceNodeInRegion(regionControl, nodeControl);
+ }
+ return;
+ }
+ else
+ {
+ if (!TryPlaceNodeInRegion(nodeControl, position))
+ {
+ PlaceNodeOnCanvas(nodeControl, position.X, position.Y);
+ }
+ }
+
+
+ });
}
@@ -365,29 +421,32 @@ namespace Serein.WorkBench
///
private void FlowEnvironment_StartNodeChangeEvent(StartNodeChangeEventArgs eventArgs)
{
- string oldNodeGuid = eventArgs.OldNodeGuid;
- string newNodeGuid = eventArgs.NewNodeGuid;
- if (!NodeControls.TryGetValue(newNodeGuid, out var newStartNodeControl))
+ this.Dispatcher.Invoke(() =>
{
- return;
- }
- if (newStartNodeControl == null)
- {
- return;
- }
- if (!string.IsNullOrEmpty(oldNodeGuid))
- {
- NodeControls.TryGetValue(oldNodeGuid, out var oldStartNodeControl);
- if (oldStartNodeControl != null)
+ string oldNodeGuid = eventArgs.OldNodeGuid;
+ string newNodeGuid = eventArgs.NewNodeGuid;
+ if (!NodeControls.TryGetValue(newNodeGuid, out var newStartNodeControl))
{
- oldStartNodeControl.BorderBrush = Brushes.Black;
- oldStartNodeControl.BorderThickness = new Thickness(0);
+ return;
+ }
+ if (newStartNodeControl == null)
+ {
+ return;
+ }
+ if (!string.IsNullOrEmpty(oldNodeGuid))
+ {
+ NodeControls.TryGetValue(oldNodeGuid, out var oldStartNodeControl);
+ if (oldStartNodeControl != null)
+ {
+ oldStartNodeControl.BorderBrush = Brushes.Black;
+ oldStartNodeControl.BorderThickness = new Thickness(0);
+ }
+
}
- }
-
- newStartNodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"));
- newStartNodeControl.BorderThickness = new Thickness(2);
+ newStartNodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"));
+ newStartNodeControl.BorderThickness = new Thickness(2);
+ });
}
@@ -396,31 +455,7 @@ namespace Serein.WorkBench
#region 加载 DynamicNodeFlow 文件
- private void Window_Loaded(object sender, RoutedEventArgs e)
- {
- var project = App.FData;
- if (project == null)
- {
- return;
- }
-
- InitializeCanvas(project.Basic.canvas.width, project.Basic.canvas.lenght);// 设置画布大小
- FlowEnvironment.LoadProject(project, App.FileDataPath); // 加载项目
-
-
- //LoadDll(project); // 加载DLL
- //LoadNodeControls(project); // 加载节点
-
- //var startNode = nodeControls.Values.FirstOrDefault(control => control.ViewModel.Node.Guid.Equals(project.StartNode));
- //var startNodeGuid = nodeControls.Keys.FirstOrDefault(guid => guid.Equals(project.StartNode));
- //if (!string.IsNullOrEmpty(startNodeGuid))
- //{
- // FlowEnvironment.SetStartNode(startNodeGuid);
- //}
-
- }
-
-
+
///
/// 运行环节加载了项目文件,需要创建节点控件
///
@@ -433,17 +468,17 @@ namespace Serein.WorkBench
// 创建控件实例
NodeControlBase nodeControl = nodeInfo.Type switch
{
- $"{NodeSpaceName}.{nameof(SingleActionNode)}" =>
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleActionNode)}" =>
CreateNodeControl(methodDetailss),// 动作节点控件
- $"{NodeSpaceName}.{nameof(SingleFlipflopNode)}" =>
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleFlipflopNode)}" =>
CreateNodeControl(methodDetailss), // 触发器节点控件
- $"{NodeSpaceName}.{nameof(SingleConditionNode)}" =>
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleConditionNode)}" =>
CreateNodeControl(), // 条件表达式控件
- $"{NodeSpaceName}.{nameof(SingleExpOpNode)}" =>
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(SingleExpOpNode)}" =>
CreateNodeControl(), // 操作表达式控件
- $"{NodeSpaceName}.{nameof(CompositeConditionNode)}" =>
+ $"{NodeStaticConfig.NodeSpaceName}.{nameof(CompositeConditionNode)}" =>
CreateNodeControl(), // 条件区域控件
_ => throw new NotImplementedException($"非预期的节点类型{nodeInfo.Type}"),
};
@@ -755,13 +790,14 @@ namespace Serein.WorkBench
///
private void FlowChartCanvas_Drop(object sender, DragEventArgs e)
{
- canvasDropPosition = e.GetPosition(FlowChartCanvas); // 更新画布落点
+ var canvasDropPosition = e.GetPosition(FlowChartCanvas); // 更新画布落点
+ Position position = new Position(canvasDropPosition.X, canvasDropPosition.Y);
if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas))
{
if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeData nodeData)
{
// 创建DLL文件的节点对象
- FlowEnvironment.CreateNode(nodeData.NodeControlType, nodeData.MethodDetails);
+ FlowEnvironment.CreateNode(nodeData.NodeControlType, position, nodeData.MethodDetails);
}
}
else if (e.Data.GetDataPresent(MouseNodeType.CreateBaseNodeInCanvas))
@@ -778,7 +814,7 @@ namespace Serein.WorkBench
if(nodeControlType != NodeControlType.None)
{
// 创建基础节点对象
- FlowEnvironment.CreateNode(nodeControlType);
+ FlowEnvironment.CreateNode(nodeControlType, position);
}
}
}
@@ -792,9 +828,10 @@ namespace Serein.WorkBench
///
///
///
- private bool TryPlaceNodeInRegion(NodeControlBase nodeControl)
+ private bool TryPlaceNodeInRegion(NodeControlBase nodeControl, Position position)
{
- HitTestResult hitTestResult = VisualTreeHelper.HitTest(FlowChartCanvas, canvasDropPosition);
+ var point = new Point(position.X, position.Y);
+ HitTestResult hitTestResult = VisualTreeHelper.HitTest(FlowChartCanvas, point);
if (hitTestResult != null && hitTestResult.VisualHit is UIElement hitElement)
{
// 准备放置条件表达式控件
@@ -803,10 +840,12 @@ namespace Serein.WorkBench
ConditionRegionControl conditionRegion = GetParentOfType(hitElement);
if (conditionRegion != null)
{
- // 如果存在条件区域容器
- conditionRegion.AddCondition(nodeControl);
+ TryPlaceNodeInRegion(conditionRegion, nodeControl);
+ //// 如果存在条件区域容器
+ //conditionRegion.AddCondition(nodeControl);
return true;
}
+
}
}
return false;
@@ -831,8 +870,26 @@ namespace Serein.WorkBench
return null;
}
+ ///
+ /// 将节点放在目标区域中
+ ///
+ /// 区域容器
+ /// 节点控件
+ private void TryPlaceNodeInRegion(NodeControlBase regionControl, NodeControlBase nodeControl)
+ {
+ // 准备放置条件表达式控件
+ if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition)
+ {
+ ConditionRegionControl conditionRegion = regionControl as ConditionRegionControl;
+ if (conditionRegion != null)
+ {
+ // 如果存在条件区域容器
+ conditionRegion.AddCondition(nodeControl);
+ }
+ }
+ }
+
-
///
/// 拖动效果,根据拖放数据是否为指定类型设置拖放效果
///
@@ -1038,6 +1095,7 @@ namespace Serein.WorkBench
}
}
#endregion
+
#region 画布中框选节点控件动作
///
@@ -1120,7 +1178,7 @@ namespace Serein.WorkBench
///
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
{
- if (IsSelectControl && e.LeftButton == MouseButtonState.Pressed)
+ if (IsSelectControl && e.LeftButton == MouseButtonState.Pressed) // 正在选取节点
{
// 获取当前鼠标位置
Point currentPoint = e.GetPosition(FlowChartCanvas);
@@ -1137,7 +1195,8 @@ namespace Serein.WorkBench
SelectionRectangle.Height = height;
}
- if (IsConnecting)
+
+ if (IsConnecting) // 正在连接节点
{
Point position = e.GetPosition(FlowChartCanvas);
if (currentLine == null || startConnectNodeControl == null)
@@ -1149,7 +1208,7 @@ namespace Serein.WorkBench
currentLine.X2 = position.X;
currentLine.Y2 = position.Y;
}
- if (IsCanvasDragging)
+ if (IsCanvasDragging) // 正在移动画布
{
Point currentMousePosition = e.GetPosition(this);
double deltaX = currentMousePosition.X - startPoint.X;
@@ -1160,9 +1219,6 @@ namespace Serein.WorkBench
startPoint = currentMousePosition;
- // AdjustCanvasSizeAndContent(deltaX, deltaY);
-
-
foreach (var line in Connections)
{
line.Refresh();
@@ -1173,6 +1229,7 @@ namespace Serein.WorkBench
}
#endregion
+
#region 拖动画布实现缩放平移效果
private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
@@ -1197,16 +1254,25 @@ 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.ScaleX > 2.0) return;
+ if (e.Delta > 0 && scaleTransform.ScaleY > 1.5) return;
double scale = e.Delta > 0 ? 0.1 : -0.1;
-
scaleTransform.ScaleX += scale;
scaleTransform.ScaleY += scale;
+
}
}
@@ -1215,6 +1281,8 @@ namespace Serein.WorkBench
{
FlowChartCanvas.Width = width;
FlowChartCanvas.Height = height;
+ //FlowChartStackPanel.Width = width;
+ //FlowChartStackPanel.Height = height;
}
@@ -1258,19 +1326,49 @@ namespace Serein.WorkBench
private void Thumb_DragDelta_BottomRight(object sender, DragDeltaEventArgs e)
{
- // 从右下角调整大小
- double newWidth = Math.Max(FlowChartCanvas.ActualWidth + e.HorizontalChange * scaleTransform.ScaleX, 0);
- double newHeight = Math.Max(FlowChartCanvas.ActualHeight + e.VerticalChange * scaleTransform.ScaleY, 0);
+ // 获取缩放后的水平和垂直变化
+ double horizontalChange = e.HorizontalChange * scaleTransform.ScaleX;
+ double verticalChange = e.VerticalChange * scaleTransform.ScaleY;
+ // 计算新的宽度和高度,确保不会小于400
+ double newWidth = Math.Max(FlowChartCanvas.ActualWidth + horizontalChange, 400);
+ double newHeight = Math.Max(FlowChartCanvas.ActualHeight + verticalChange, 400);
-
- newWidth = newWidth < 400 ? 400 : newWidth;
- newHeight = newHeight < 400 ? 400 : newHeight;
-
+ // 更新 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;
+ }
+
+ //// 从右下角调整大小
+ //double newWidth = Math.Max(FlowChartCanvas.ActualWidth + e.HorizontalChange * scaleTransform.ScaleX, 0);
+ //double newHeight = Math.Max(FlowChartCanvas.ActualHeight + e.VerticalChange * scaleTransform.ScaleY, 0);
+
+ //newWidth = newWidth < 400 ? 400 : newWidth;
+ //newHeight = newHeight < 400 ? 400 : newHeight;
+
+ //if (newWidth > 400 && newHeight > 400)
+ //{
+ // FlowChartCanvas.Width = newWidth;
+ // FlowChartCanvas.Height = newHeight;
+
+ // double x = e.HorizontalChange > 0 ? -0.5 : 0.5;
+ // double y = e.VerticalChange > 0 ? -0.5 : 0.5;
+
+ // double deltaX = x * scaleTransform.ScaleX;
+ // double deltaY = y * scaleTransform.ScaleY;
+ // Test(deltaX, deltaY);
+ //}
}
//private void Thumb_DragDelta_Left(object sender, DragDeltaEventArgs e)
@@ -1285,30 +1383,113 @@ 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;
+ //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
+ 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;
+ }
+
}
//private void Thumb_DragDelta_Top(object sender, DragDeltaEventArgs e)
//{
// // 从顶部调整大小
// double newHeight = Math.Max(FlowChartCanvas.ActualHeight - e.VerticalChange, 0);
-
+
// FlowChartCanvas.Height = newHeight;
// Canvas.SetTop(FlowChartCanvas, Canvas.GetTop(FlowChartCanvas) + e.VerticalChange);
//}
private void Thumb_DragDelta_Bottom(object sender, DragDeltaEventArgs e)
{
- // 从底部调整大小
- double newHeight = Math.Max(FlowChartCanvas.ActualHeight + e.VerticalChange * scaleTransform.ScaleY, 0);
- newHeight = newHeight < 400 ? 400 : newHeight;
+ //// 从底部调整大小
+ //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);
+ 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;
+ }
+
+
}
+ private void Test(double deltaX, double deltaY)
+ {
+ translateTransform.X += deltaX;
+ translateTransform.Y += deltaY;
+ //Console.WriteLine((translateTransform.X, translateTransform.Y));
+ }
+
#endregion
#endregion
@@ -1358,7 +1539,8 @@ namespace Serein.WorkBench
///
private void ButtonDebugFlipflopNode_Click(object sender, RoutedEventArgs e)
{
- FlowEnvironment.Exit(); // 在运行平台上点击了退出
+ Console.WriteLine((FlowChartStackPanel.ActualWidth, FlowChartStackPanel.ActualHeight));
+ FlowEnvironment?.Exit(); // 在运行平台上点击了退出
}
///
@@ -1373,26 +1555,24 @@ namespace Serein.WorkBench
var projectData = FlowEnvironment.SaveProject();
projectData.Basic = new Basic
{
- canvas = new FlowCanvas
+ Canvas = new FlowCanvas
{
- lenght = (float)FlowChartCanvas.Width,
- width = (float)FlowChartCanvas.Height,
+ Lenght = (float)FlowChartCanvas.Width,
+ Width = (float)FlowChartCanvas.Height,
},
- versions = "1",
+ Versions = "1",
};
foreach(var node in projectData.Nodes)
{
- var control = new ActionNodeControl(null);// GetControl(node.Guid);
- Point positionRelativeToParent = control.TranslatePoint(new Point(0, 0), FlowChartCanvas);
-
- node.Position = new Position
+
+ if(NodeControls.TryGetValue(node.Guid,out var nodeControl))
{
- X = (float)positionRelativeToParent.X,
- Y = (float)positionRelativeToParent.Y,
- };
+ Point positionRelativeToParent = nodeControl.TranslatePoint(new Point(0, 0), FlowChartCanvas);
+ node.Position = new Position(positionRelativeToParent.X, positionRelativeToParent.Y);
+ }
}
- var projectJsonData = JArray.FromObject(projectData);
+ var projectJsonData = JObject.FromObject(projectData);
var savePath = SaveContentToFile(projectJsonData.ToString());
savePath = System.IO.Path.GetDirectoryName(savePath);
@@ -1696,8 +1876,6 @@ namespace Serein.WorkBench
return Uri.UnescapeDataString(relativeUri.ToString().Replace('/', System.IO.Path.DirectorySeparatorChar));
}
-
-
}
#region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线
@@ -1725,7 +1903,9 @@ namespace Serein.WorkBench
canvas.Children.Add(connection.ArrowPath);
}
+
BezierLineDrawer.UpdateBezierLine(canvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
+
return connection;
}
@@ -1826,7 +2006,8 @@ namespace Serein.WorkBench
public void Refresh()
{
- BsControl.Draw(Canvas, this);
+ // BsControl.Draw(Canvas, this);
+ BezierLineDrawer.UpdateBezierLine(Canvas, Start, End, BezierPath, ArrowPath);
}
}
@@ -1925,6 +2106,7 @@ namespace Serein.WorkBench
arrowGeometry.Figures.Add(arrowFigure);
arrowPath.Data = arrowGeometry;
+
}
// 计算终点落点位置
private static Point CalculateEndpointOutsideElement(FrameworkElement element, Canvas canvas, Point startPoint, out Localhost localhost)
diff --git a/WorkBench/Node/ViewModel/ConditionNodeControlViewModel.cs b/WorkBench/Node/ViewModel/ConditionNodeControlViewModel.cs
index ed48227..4f0ccc5 100644
--- a/WorkBench/Node/ViewModel/ConditionNodeControlViewModel.cs
+++ b/WorkBench/Node/ViewModel/ConditionNodeControlViewModel.cs
@@ -35,9 +35,9 @@ namespace Serein.WorkBench.Node.ViewModel
public ConditionNodeControlViewModel(SingleConditionNode node) : base(node)
{
this.singleConditionNode = node;
- IsCustomData = false;
- CustomData = "";
- Expression = "PASS";
+ //IsCustomData = false;
+ //CustomData = "";
+ //Expression = "PASS";
}
}