mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-11 10:26:34 +08:00
修改了远程环境的节点加载流程、容器节点子节点的位置关系
This commit is contained in:
@@ -1,17 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.FlowRemoteManagement.Model
|
||||
{
|
||||
public class ConnectionInfoData
|
||||
{
|
||||
public bool Op { get; set; }
|
||||
public string? FromNodeGuid { get; set; }
|
||||
public string? ToNodeGuid { get; set; }
|
||||
// None Upstream IsSucceed IsFail IsError
|
||||
public string? Type { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<Version>1.0.0</Version>
|
||||
<TargetFrameworks>net8.0</TargetFrameworks>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Library.Core\Serein.Library.Core.csproj" />
|
||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||
<ProjectReference Include="..\NodeFlow\Serein.NodeFlow.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,167 +0,0 @@
|
||||
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Network.WebSocketCommunication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using Serein.NodeFlow;
|
||||
using Serein.Library.Core.NodeFlow;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.FlowRemoteManagement.Model;
|
||||
using System.Reflection;
|
||||
using Serein.Library.FlowNode;
|
||||
|
||||
namespace SereinFlowRemoteManagement
|
||||
{
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// SereinFlow 远程控制模块
|
||||
/// </summary>
|
||||
[DynamicFlow]
|
||||
[AutoRegister]
|
||||
[AutoSocketModule(ThemeKey ="theme",DataKey ="data")]
|
||||
public class SereinFlowRemoteControl : ISocketHandleModule
|
||||
{
|
||||
public int ServerPort { get; set; } = 7525;
|
||||
|
||||
#region 初始化服务端
|
||||
public Guid HandleGuid { get; } = new Guid();
|
||||
|
||||
private readonly IFlowEnvironment environment;
|
||||
public SereinFlowRemoteControl(IFlowEnvironment environment)
|
||||
{
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Init)]
|
||||
public void Init(IDynamicContext context)
|
||||
{
|
||||
environment.IOC.Register<WebSocketServer>();
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Loading)]
|
||||
public async Task Loading(IDynamicContext context)
|
||||
{
|
||||
environment.IOC.Run<WebSocketServer>(async (socketServer) =>
|
||||
{
|
||||
socketServer.MsgHandleHelper.AddModule(this,
|
||||
(ex, send) =>
|
||||
{
|
||||
send(new
|
||||
{
|
||||
code = 400,
|
||||
ex = ex.Message
|
||||
});
|
||||
});
|
||||
await Console.Out.WriteLineAsync("启动远程管理模块");
|
||||
await socketServer.StartAsync($"http://*:{ServerPort}/");
|
||||
});
|
||||
SereinProjectData projectData = await environment.GetProjectInfoAsync();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 流程运行接口
|
||||
|
||||
/// <summary>
|
||||
/// 连接到运行环境,获取当前的节点信息
|
||||
/// </summary>
|
||||
/// <param name="Send"></param>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle]
|
||||
public async Task<object?> ConnectWorkBench(Func<string, Task> Send)
|
||||
{
|
||||
await Send("尝试获取");
|
||||
|
||||
try
|
||||
{
|
||||
var envInfo = this.environment.GetEnvInfoAsync();
|
||||
return envInfo;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Send(ex.Message);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void AddNode(string nodeType,string methodName,int x, int y)
|
||||
{
|
||||
if(x <= 0 || y <= 0)
|
||||
{
|
||||
throw new InvalidOperationException("坐标错误");
|
||||
}
|
||||
if (!EnumHelper.TryConvertEnum<NodeControlType>(nodeType, out var connectionType))
|
||||
{
|
||||
throw new InvalidOperationException("类型错误");
|
||||
}
|
||||
|
||||
if (this.environment.TryGetMethodDetailsInfo(methodName,out var mdInfo))
|
||||
{
|
||||
this.environment.CreateNode(connectionType, new PositionOfUI(x, y), mdInfo); //
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 远程更改两个节点的连接关系
|
||||
/// </summary>
|
||||
/// <param name="nodeInfo"></param>
|
||||
/// <param name="Send"></param>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
[AutoSocketHandle(ThemeValue = "ConnectionChange")]
|
||||
public void ChangeNodeConnection(ConnectionInfoData nodeInfo, Func<object, Task> Send)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nodeInfo.FromNodeGuid) || string.IsNullOrEmpty(nodeInfo.ToNodeGuid))
|
||||
{
|
||||
throw new InvalidOperationException("Guid错误");
|
||||
}
|
||||
if (!EnumHelper.TryConvertEnum<ConnectionType>(nodeInfo.Type, out var connectionType))
|
||||
{
|
||||
throw new InvalidOperationException("类型错误");
|
||||
}
|
||||
|
||||
if (nodeInfo.Op)
|
||||
{
|
||||
environment.ConnectNodeAsync(nodeInfo.FromNodeGuid, nodeInfo.ToNodeGuid, connectionType);
|
||||
}
|
||||
else
|
||||
{
|
||||
environment.RemoveConnect(nodeInfo.FromNodeGuid, nodeInfo.ToNodeGuid, connectionType);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 远程调用某个节点
|
||||
/// </summary>
|
||||
[AutoSocketHandle(ThemeValue = "InvokeNode")]
|
||||
public async Task InvokeNode(string nodeGuid, Func<object, Task> Send)
|
||||
{
|
||||
if (string.IsNullOrEmpty(nodeGuid))
|
||||
{
|
||||
throw new InvalidOperationException("Guid错误");
|
||||
}
|
||||
|
||||
await environment.StartAsyncInSelectNode(nodeGuid);
|
||||
|
||||
await Send(new
|
||||
{
|
||||
state = 200,
|
||||
tips = "执行完成",
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取项目配置文件信息
|
||||
/// </summary>
|
||||
[AutoSocketHandle(ThemeValue = "GetProjectInfo")]
|
||||
public async Task<SereinProjectData> GetProjectInfo()
|
||||
{
|
||||
await Task.Delay(0);
|
||||
return await environment.GetProjectInfoAsync();
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
Binary file not shown.
@@ -54,10 +54,16 @@ namespace Serein.Library.Api
|
||||
public delegate void NodeCreateHandler(NodeCreateEventArgs eventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// 容器节点与子项节点的关系发生改变
|
||||
/// 节点放置事件
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
public delegate void NodeContainerChildChangeHandler(NodeContainerChildChangeEventArgs eventArgs);
|
||||
public delegate void NodePlaceHandler(NodePlaceEventArgs eventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// 节点取出事件
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
public delegate void NodeTakeOutHandler(NodeTakeOutEventArgs eventArgs);
|
||||
|
||||
/// <summary>
|
||||
/// 环境中流程起始节点发生了改变
|
||||
@@ -314,51 +320,41 @@ namespace Serein.Library.Api
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点父子关系改变
|
||||
/// 节点放置事件参数
|
||||
/// </summary>
|
||||
public class NodeContainerChildChangeEventArgs : FlowEventArgs
|
||||
public class NodePlaceEventArgs : FlowEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// 变更类型
|
||||
/// </summary>
|
||||
public enum Type
|
||||
public NodePlaceEventArgs(string nodeGuid, string containerNodeGuid)
|
||||
{
|
||||
/// <summary>
|
||||
/// 放置
|
||||
/// </summary>
|
||||
Place,
|
||||
/// <summary>
|
||||
/// 取出
|
||||
/// </summary>
|
||||
TakeOut
|
||||
}
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
/// <param name="childNodeGuid">子项节点</param>
|
||||
/// <param name="containerNodeGuid">容器节点</param>
|
||||
/// <param name="state">类别</param>
|
||||
public NodeContainerChildChangeEventArgs(string childNodeGuid, string containerNodeGuid, Type state)
|
||||
{
|
||||
ChildNodeGuid = childNodeGuid;
|
||||
NodeGuid = nodeGuid;
|
||||
ContainerNodeGuid = containerNodeGuid;
|
||||
State = state;
|
||||
}
|
||||
/// <summary>
|
||||
/// 子节点,该数据为此次时间的主节点
|
||||
/// </summary>
|
||||
public string ChildNodeGuid { get; private set; }
|
||||
public string NodeGuid { get; private set; }
|
||||
/// <summary>
|
||||
/// 父节点
|
||||
/// </summary>
|
||||
public string ContainerNodeGuid { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// 改变类型
|
||||
/// </summary>
|
||||
public Type State { get; private set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点取出事件参数
|
||||
/// </summary>
|
||||
public class NodeTakeOutEventArgs : FlowEventArgs
|
||||
{
|
||||
public NodeTakeOutEventArgs(string nodeGuid)
|
||||
{
|
||||
NodeGuid = nodeGuid;
|
||||
}
|
||||
/// <summary>
|
||||
/// 需要取出的节点Guid
|
||||
/// </summary>
|
||||
public string NodeGuid { get; private set; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 环境中移除了一个节点
|
||||
/// </summary>
|
||||
@@ -664,9 +660,14 @@ namespace Serein.Library.Api
|
||||
event NodeRemoveHandler OnNodeRemove;
|
||||
|
||||
/// <summary>
|
||||
/// 节点父子关系发生改变事件
|
||||
/// 节点放置事件
|
||||
/// </summary>
|
||||
event NodeContainerChildChangeHandler OnNodeParentChildChange;
|
||||
event NodePlaceHandler OnNodePlace;
|
||||
|
||||
/// <summary>
|
||||
/// 节点取出事件
|
||||
/// </summary>
|
||||
event NodeTakeOutHandler OnNodeTakeOut;
|
||||
|
||||
/// <summary>
|
||||
/// 起始节点变化事件
|
||||
@@ -740,6 +741,7 @@ namespace Serein.Library.Api
|
||||
/// 启动远程服务
|
||||
/// </summary>
|
||||
Task StartRemoteServerAsync(int port = 7525);
|
||||
|
||||
/// <summary>
|
||||
/// 停止远程服务
|
||||
/// </summary>
|
||||
@@ -781,40 +783,29 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
/// <param name="dllPath"></param>
|
||||
void LoadLibrary(string dllPath);
|
||||
|
||||
/// <summary>
|
||||
/// 移除DLL
|
||||
/// </summary>
|
||||
/// <param name="assemblyFullName">程序集的名称</param>
|
||||
bool UnloadLibrary(string assemblyFullName);
|
||||
bool TryUnloadLibrary(string assemblyFullName);
|
||||
|
||||
/// <summary>
|
||||
/// 清理加载的DLL(待更改)
|
||||
/// </summary>
|
||||
void ClearAll();
|
||||
|
||||
/// <summary>
|
||||
/// 开始运行
|
||||
/// </summary>
|
||||
Task StartAsync();
|
||||
Task<bool> StartFlowAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 从选定的节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
Task StartAsyncInSelectNode(string startNodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 立刻调用某个节点,并获取其返回值
|
||||
/// </summary>
|
||||
/// <param name="context">调用时的上下文</param>
|
||||
/// <param name="nodeGuid">节点Guid</param>
|
||||
/// <returns></returns>
|
||||
Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid);
|
||||
Task<bool> StartAsyncInSelectNode(string startNodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 结束运行
|
||||
/// </summary>
|
||||
void ExitFlow();
|
||||
Task<bool> ExitFlowAsync();
|
||||
|
||||
/// <summary>
|
||||
/// 移动了某个节点(远程插件使用)
|
||||
@@ -827,8 +818,9 @@ namespace Serein.Library.Api
|
||||
/// <summary>
|
||||
/// 设置流程起点节点
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
void SetStartNode(string nodeGuid);
|
||||
/// <param name="nodeGuid">尝试设置为起始节点的节点Guid</param>
|
||||
/// <returns>被设置为起始节点的Guid</returns>
|
||||
Task<string> SetStartNodeAsync(string nodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 在两个节点之间创建连接关系
|
||||
@@ -875,14 +867,27 @@ namespace Serein.Library.Api
|
||||
/// <param name="methodDetailsInfo">节点绑定的方法说明</param>
|
||||
Task<NodeInfo> CreateNodeAsync(NodeControlType nodeType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null);
|
||||
|
||||
///// <summary>
|
||||
///// 将节点放置在容器中/从容器中取出
|
||||
///// </summary>
|
||||
///// <param name="childNodeGuid">子节点(主要节点)</param>
|
||||
///// <param name="parentNodeGuid">父节点</param>
|
||||
///// <param name="isAssembly">是否组合(反之为分解节点组合关系)</param>
|
||||
///// <returns></returns>
|
||||
//Task<bool> ChangeNodeContainerChildAsync(string childNodeGuid,string parentNodeGuid,bool isAssembly);
|
||||
|
||||
/// <summary>
|
||||
/// 将节点放置在容器中/从容器中取出
|
||||
/// 将节点放置在容器中
|
||||
/// </summary>
|
||||
/// <param name="childNodeGuid">子节点(主要节点)</param>
|
||||
/// <param name="parentNodeGuid">父节点</param>
|
||||
/// <param name="isPlace">是否组合(反之为分解节点组合关系)</param>
|
||||
/// <returns></returns>
|
||||
Task<bool> ChangeNodeContainerChild(string childNodeGuid,string parentNodeGuid,bool isAssembly);
|
||||
Task<bool> PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 将节点从容器中脱离
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
Task<bool> TakeOutNodeToContainerAsync(string nodeGuid);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置两个节点某个类型的方法调用关系为优先调用
|
||||
@@ -910,7 +915,6 @@ namespace Serein.Library.Api
|
||||
/// <param name="connectionArgSourceType">参数来源类型</param>
|
||||
Task<bool> RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 移除节点/区域/基础控件
|
||||
/// </summary>
|
||||
@@ -1035,6 +1039,13 @@ namespace Serein.Library.Api
|
||||
/// <param name="type">中断类型。0主动监视,1表达式</param>
|
||||
void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type);
|
||||
|
||||
/// <summary>
|
||||
/// 立刻调用某个节点,并获取其返回值
|
||||
/// </summary>
|
||||
/// <param name="context">调用时的上下文</param>
|
||||
/// <param name="nodeGuid">节点Guid</param>
|
||||
/// <returns></returns>
|
||||
Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid);
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -105,12 +105,12 @@ namespace Serein.Library
|
||||
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> SuccessorNodes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 该节点的父级节点(容器)
|
||||
/// 该节点的容器节点
|
||||
/// </summary>
|
||||
public NodeModelBase ParentNode { get; set; } = null;
|
||||
public NodeModelBase ContainerNode { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// 该节点的子项节点(容器)
|
||||
/// 该节点的子项节点(如果该节点是容器节点,那就会有这个参数)
|
||||
/// </summary>
|
||||
public List<NodeModelBase> ChildrenNode { get; }
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ namespace Serein.Library
|
||||
IsProtectionParameter = this.MethodDetails.IsProtectionParameter,
|
||||
IsInterrupt = this.DebugSetting.IsInterrupt,
|
||||
IsEnable = this.DebugSetting.IsEnable,
|
||||
ParentNodeGuid = ParentNode?.Guid,
|
||||
ParentNodeGuid = ContainerNode?.Guid,
|
||||
ChildNodeGuids = ChildrenNode.Select(item => item.Guid).ToArray(),
|
||||
};
|
||||
nodeInfo.Position.X = Math.Round(nodeInfo.Position.X, 1);
|
||||
|
||||
@@ -124,6 +124,16 @@ namespace Serein.Library.Utils
|
||||
{
|
||||
SereinEnv.environment.WriteLine(type,message,@class);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 输出异常信息
|
||||
/// </summary>
|
||||
/// <param name="ex"></param>
|
||||
/// <param name="class"></param>
|
||||
public static void WriteLine(Exception ex, InfoClass @class = InfoClass.General)
|
||||
{
|
||||
SereinEnv.environment.WriteLine(InfoType.ERROR, ex.ToString(), @class);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -38,6 +38,14 @@
|
||||
/// </summary>
|
||||
public const string RemoveNode = nameof(RemoveNode);
|
||||
/// <summary>
|
||||
/// 尝试放置节点
|
||||
/// </summary>
|
||||
public const string PlaceNode = nameof(PlaceNode);
|
||||
/// <summary>
|
||||
/// 尝试取出节点
|
||||
/// </summary>
|
||||
public const string TakeOutNode = nameof(TakeOutNode);
|
||||
/// <summary>
|
||||
/// 尝试连接两个节点的方法调用关系
|
||||
/// </summary>
|
||||
public const string ConnectInvokeNode = nameof(ConnectInvokeNode);
|
||||
|
||||
@@ -152,9 +152,14 @@ namespace Serein.NodeFlow.Env
|
||||
public event NodeRemoveHandler? OnNodeRemove;
|
||||
|
||||
/// <summary>
|
||||
/// 节点父子关系发生改变事件
|
||||
/// 节点放置事件
|
||||
/// </summary>
|
||||
public event NodeContainerChildChangeHandler OnNodeParentChildChange;
|
||||
public event NodePlaceHandler OnNodePlace;
|
||||
|
||||
/// <summary>
|
||||
/// 节点取出事件
|
||||
/// </summary>
|
||||
public event NodeTakeOutHandler OnNodeTakeOut;
|
||||
|
||||
/// <summary>
|
||||
/// 起始节点变化事件
|
||||
@@ -350,15 +355,6 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
#region 环境对外接口
|
||||
|
||||
///// <summary>
|
||||
///// 重定向Console输出
|
||||
///// </summary>
|
||||
//public void SetConsoleOut()
|
||||
//{
|
||||
// var logTextWriter = new LogTextWriter(msg => Output(msg));
|
||||
// Console.SetOut(logTextWriter);
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 输出信息
|
||||
/// </summary>
|
||||
@@ -375,25 +371,11 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
///// 使用JSON处理库输出对象信息
|
||||
///// </summary>
|
||||
///// <param name="obj"></param>
|
||||
//public void WriteLineObjToJson(object obj)
|
||||
//{
|
||||
// var msg = JsonConvert.SerializeObject(obj);
|
||||
// if (OperatingSystem.IsWindows())
|
||||
// {
|
||||
// UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg + Environment.NewLine));
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 异步运行
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task StartAsync()
|
||||
public async Task<bool> StartFlowAsync()
|
||||
{
|
||||
ChannelFlowInterrupt?.CancelAllTasks();
|
||||
flowStarter = new FlowStarter();
|
||||
@@ -416,13 +398,16 @@ namespace Serein.NodeFlow.Env
|
||||
// 注册封装好的UI线程上下文
|
||||
IOC.CustomRegisterInstance(typeof(UIContextOperation).FullName, this.UIContextOperation, false);
|
||||
}
|
||||
|
||||
await flowStarter.RunAsync(this, nodes, autoRegisterTypes, initMethods, loadMethods, exitMethods);
|
||||
|
||||
if (FlipFlopState == RunState.Completion)
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
ExitFlow(); // 未运行触发器时,才会调用结束方法
|
||||
}
|
||||
await flowStarter.RunAsync(this, nodes, autoRegisterTypes, initMethods, loadMethods, exitMethods);
|
||||
if (FlipFlopState == RunState.Completion)
|
||||
{
|
||||
await ExitFlowAsync(); // 未运行触发器时,才会调用结束方法
|
||||
}
|
||||
});
|
||||
return true;
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -431,20 +416,20 @@ namespace Serein.NodeFlow.Env
|
||||
/// </summary>
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task StartAsyncInSelectNode(string startNodeGuid)
|
||||
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid)
|
||||
{
|
||||
|
||||
if (flowStarter is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, "没有启动流程,无法运行单个节点");
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (true || FlowState == RunState.Running || FlipFlopState == RunState.Running)
|
||||
{
|
||||
NodeModelBase? nodeModel = GuidToModel(startNodeGuid);
|
||||
if (nodeModel is null || nodeModel is SingleFlipflopNode)
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
//var getExp = "@get .DebugSetting.IsEnable";
|
||||
//var getExpResult1 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel,out _);
|
||||
@@ -453,10 +438,11 @@ namespace Serein.NodeFlow.Env
|
||||
//var getExpResult2 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _);
|
||||
|
||||
await flowStarter.StartFlowInSelectNodeAsync(this, nodeModel);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -478,21 +464,20 @@ namespace Serein.NodeFlow.Env
|
||||
/// <summary>
|
||||
/// 结束流程
|
||||
/// </summary>
|
||||
public void ExitFlow()
|
||||
public Task<bool> ExitFlowAsync()
|
||||
{
|
||||
ChannelFlowInterrupt?.CancelAllTasks();
|
||||
flowStarter?.Exit();
|
||||
UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs()));
|
||||
flowStarter = null;
|
||||
GC.Collect();
|
||||
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 激活全局触发器
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
// [AutoSocketHandle]
|
||||
public void ActivateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
var nodeModel = GuidToModel(nodeGuid);
|
||||
@@ -512,7 +497,6 @@ namespace Serein.NodeFlow.Env
|
||||
/// 关闭全局触发器
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
// [AutoSocketHandle]
|
||||
public void TerminateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
var nodeModel = GuidToModel(nodeGuid);
|
||||
@@ -541,16 +525,6 @@ namespace Serein.NodeFlow.Env
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除所有
|
||||
/// </summary>
|
||||
public void ClearAll()
|
||||
{
|
||||
//LoadedAssemblyPaths.Clear();
|
||||
//NodeLibrarys.Clear();
|
||||
//MethodDetailss.Clear();
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存项目
|
||||
@@ -582,7 +556,7 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
_ = LoadNodeInfosAsync(projectData.Nodes.ToList());
|
||||
SetStartNode(projectData.StartNode);
|
||||
SetStartNodeAsync(projectData.StartNode);
|
||||
|
||||
}
|
||||
|
||||
@@ -686,7 +660,7 @@ namespace Serein.NodeFlow.Env
|
||||
/// </summary>
|
||||
/// <param name="assemblyName"></param>
|
||||
/// <returns></returns>
|
||||
public bool UnloadLibrary(string assemblyName)
|
||||
public bool TryUnloadLibrary(string assemblyName)
|
||||
{
|
||||
// 获取与此程序集相关的节点
|
||||
var groupedNodes = NodeModels.Values.Where(node => node.MethodDetails.AssemblyName.Equals(assemblyName)).ToArray();
|
||||
@@ -773,7 +747,6 @@ namespace Serein.NodeFlow.Env
|
||||
/// <returns></returns>
|
||||
public Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos)
|
||||
{
|
||||
List<NodeInfo> needPlaceNodeInfos = [];
|
||||
#region 从NodeInfo创建NodeModel
|
||||
foreach (NodeInfo? nodeInfo in nodeInfos)
|
||||
{
|
||||
@@ -805,27 +778,36 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
nodeModel.LoadInfo(nodeInfo); // 创建节点model
|
||||
TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
|
||||
if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) &&
|
||||
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
|
||||
{
|
||||
needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点
|
||||
}
|
||||
|
||||
UIContextOperation?.Invoke(() =>
|
||||
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); // 添加到UI上
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 重新放置节点
|
||||
foreach (NodeInfo nodeInfo in needPlaceNodeInfos)
|
||||
|
||||
List<NodeInfo> needPlaceNodeInfos = [];
|
||||
foreach (NodeInfo? nodeInfo in nodeInfos)
|
||||
{
|
||||
if (NodeModels.TryGetValue(nodeInfo.Guid, out var childNode) &&
|
||||
if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) &&
|
||||
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
|
||||
{
|
||||
childNode.ParentNode = parentNode;
|
||||
parentNode.ChildrenNode.Add(childNode);
|
||||
UIContextOperation?.Invoke(() => OnNodeParentChildChange?.Invoke(
|
||||
new NodeContainerChildChangeEventArgs(childNode.Guid, parentNode.Guid,
|
||||
NodeContainerChildChangeEventArgs.Type.Place)));
|
||||
needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点
|
||||
}
|
||||
}
|
||||
|
||||
foreach (NodeInfo nodeInfo in needPlaceNodeInfos)
|
||||
{
|
||||
if (NodeModels.TryGetValue(nodeInfo.Guid, out var nodeMoel) &&
|
||||
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var containerNode)
|
||||
&& containerNode is INodeContainer nodeContainer)
|
||||
{
|
||||
nodeMoel.ContainerNode = containerNode; // 放置节点
|
||||
containerNode.ChildrenNode.Add(nodeMoel);
|
||||
nodeContainer.PlaceNode(nodeMoel);
|
||||
|
||||
UIContextOperation?.Invoke(() => OnNodePlace?.Invoke(
|
||||
new NodePlaceEventArgs(nodeMoel.Guid, containerNode.Guid)));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -935,40 +917,57 @@ namespace Serein.NodeFlow.Env
|
||||
return Task.FromResult(nodeInfo);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将节点放置在容器中/从容器中取出
|
||||
/// 将节点放置在容器中
|
||||
/// </summary>
|
||||
/// <param name="childNodeGuid">子节点(主要节点)</param>
|
||||
/// <param name="parentNodeGuid">父节点</param>
|
||||
/// <param name="isPlace">是否组合(反之为分解节点组合关系)</param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> ChangeNodeContainerChild(string childNodeGuid, string parentNodeGuid, bool isPlace)
|
||||
public async Task<bool> PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid)
|
||||
{
|
||||
// 获取起始节点与目标节点
|
||||
var childNode = GuidToModel(childNodeGuid);
|
||||
var parentNode = GuidToModel(parentNodeGuid);
|
||||
if (childNode is null || parentNode is null || parentNode is not INodeContainer nodeContainer) return false;
|
||||
// 获取目标节点与容器节点
|
||||
var nodeMoel = GuidToModel(nodeGuid);
|
||||
if (nodeMoel is null ) return false;
|
||||
|
||||
if (isPlace)
|
||||
if(nodeMoel.ContainerNode is INodeContainer tmpContainer)
|
||||
{
|
||||
// 放置节点
|
||||
parentNode.ChildrenNode.Add(childNode);
|
||||
childNode.ParentNode = parentNode;
|
||||
nodeContainer.PlaceNode(childNode);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 取出节点
|
||||
parentNode.ChildrenNode.Remove(childNode);
|
||||
childNode.ParentNode = null;
|
||||
nodeContainer.TakeOutNode(childNode);
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"节点放置失败,节点[{nodeGuid}]已经放置于容器节点[{((NodeModelBase)tmpContainer).Guid}]");
|
||||
return false;
|
||||
}
|
||||
|
||||
OnNodeParentChildChange?.Invoke(new NodeContainerChildChangeEventArgs(childNodeGuid, parentNodeGuid,
|
||||
isPlace ? NodeContainerChildChangeEventArgs.Type.Place : NodeContainerChildChangeEventArgs.Type.TakeOut));
|
||||
var containerNode = GuidToModel(containerNodeGuid);
|
||||
if (containerNode is not INodeContainer nodeContainer) return false;
|
||||
|
||||
nodeMoel.ContainerNode = containerNode; // 放置节点
|
||||
containerNode.ChildrenNode.Add(nodeMoel);
|
||||
nodeContainer.PlaceNode(nodeMoel);
|
||||
OnNodePlace?.Invoke(new NodePlaceEventArgs(nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将节点从容器节点中脱离
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> TakeOutNodeToContainerAsync(string nodeGuid)
|
||||
{
|
||||
// 获取目标节点与容器节点
|
||||
var nodeMoel = GuidToModel(nodeGuid);
|
||||
if (nodeMoel is null) return false;
|
||||
|
||||
if(nodeMoel.ContainerNode is not INodeContainer nodeContainer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
nodeContainer.TakeOutNode(nodeMoel); // 从容器节点取出
|
||||
nodeMoel.ContainerNode = null; // 取消映射关系
|
||||
|
||||
OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(nodeGuid)); // 重新放置在画布上
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 移除节点
|
||||
/// </summary>
|
||||
@@ -1232,11 +1231,13 @@ namespace Serein.NodeFlow.Env
|
||||
/// 设置起点控件
|
||||
/// </summary>
|
||||
/// <param name="newNodeGuid"></param>
|
||||
public void SetStartNode(string newNodeGuid)
|
||||
public Task<string> SetStartNodeAsync(string newNodeGuid)
|
||||
{
|
||||
var newStartNodeModel = GuidToModel(newNodeGuid);
|
||||
if (newStartNodeModel is null) return;
|
||||
if (newStartNodeModel is null)
|
||||
return Task.FromResult(StartNode?.Guid ?? string.Empty);
|
||||
SetStartNode(newStartNodeModel);
|
||||
return Task.FromResult(StartNode?.Guid ?? string.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -1893,11 +1894,11 @@ namespace Serein.NodeFlow.Env
|
||||
{
|
||||
var oldNodeGuid = StartNode?.Guid;
|
||||
StartNode = newStartNode;
|
||||
if (OperatingSystem.IsWindows())
|
||||
{
|
||||
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid)));
|
||||
}
|
||||
|
||||
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid)));
|
||||
|
||||
//if (OperatingSystem.IsWindows())
|
||||
//{
|
||||
// }
|
||||
}
|
||||
|
||||
///// <summary>
|
||||
@@ -1910,7 +1911,7 @@ namespace Serein.NodeFlow.Env
|
||||
// {
|
||||
// UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg));
|
||||
// }
|
||||
|
||||
|
||||
//}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -3,6 +3,7 @@ using Serein.Library.Api;
|
||||
using Serein.Library.FlowNode;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Serein.NodeFlow.Env
|
||||
{
|
||||
@@ -121,13 +122,16 @@ namespace Serein.NodeFlow.Env
|
||||
remove { currentFlowEnvironment.OnNodeRemove -= value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点父子关系发生改变事件
|
||||
/// </summary>
|
||||
public event NodeContainerChildChangeHandler OnNodeParentChildChange
|
||||
public event NodePlaceHandler OnNodePlace
|
||||
{
|
||||
add { currentFlowEnvironment.OnNodeParentChildChange += value; }
|
||||
remove { currentFlowEnvironment.OnNodeParentChildChange -= value; }
|
||||
add { currentFlowEnvironment.OnNodePlace += value; }
|
||||
remove { currentFlowEnvironment.OnNodePlace -= value; }
|
||||
}
|
||||
|
||||
public event NodeTakeOutHandler OnNodeTakeOut
|
||||
{
|
||||
add { currentFlowEnvironment.OnNodeTakeOut += value; }
|
||||
remove { currentFlowEnvironment.OnNodeTakeOut -= value; }
|
||||
}
|
||||
|
||||
public event StartNodeChangeHandler OnStartNodeChange
|
||||
@@ -203,10 +207,6 @@ namespace Serein.NodeFlow.Env
|
||||
return await currentFlowEnvironment.CheckObjMonitorStateAsync(key);
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
currentFlowEnvironment.ClearAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 在两个节点之间创建连接关系
|
||||
@@ -287,27 +287,35 @@ namespace Serein.NodeFlow.Env
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将节点放置在容器中/从容器中取出
|
||||
/// 将节点放置在容器中
|
||||
/// </summary>
|
||||
/// <param name="childNodeGuid">子节点(主要节点)</param>
|
||||
/// <param name="parentNodeGuid">父节点</param>
|
||||
/// <param name="isPlace">是否组合(反之为分解节点组合关系)</param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> ChangeNodeContainerChild(string childNodeGuid, string parentNodeGuid, bool isAssembly)
|
||||
public async Task<bool> PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid)
|
||||
{
|
||||
SetProjectLoadingFlag(false);
|
||||
var result = await currentFlowEnvironment.ChangeNodeContainerChild(childNodeGuid, parentNodeGuid, isAssembly); // 装饰器调用
|
||||
var result = await currentFlowEnvironment.PlaceNodeToContainerAsync(nodeGuid, containerNodeGuid); // 装饰器调用
|
||||
SetProjectLoadingFlag(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将节点从容器中脱离
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> TakeOutNodeToContainerAsync(string nodeGuid)
|
||||
{
|
||||
SetProjectLoadingFlag(false);
|
||||
var result = await currentFlowEnvironment.TakeOutNodeToContainerAsync(nodeGuid); // 装饰器调用
|
||||
SetProjectLoadingFlag(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void ExitFlow()
|
||||
public async Task<bool> ExitFlowAsync()
|
||||
{
|
||||
currentFlowEnvironment.ExitFlow();
|
||||
return await currentFlowEnvironment.ExitFlowAsync();
|
||||
}
|
||||
|
||||
public void ExitRemoteEnv()
|
||||
@@ -369,9 +377,9 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
|
||||
public bool UnloadLibrary(string assemblyName)
|
||||
public bool TryUnloadLibrary(string assemblyName)
|
||||
{
|
||||
return currentFlowEnvironment.UnloadLibrary(assemblyName);
|
||||
return currentFlowEnvironment.TryUnloadLibrary(assemblyName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -441,19 +449,19 @@ namespace Serein.NodeFlow.Env
|
||||
return await currentFlowEnvironment.SetNodeInterruptAsync(nodeGuid, isInterrupt);
|
||||
}
|
||||
|
||||
public void SetStartNode(string nodeGuid)
|
||||
public async Task<string> SetStartNodeAsync(string nodeGuid)
|
||||
{
|
||||
currentFlowEnvironment.SetStartNode(nodeGuid);
|
||||
return await currentFlowEnvironment.SetStartNodeAsync(nodeGuid);
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
public async Task<bool> StartFlowAsync()
|
||||
{
|
||||
await currentFlowEnvironment.StartAsync();
|
||||
return await currentFlowEnvironment.StartFlowAsync();
|
||||
}
|
||||
|
||||
public async Task StartAsyncInSelectNode(string startNodeGuid)
|
||||
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid)
|
||||
{
|
||||
await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid);
|
||||
return await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid);
|
||||
}
|
||||
|
||||
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
||||
|
||||
@@ -48,7 +48,6 @@ namespace Serein.NodeFlow.Env
|
||||
/// <exception cref="NotImplementedException">超时触发</exception>
|
||||
public async Task SendAsync(string theme, object? data = null, int overtimeInMs = 100)
|
||||
{
|
||||
|
||||
var msgId = MsgIdHelper.GenerateId().ToString();
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"[{msgId}] => {theme}");
|
||||
await SendCommandAsync(msgId, theme, data); // 客户端发送消息
|
||||
@@ -62,12 +61,17 @@ namespace Serein.NodeFlow.Env
|
||||
public async Task<TResult> SendAndWaitDataAsync<TResult>(string theme, object? data = null, int overtimeInMs = 50)
|
||||
{
|
||||
var msgId = MsgIdHelper.GenerateId().ToString();
|
||||
//_ = Task.Run(async () =>
|
||||
//{
|
||||
// await Task.Delay(500);
|
||||
//});
|
||||
await SendCommandAsync(msgId, theme, data); // 客户端发送消息
|
||||
return (await remoteFlowEnvironment.WaitTriggerAsync<TResult>(msgId)).Value;
|
||||
_ = SendCommandAsync(msgId, theme, data); // 客户端发送消息
|
||||
var result = await remoteFlowEnvironment.WaitTriggerAsync<TResult>(msgId);
|
||||
if (result.Type == TriggerDescription.Overtime)
|
||||
{
|
||||
throw new Exception($"主题【{theme}】异常,服务端未响应");
|
||||
}
|
||||
else if (result.Type == TriggerDescription.TypeInconsistency)
|
||||
{
|
||||
throw new Exception($"主题【{theme}】异常,服务端返回数据类型与预期不一致{result.Value?.GetType()}");
|
||||
}
|
||||
return result.Value;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,12 +100,66 @@ namespace Serein.NodeFlow.Env
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, sereinProjectData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 开始流程
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="state"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlow, IsReturnValue = false)]
|
||||
public void StartFlow([UseMsgId] string msgId, bool state)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||
}
|
||||
/// <summary>
|
||||
/// 结束流程
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="state"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ExitFlow, IsReturnValue = false)]
|
||||
public void ExitFlow([UseMsgId] string msgId, bool state)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置了某个节点为起始节点
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="nodeGuid">节点Guid</param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.SetStartNode, IsReturnValue = false)]
|
||||
public void SetStartNode([UseMsgId] string msgId, string nodeGuid)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, nodeGuid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从某个节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="state"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlowInSelectNode, IsReturnValue = false)]
|
||||
public void StartFlowInSelectNode([UseMsgId] string msgId, bool state)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置节点的中断
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.SetNodeInterrupt, IsReturnValue = false)]
|
||||
public void SetNodeInterrupt([UseMsgId] string msgId)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync<object>(msgId, null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加中断监视表达式
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.AddInterruptExpression, IsReturnValue = false)]
|
||||
public void AddInterruptExpression([UseMsgId] string msgId)
|
||||
{
|
||||
@@ -109,43 +167,77 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 创建节点
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="nodeInfo"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.CreateNode, IsReturnValue = false)]
|
||||
public void CreateNode([UseMsgId] string msgId, [UseData] NodeInfo nodeInfo)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, nodeInfo);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除节点
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="state"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveNode, IsReturnValue = false)]
|
||||
public void RemoveNode([UseMsgId] string msgId, bool state)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建节点之间的调用关系
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="state"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectInvokeNode, IsReturnValue = false)]
|
||||
public void ConnectInvokeNode([UseMsgId] string msgId, bool state)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除节点之间的调用关系
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="state"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveInvokeConnect, IsReturnValue = false)]
|
||||
public void RemoveInvokeConnect([UseMsgId] string msgId, bool state)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建节点之间参数获取关系
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="state"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ConnectArgSourceNode, IsReturnValue = false)]
|
||||
public void ConnectArgSourceNode([UseMsgId] string msgId, bool state)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除节点之间参数获取关系
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="state"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveArgSourceConnect, IsReturnValue = false)]
|
||||
public void RemoveArgSourceConnect([UseMsgId] string msgId, bool state)
|
||||
{
|
||||
_ = remoteFlowEnvironment.InvokeTriggerAsync(msgId, state);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 改变参数
|
||||
/// </summary>
|
||||
/// <param name="msgId"></param>
|
||||
/// <param name="state"></param>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ChangeParameter, IsReturnValue = false)]
|
||||
public void ChangeParameter([UseMsgId] string msgId, bool state)
|
||||
{
|
||||
@@ -153,8 +245,6 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
@@ -172,10 +172,14 @@ namespace Serein.NodeFlow.Env
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlow)]
|
||||
private async Task StartAsync()
|
||||
private async Task<object> StartAsync()
|
||||
{
|
||||
var uiContextOperation = environment.IOC.Get<UIContextOperation>();
|
||||
await environment.StartAsync();
|
||||
var state = await environment.StartFlowAsync();
|
||||
return new
|
||||
{
|
||||
state = state,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -184,19 +188,26 @@ namespace Serein.NodeFlow.Env
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlowInSelectNode)]
|
||||
private async Task StartAsyncInSelectNode(string nodeGuid)
|
||||
private async Task<object> StartAsyncInSelectNode(string nodeGuid)
|
||||
{
|
||||
await environment.StartAsyncInSelectNode(nodeGuid);
|
||||
var state = await environment.StartAsyncInSelectNode(nodeGuid);
|
||||
return new
|
||||
{
|
||||
state = state,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 结束流程
|
||||
/// </summary>
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.ExitFlow)]
|
||||
private void ExitFlow()
|
||||
private async Task<object> ExitFlow()
|
||||
{
|
||||
environment.ExitFlow();
|
||||
|
||||
var state = await environment.ExitFlowAsync();
|
||||
return new
|
||||
{
|
||||
state = state,
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -519,7 +530,7 @@ namespace Serein.NodeFlow.Env
|
||||
[AutoSocketHandle(ThemeValue = EnvMsgTheme.SetStartNode)]
|
||||
public void SetStartNode(string nodeGuid)
|
||||
{
|
||||
environment.SetStartNode(nodeGuid);
|
||||
environment.SetStartNodeAsync(nodeGuid);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -55,10 +55,8 @@ namespace Serein.NodeFlow.Env
|
||||
public event NodeConnectChangeHandler OnNodeConnectChange;
|
||||
public event NodeCreateHandler OnNodeCreate;
|
||||
public event NodeRemoveHandler OnNodeRemove;
|
||||
/// <summary>
|
||||
/// 节点父子关系发生改变事件
|
||||
/// </summary>
|
||||
public event NodeContainerChildChangeHandler OnNodeParentChildChange;
|
||||
public event NodePlaceHandler OnNodePlace;
|
||||
public event NodeTakeOutHandler OnNodeTakeOut;
|
||||
public event StartNodeChangeHandler OnStartNodeChange;
|
||||
public event FlowRunCompleteHandler OnFlowRunComplete;
|
||||
public event MonitorObjectChangeHandler OnMonitorObjectChange;
|
||||
@@ -117,11 +115,6 @@ namespace Serein.NodeFlow.Env
|
||||
OnEnvOut?.Invoke(type, message);
|
||||
}
|
||||
|
||||
public void WriteLineObjToJson(object obj)
|
||||
{
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:WriteLineObjToJson");
|
||||
}
|
||||
|
||||
public async Task StartRemoteServerAsync(int port = 7525)
|
||||
{
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:StartRemoteServerAsync");
|
||||
@@ -133,10 +126,14 @@ namespace Serein.NodeFlow.Env
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:StopRemoteServer");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取远程环境
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<SereinProjectData> GetProjectInfoAsync()
|
||||
{
|
||||
var prjectInfo = await msgClient.SendAndWaitDataAsync<SereinProjectData>(EnvMsgTheme.GetProjectInfo); // 等待服务器返回项目信息
|
||||
return prjectInfo;
|
||||
var projectData = await msgClient.SendAndWaitDataAsync<SereinProjectData>(EnvMsgTheme.GetProjectInfo); // 等待服务器返回项目信息
|
||||
return projectData;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -176,9 +173,9 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
_ = LoadNodeInfosAsync(flowEnvInfo.Project.Nodes.ToList());
|
||||
SetStartNode(flowEnvInfo.Project.StartNode); // 设置流程起点
|
||||
|
||||
LoadNodeInfos(flowEnvInfo.Project.Nodes.ToList()); // 加载节点
|
||||
_ = SetStartNodeAsync(flowEnvInfo.Project.StartNode); // 设置流程起点
|
||||
UIContextOperation?.Invoke(() =>
|
||||
{
|
||||
OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()); // 加载完成
|
||||
@@ -341,87 +338,95 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
private bool TryAddNode(NodeModelBase nodeModel)
|
||||
{
|
||||
//nodeModel.Guid ??= Guid.NewGuid().ToString();
|
||||
NodeModels[nodeModel.Guid] = nodeModel;
|
||||
|
||||
// 如果是触发器,则需要添加到专属集合中
|
||||
//if (nodeModel is SingleFlipflopNode flipflopNode)
|
||||
//{
|
||||
// var guid = flipflopNode.Guid;
|
||||
// if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid)))
|
||||
// {
|
||||
// FlipflopNodes.Add(flipflopNode);
|
||||
// }
|
||||
//}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 从远程环境获取项目信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<FlowEnvInfo> GetEnvInfoAsync()
|
||||
{
|
||||
var envInfo = await msgClient.SendAndWaitDataAsync<FlowEnvInfo>(EnvMsgTheme.GetEnvInfo);
|
||||
return envInfo;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 连接到远程环境
|
||||
/// </summary>
|
||||
/// <param name="addres"></param>
|
||||
/// <param name="port"></param>
|
||||
/// <param name="token"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token)
|
||||
{
|
||||
await Console.Out.WriteLineAsync("远程环境尚未实现的接口:ConnectRemoteEnv");
|
||||
return (false, null);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 退出远程环境
|
||||
/// </summary>
|
||||
public void ExitRemoteEnv()
|
||||
{
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:ExitRemoteEnv");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// (待更新)加载类库
|
||||
/// </summary>
|
||||
/// <param name="dllPath"></param>
|
||||
public void LoadLibrary(string dllPath)
|
||||
{
|
||||
// 将dll文件发送到远程环境,由远程环境进行加载
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:LoadDll");
|
||||
}
|
||||
|
||||
public bool UnloadLibrary(string assemblyName)
|
||||
/// <summary>
|
||||
/// (待更新)卸载类库
|
||||
/// </summary>
|
||||
/// <param name="assemblyName"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryUnloadLibrary(string assemblyName)
|
||||
{
|
||||
// 尝试移除远程环境中的加载了的依赖
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:RemoteDll");
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ClearAll()
|
||||
{
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:ClearAll");
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
/// <summary>
|
||||
/// 启动远程环境的流程
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> StartFlowAsync()
|
||||
{
|
||||
// 远程环境下不需要UI上下文
|
||||
await msgClient.SendAsync(EnvMsgTheme.StartFlow);
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.StartFlow);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task StartAsyncInSelectNode(string startNodeGuid)
|
||||
/// <summary>
|
||||
/// 从选定的节点开始运行
|
||||
/// </summary>
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid)
|
||||
{
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.StartFlowInSelectNode, new
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.StartFlowInSelectNode, new
|
||||
{
|
||||
nodeGuid = startNodeGuid
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
public async void ExitFlow()
|
||||
/// <summary>
|
||||
/// 结束远程环境的流程运行
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> ExitFlowAsync()
|
||||
{
|
||||
await msgClient.SendAsync(EnvMsgTheme.ExitFlow, null);
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ExitFlow, null);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移动节点,通知远程环境也一起移动,保持相对位置一致
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="x"></param>
|
||||
/// <param name="y"></param>
|
||||
public void MoveNode(string nodeGuid, double x, double y)
|
||||
{
|
||||
//UIContextOperation?.Invoke(() =>
|
||||
@@ -443,26 +448,26 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SetStartNode(string nodeGuid)
|
||||
|
||||
/// <summary>
|
||||
/// 设置远程环境的流程起点节点
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid">尝试设置为起始节点的节点Guid</param>
|
||||
/// <returns>被设置为起始节点的Guid</returns>
|
||||
public async Task<string> SetStartNodeAsync(string nodeGuid)
|
||||
{
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.SetStartNode, new
|
||||
var newNodeGuid = await msgClient.SendAndWaitDataAsync<string>(EnvMsgTheme.SetStartNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid,nodeGuid)));
|
||||
}
|
||||
|
||||
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
||||
{
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现接口 InvokeNodeAsync");
|
||||
//_ = msgClient.SendAsync(EnvMsgTheme.InvokeNodeAsync, new
|
||||
//{
|
||||
// nodeGuid
|
||||
//});
|
||||
return null;
|
||||
if (NodeModels.TryGetValue(newNodeGuid, out var nodeModel)) // 存在节点
|
||||
{
|
||||
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid, newNodeGuid)));
|
||||
}
|
||||
return newNodeGuid;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 在两个节点之间创建方法调用关系
|
||||
/// </summary>
|
||||
@@ -599,7 +604,7 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置两个节点某个类型的方法调用关系为优先调用
|
||||
/// (待更新)设置两个节点某个类型的方法调用关系为优先调用
|
||||
/// </summary>
|
||||
/// <param name="fromNodeGuid">起始节点</param>
|
||||
/// <param name="toNodeGuid">目标节点</param>
|
||||
@@ -610,6 +615,7 @@ namespace Serein.NodeFlow.Env
|
||||
this.WriteLine(InfoType.WARN, "远程环境尚未实现的接口(重要,会尽快实现):SetConnectPriorityInvoke");
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除两个节点之间的方法调用关系
|
||||
/// </summary>
|
||||
@@ -637,6 +643,7 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 移除连接节点之间参数传递的关系
|
||||
/// </summary>
|
||||
@@ -670,13 +677,369 @@ namespace Serein.NodeFlow.Env
|
||||
/// <summary>
|
||||
/// 从节点信息集合批量加载节点控件
|
||||
/// </summary>
|
||||
/// <param name="List<NodeInfo>">节点信息</param>
|
||||
/// <param name="position">需要加载的位置</param>
|
||||
/// <param name="nodeInfos">节点信息</param>
|
||||
/// <returns></returns>
|
||||
public async Task LoadNodeInfosAsync(List<NodeInfo> nodeInfos)
|
||||
{
|
||||
List<NodeInfo> needPlaceNodeInfos = [];
|
||||
if (IsLoadingProject || IsLoadingNode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
List<NodeInfo> loadSuuccessNodes = new List<NodeInfo>(); // 加载成功的节点信息
|
||||
List<NodeInfo> loadFailureNodes = new List<NodeInfo>(); // 加载失败的节点信息
|
||||
List<NodeInfo> needPlaceNodeInfos = new List<NodeInfo>(); // 需要重新放置的节点
|
||||
|
||||
#region 尝试从节点信息加载节点
|
||||
foreach (NodeInfo? nodeInfo in nodeInfos)
|
||||
{
|
||||
if (!EnumHelper.TryConvertEnum<NodeControlType>(nodeInfo.Type, out var controlType))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
NodeInfo newNodeInfo;
|
||||
try
|
||||
{
|
||||
if (!string.IsNullOrEmpty(nodeInfo.MethodName))
|
||||
{
|
||||
if (!MethodDetailss.TryGetValue(nodeInfo.MethodName, out var methodDetails))
|
||||
{
|
||||
loadFailureNodes.Add(nodeInfo);
|
||||
continue; // 有方法名称,但本地没有缓存的相关方法信息,跳过
|
||||
}
|
||||
// 加载远程环境时尝试获取方法信息
|
||||
newNodeInfo = await CreateNodeAsync(controlType, nodeInfo.Position, methodDetails.ToInfo());
|
||||
}
|
||||
else
|
||||
{
|
||||
newNodeInfo = await CreateNodeAsync(controlType, nodeInfo.Position);
|
||||
}
|
||||
loadSuuccessNodes.Add(nodeInfo);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
loadFailureNodes.Add(nodeInfo);
|
||||
continue; // 跳过加载失败的节点
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
// 远程环境无法加载的节点,输出信息
|
||||
foreach (var f_node in loadFailureNodes)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, "无法加载的节点Guid:" + f_node.Guid);
|
||||
}
|
||||
|
||||
#region 尝试重新放置节点的位置
|
||||
// 判断加载的节点是否需要放置在容器中
|
||||
foreach (var nodeInfo in loadSuuccessNodes)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) &&
|
||||
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
|
||||
{
|
||||
needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点
|
||||
}
|
||||
}
|
||||
loadSuuccessNodes.Clear();
|
||||
loadFailureNodes.Clear();
|
||||
foreach (var nodeInfo in needPlaceNodeInfos)
|
||||
{
|
||||
// 通知远程调整节点放置位置
|
||||
var isSuuccess = await PlaceNodeToContainerAsync(nodeInfo.Guid, nodeInfo.ParentNodeGuid);
|
||||
if (isSuuccess)
|
||||
{
|
||||
loadSuuccessNodes.Add(nodeInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
loadFailureNodes.Add(nodeInfo);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
foreach (var f_node in loadFailureNodes)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, $"无法移动到指定容器的节点Guid :{f_node.Guid}" +
|
||||
$"{Environment.NewLine}容器节点Guid{f_node.ParentNodeGuid}{Environment.NewLine}" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建节点/区域/基础控件
|
||||
/// </summary>
|
||||
/// <param name="nodeType">节点/区域/基础控件类型</param>
|
||||
/// <param name="position">节点在画布上的位置(</param>
|
||||
/// <param name="methodDetailsInfo">节点绑定的方法说明</param>
|
||||
public async Task<NodeInfo> CreateNodeAsync(NodeControlType nodeControlType,
|
||||
PositionOfUI position,
|
||||
MethodDetailsInfo methodDetailsInfo = null)
|
||||
{
|
||||
IsLoadingNode = true;
|
||||
var nodeInfo = await msgClient.SendAndWaitDataAsync<NodeInfo>(EnvMsgTheme.CreateNode, new
|
||||
{
|
||||
nodeType = nodeControlType.ToString(),
|
||||
position = position,
|
||||
mdInfo = methodDetailsInfo,
|
||||
});
|
||||
|
||||
MethodDetails? methodDetails = null;
|
||||
if (!string.IsNullOrEmpty(nodeInfo.MethodName))
|
||||
{
|
||||
MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载远程环境时尝试获取方法信息
|
||||
}
|
||||
|
||||
//MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息
|
||||
var nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 远程环境下加载节点
|
||||
nodeModel.LoadInfo(nodeInfo);
|
||||
TryAddNode(nodeModel);
|
||||
IsLoadingNode = false;
|
||||
|
||||
// 通知UI更改
|
||||
UIContextOperation.Invoke(() =>
|
||||
{
|
||||
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position));
|
||||
});
|
||||
return nodeInfo;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 将节点放置在容器中
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> PlaceNodeToContainerAsync(string nodeGuid, string containerNodeGuid)
|
||||
{
|
||||
var isSuuccess = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.PlaceNode, new
|
||||
{
|
||||
nodeGuid = nodeGuid,
|
||||
containerNodeGuid = containerNodeGuid,
|
||||
});
|
||||
if (isSuuccess)
|
||||
{
|
||||
OnNodePlace?.Invoke(new NodePlaceEventArgs(nodeGuid, containerNodeGuid)); // 通知UI更改节点放置位置
|
||||
}
|
||||
return isSuuccess;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将节点从容器中脱离
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> TakeOutNodeToContainerAsync(string nodeGuid)
|
||||
{
|
||||
var isSuuccess = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.TakeOutNode, new
|
||||
{
|
||||
nodeGuid = nodeGuid,
|
||||
});
|
||||
if (isSuuccess)
|
||||
{
|
||||
OnNodeTakeOut?.Invoke(new NodeTakeOutEventArgs(nodeGuid)); // 重新放置在画布上
|
||||
}
|
||||
return isSuuccess;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 移除远程环境的某个节点
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> RemoveNodeAsync(string nodeGuid)
|
||||
{
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
if (result)
|
||||
{
|
||||
UIContextOperation.Invoke(() =>
|
||||
{
|
||||
OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.WriteLine(InfoType.ERROR, "删除失败");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 激活远程某个全局触发器节点
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
public void ActivateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
// 需要重写
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.ActivateFlipflopNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 暂停远程某个全局触发器节点
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
public void TerminateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
// 需要重写
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.TerminateFlipflopNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 设置远程环境某个节点的中断
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="isInterrupt"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, bool isInterrupt)
|
||||
{
|
||||
var state = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.SetNodeInterrupt, // 设置节点中断
|
||||
new
|
||||
{
|
||||
nodeGuid,
|
||||
isInterrupt,
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 为远程某个节点添加中断的表达式
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="expression"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> AddInterruptExpressionAsync(string key, string expression)
|
||||
{
|
||||
var state = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.AddInterruptExpression, // 设置节点/对象的中断表达式
|
||||
new
|
||||
{
|
||||
key,
|
||||
expression,
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查并获取节点/对象是否正在监视、以及监视的表达式(需要重写)
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<(bool, string[])> CheckObjMonitorStateAsync(string key)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
var exps = Array.Empty<string>();
|
||||
return (false, exps);
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await msgClient.SendAndWaitDataAsync<(bool, string[])>(EnvMsgTheme.SetNodeInterrupt, // 检查并获取节点/对象是否正在监视、以及监视的表达式
|
||||
new
|
||||
{
|
||||
key,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 需要定位某个节点
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
public void NodeLocated(string nodeGuid)
|
||||
{
|
||||
UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通知远程环境修改节点数据
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="path"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
||||
{
|
||||
if(IsLoadingProject || IsLoadingNode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//this.WriteLine(InfoType.INFO, $"通知远程环境修改节点数据:{nodeGuid},name:{path},value:{value}");
|
||||
await msgClient.SendAsync(EnvMsgTheme.ValueNotification, new
|
||||
{
|
||||
nodeGuid = nodeGuid,
|
||||
path = path,
|
||||
value = value.ToString(),
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 改变可选参数的数目
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid">对应的节点Guid</param>
|
||||
/// <param name="isAdd">true,增加参数;false,减少参数</param>
|
||||
/// <param name="paramIndex">以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)</param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
||||
{
|
||||
if (IsLoadingProject || IsLoadingNode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!NodeModels.TryGetValue(nodeGuid,out var nodeModel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//this.WriteLine(InfoType.INFO, $"通知远程环境修改节点可选数据:{nodeGuid},isAdd:{isAdd},paramIndex:{paramIndex}");
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ChangeParameter, new
|
||||
{
|
||||
nodeGuid = nodeGuid,
|
||||
isAdd = isAdd,
|
||||
paramIndex = paramIndex,
|
||||
});
|
||||
if (result) {
|
||||
if (isAdd)
|
||||
{
|
||||
nodeModel.MethodDetails.AddParamsArg(paramIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeModel.MethodDetails.RemoveParamsArg(paramIndex);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#region 私有方法
|
||||
|
||||
|
||||
private bool TryAddNode(NodeModelBase nodeModel)
|
||||
{
|
||||
NodeModels[nodeModel.Guid] = nodeModel;
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 私有方法,通过节点信息集合加载节点
|
||||
/// </summary>
|
||||
/// <param name="nodeInfos"></param>
|
||||
private void LoadNodeInfos(List<NodeInfo> nodeInfos)
|
||||
{
|
||||
#region 从NodeInfo创建NodeModel
|
||||
foreach (NodeInfo? nodeInfo in nodeInfos)
|
||||
{
|
||||
@@ -709,27 +1072,32 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
nodeModel.LoadInfo(nodeInfo); // 创建节点model
|
||||
TryAddNode(nodeModel); // 加载项目时将节点加载到环境中
|
||||
if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) &&
|
||||
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
|
||||
{
|
||||
needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点
|
||||
}
|
||||
|
||||
UIContextOperation?.Invoke(() =>
|
||||
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); // 添加到UI上
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 重新放置节点
|
||||
List<NodeInfo> needPlaceNodeInfos = [];
|
||||
foreach (NodeInfo? nodeInfo in nodeInfos)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(nodeInfo.ParentNodeGuid) &&
|
||||
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
|
||||
{
|
||||
needPlaceNodeInfos.Add(nodeInfo); // 需要重新放置的节点
|
||||
}
|
||||
}
|
||||
foreach (NodeInfo nodeInfo in needPlaceNodeInfos)
|
||||
{
|
||||
if (NodeModels.TryGetValue(nodeInfo.Guid, out var childNode) &&
|
||||
NodeModels.TryGetValue(nodeInfo.ParentNodeGuid, out var parentNode))
|
||||
{
|
||||
childNode.ParentNode = parentNode;
|
||||
childNode.ContainerNode = parentNode;
|
||||
parentNode.ChildrenNode.Add(childNode);
|
||||
UIContextOperation?.Invoke(() => OnNodeParentChildChange?.Invoke(
|
||||
new NodeContainerChildChangeEventArgs(childNode.Guid, parentNode.Guid,
|
||||
NodeContainerChildChangeEventArgs.Type.Place)));
|
||||
UIContextOperation?.Invoke(() =>
|
||||
OnNodePlace?.Invoke(new NodePlaceEventArgs(nodeInfo.Guid, nodeInfo.ParentNodeGuid)) // 通知UI更改节点放置位置
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -805,141 +1173,23 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 创建节点/区域/基础控件
|
||||
/// </summary>
|
||||
/// <param name="nodeType">节点/区域/基础控件类型</param>
|
||||
/// <param name="position">节点在画布上的位置(</param>
|
||||
/// <param name="methodDetailsInfo">节点绑定的方法说明</param>
|
||||
public async Task<NodeInfo> CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo methodDetailsInfo = null)
|
||||
{
|
||||
IsLoadingNode = true;
|
||||
var nodeInfo = await msgClient.SendAndWaitDataAsync<NodeInfo>(EnvMsgTheme.CreateNode, new
|
||||
{
|
||||
nodeType = nodeControlType.ToString(),
|
||||
position = position,
|
||||
mdInfo = methodDetailsInfo,
|
||||
});
|
||||
|
||||
MethodDetails? methodDetails = null;
|
||||
if (!string.IsNullOrEmpty(nodeInfo.MethodName))
|
||||
{
|
||||
MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载远程环境时尝试获取方法信息
|
||||
}
|
||||
#region 远程环境下暂未实现的接口
|
||||
|
||||
//MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails);// 加载项目时尝试获取方法信息
|
||||
var nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 远程环境下加载节点
|
||||
nodeModel.LoadInfo(nodeInfo);
|
||||
TryAddNode(nodeModel);
|
||||
IsLoadingNode = false;
|
||||
|
||||
// 通知UI更改
|
||||
UIContextOperation.Invoke(() =>
|
||||
{
|
||||
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position));
|
||||
});
|
||||
return nodeInfo;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将节点放置在容器中/从容器中取出
|
||||
/// </summary>
|
||||
/// <param name="childNodeGuid">子节点(主要节点)</param>
|
||||
/// <param name="parentNodeGuid">父节点</param>
|
||||
/// <param name="isPlace">是否组合(反之为分解节点组合关系)</param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> ChangeNodeContainerChild(string childNodeGuid, string parentNodeGuid, bool isAssembly)
|
||||
{
|
||||
this.WriteLine(InfoType.WARN, "远程环境尚未实现的接口(重要,会尽快实现):ChangeNodeParentChild");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> RemoveNodeAsync(string nodeGuid)
|
||||
{
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
if (result)
|
||||
{
|
||||
UIContextOperation.Invoke(() =>
|
||||
{
|
||||
OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid));
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
this.WriteLine(InfoType.ERROR, "删除失败");
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void ActivateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.ActivateFlipflopNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
}
|
||||
|
||||
public void TerminateFlipflopNode(string nodeGuid)
|
||||
{
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.TerminateFlipflopNode, new
|
||||
{
|
||||
nodeGuid
|
||||
});
|
||||
}
|
||||
|
||||
public async Task<bool> SetNodeInterruptAsync(string nodeGuid, bool isInterrupt)
|
||||
{
|
||||
var state = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.SetNodeInterrupt, // 设置节点中断
|
||||
new
|
||||
{
|
||||
nodeGuid,
|
||||
isInterrupt,
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
|
||||
public async Task<bool> AddInterruptExpressionAsync(string key, string expression)
|
||||
{
|
||||
var state = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.AddInterruptExpression, // 设置节点/对象的中断表达式
|
||||
new
|
||||
{
|
||||
key,
|
||||
expression,
|
||||
});
|
||||
return state;
|
||||
}
|
||||
|
||||
public void SetMonitorObjState(string key, bool isMonitor)
|
||||
{
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:SetMonitorObjState");
|
||||
}
|
||||
|
||||
public async Task<(bool, string[])> CheckObjMonitorStateAsync(string key)
|
||||
public async Task<object> InvokeNodeAsync(IDynamicContext context, string nodeGuid)
|
||||
{
|
||||
if (string.IsNullOrEmpty(key))
|
||||
{
|
||||
var exps = Array.Empty<string>();
|
||||
return (false, exps);
|
||||
}
|
||||
else
|
||||
{
|
||||
var result = await msgClient.SendAndWaitDataAsync<(bool, string[])>(EnvMsgTheme.SetNodeInterrupt, // 检查并获取节点/对象是否正在监视、以及监视的表达式
|
||||
new
|
||||
{
|
||||
key,
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
// 登录到远程环境后,启动器相关方法无效
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现接口 InvokeNodeAsync");
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
public async Task<ChannelFlowInterrupt.CancelType> GetOrCreateGlobalInterruptAsync()
|
||||
{
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:GetOrCreateGlobalInterruptAsync");
|
||||
@@ -961,76 +1211,31 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 对象监视表达式
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="monitorData"></param>
|
||||
/// <param name="sourceType"></param>
|
||||
public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType)
|
||||
{
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:MonitorObjectNotification");
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 触发节点的中断
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid"></param>
|
||||
/// <param name="expression"></param>
|
||||
/// <param name="type"></param>
|
||||
public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type)
|
||||
{
|
||||
this.WriteLine(InfoType.INFO, "远程环境尚未实现的接口:TriggerInterrupt");
|
||||
}
|
||||
|
||||
public void NodeLocated(string nodeGuid)
|
||||
{
|
||||
UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value)
|
||||
{
|
||||
if(IsLoadingProject || IsLoadingNode)
|
||||
{
|
||||
return;
|
||||
}
|
||||
//this.WriteLine(InfoType.INFO, $"通知远程环境修改节点数据:{nodeGuid},name:{path},value:{value}");
|
||||
_ = msgClient.SendAsync(EnvMsgTheme.ValueNotification, new
|
||||
{
|
||||
nodeGuid = nodeGuid,
|
||||
path = path,
|
||||
value = value.ToString(),
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 改变可选参数的数目
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid">对应的节点Guid</param>
|
||||
/// <param name="isAdd">true,增加参数;false,减少参数</param>
|
||||
/// <param name="paramIndex">以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数)</param>
|
||||
/// <returns></returns>
|
||||
public async Task<bool> ChangeParameter(string nodeGuid, bool isAdd, int paramIndex)
|
||||
{
|
||||
if (IsLoadingProject || IsLoadingNode)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!NodeModels.TryGetValue(nodeGuid,out var nodeModel))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
//this.WriteLine(InfoType.INFO, $"通知远程环境修改节点可选数据:{nodeGuid},isAdd:{isAdd},paramIndex:{paramIndex}");
|
||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ChangeParameter, new
|
||||
{
|
||||
nodeGuid = nodeGuid,
|
||||
isAdd = isAdd,
|
||||
paramIndex = paramIndex,
|
||||
});
|
||||
if (result) {
|
||||
if (isAdd)
|
||||
{
|
||||
nodeModel.MethodDetails.AddParamsArg(paramIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
nodeModel.MethodDetails.RemoveParamsArg(paramIndex);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#region 流程依赖类库的接口
|
||||
|
||||
@@ -1081,5 +1286,9 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -433,7 +433,7 @@ namespace Serein.NodeFlow
|
||||
}
|
||||
catch (FlipflopException ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR,$"触发器[{singleFlipFlopNode.MethodDetails.MethodName}]因非预期异常终止。"+ex.Message);
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{singleFlipFlopNode.MethodDetails.MethodName}]因非预期异常终止。"+ex.Message);
|
||||
if (ex.Type == FlipflopException.CancelClass.CancelFlow)
|
||||
{
|
||||
break;
|
||||
@@ -442,7 +442,7 @@ namespace Serein.NodeFlow
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"触发器[{singleFlipFlopNode.Guid}]异常。"+ ex.Message);
|
||||
//await Console.Out.WriteLineAsync(ex.Message);
|
||||
await Task.Delay(100);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -54,8 +54,19 @@ namespace Serein.NodeFlow.Model
|
||||
|
||||
public void PlaceNode(NodeModelBase nodeModel)
|
||||
{
|
||||
_ = this.Env.RemoveNodeAsync(DataNode?.Guid);
|
||||
DataNode = nodeModel;
|
||||
// 全局数据节点只有一个子控件
|
||||
if (DataNode is not null)
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await this.Env.RemoveNodeAsync(DataNode?.Guid);
|
||||
DataNode = nodeModel;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
DataNode = nodeModel;
|
||||
}
|
||||
}
|
||||
|
||||
public void TakeOutAll()
|
||||
|
||||
@@ -201,12 +201,15 @@ namespace Serein.NodeFlow.Tool
|
||||
|
||||
#region 功能性方法
|
||||
|
||||
private readonly string SereinLibraryDll = $"{nameof(Serein)}.{nameof(Serein.Library)}.dll";
|
||||
/// <summary>
|
||||
/// 基础依赖
|
||||
/// </summary>
|
||||
public readonly static string SereinBaseLibrary = $"{nameof(Serein)}.{nameof(Serein.Library)}.dll";
|
||||
|
||||
private (NodeLibraryInfo, List<MethodDetailsInfo>) LoadDllNodeInfo(string dllFilePath)
|
||||
{
|
||||
var fileName = Path.GetFileName(dllFilePath); // 获取文件名
|
||||
if (SereinLibraryDll.Equals(fileName))
|
||||
if (SereinBaseLibrary.Equals(fileName))
|
||||
{
|
||||
return LoadAssembly(typeof(IFlowEnvironment).Assembly, () => {
|
||||
//SereinEnv.PrintInfo(InfoType.WRAN, "基础模块不能卸载");
|
||||
@@ -215,9 +218,9 @@ namespace Serein.NodeFlow.Tool
|
||||
else
|
||||
{
|
||||
var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径
|
||||
var sereinFlowLibraryPath = Path.Combine(dir, SereinLibraryDll);
|
||||
var sereinFlowBaseLibraryPath = Path.Combine(dir, SereinBaseLibrary);
|
||||
// 每个类库下面至少需要有“Serein.Library.dll”类库依赖
|
||||
var flowAlc = new FlowLibraryAssemblyContext(sereinFlowLibraryPath, fileName);
|
||||
var flowAlc = new FlowLibraryAssemblyContext(sereinFlowBaseLibraryPath, fileName);
|
||||
Action actionUnload = () =>
|
||||
{
|
||||
flowAlc?.Unload(); // 卸载程序集
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<Version>1.0.0</Version>
|
||||
<!--<TargetFrameworks>net8.0</TargetFrameworks>-->
|
||||
<BaseOutputPath>D:\Project\C#\DynamicControl\SereinFlow\.Output</BaseOutputPath>
|
||||
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
|
||||
<Title>SereinFow</Title>
|
||||
<Description>基础节点</Description>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<RepositoryUrl>https://github.com/fhhyyp/serein-flow</RepositoryUrl>
|
||||
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="..\LICENSE">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
<None Include="..\README.md">
|
||||
<Pack>True</Pack>
|
||||
<PackagePath>\</PackagePath>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,91 +0,0 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Serein.BaseNode
|
||||
{
|
||||
|
||||
public enum ExpType
|
||||
{
|
||||
Get,
|
||||
Set
|
||||
}
|
||||
[DynamicFlow(Name ="[基础节点]")]
|
||||
internal class SereinBaseNodes
|
||||
{
|
||||
[NodeAction(NodeType.Action,"条件节点")]
|
||||
private bool SereinConditionNode(IDynamicContext context,
|
||||
object targetObject,
|
||||
string exp = "ISPASS")
|
||||
{
|
||||
var isPass = SereinConditionParser.To(targetObject, exp);
|
||||
context.NextOrientation = isPass ? ConnectionInvokeType.IsSucceed : ConnectionInvokeType.IsFail;
|
||||
return isPass;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "表达式节点")]
|
||||
private object SereinExpNode(IDynamicContext context,
|
||||
object targetObject,
|
||||
string exp)
|
||||
{
|
||||
|
||||
exp = "@" + exp;
|
||||
var newData = SerinExpressionEvaluator.Evaluate(exp, targetObject, out bool isChange);
|
||||
object result;
|
||||
if (isChange || exp.StartsWith("@GET",System.StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
result = newData;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = targetObject;
|
||||
}
|
||||
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "KV数据收集节点")]
|
||||
private Dictionary<string, object> SereinKvDataCollectionNode(string argName, params object[] value)
|
||||
{
|
||||
var names = argName.Split(';');
|
||||
var count = Math.Min(value.Length, names.Length);
|
||||
var dict = new Dictionary<string, object>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dict[names[i]] = value[i];
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
[NodeAction(NodeType.Action, "List数据收集节点")]
|
||||
private object[] SereinListDataCollectionNode(params object[] value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
/* if (!DynamicObjectHelper.TryResolve(dict, className, out var result))
|
||||
{
|
||||
Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
|
||||
}
|
||||
else
|
||||
{
|
||||
DynamicObjectHelper.PrintObjectProperties(result);
|
||||
}
|
||||
//if (!ObjDynamicCreateHelper.TryResolve(externalData, "RootType", out var result))
|
||||
//{
|
||||
// Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
|
||||
|
||||
//}
|
||||
//ObjDynamicCreateHelper.PrintObjectProperties(result!);
|
||||
return result;*/
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
@@ -1,50 +0,0 @@
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Serein.WorkBench.ControlLibrary.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Follow steps 1a or 1b and then 2 to use this custom control in a XAML file.
|
||||
///
|
||||
/// Step 1a) Using this custom control in a XAML file that exists in the current project.
|
||||
/// Add this XmlNamespace attribute to the root element of the markup file where it is
|
||||
/// to be used:
|
||||
///
|
||||
/// xmlns:MyNamespace="clr-namespace:Serein.WorkBench.ControlLibrary.Core"
|
||||
///
|
||||
///
|
||||
/// Step 1b) Using this custom control in a XAML file that exists in a different project.
|
||||
/// Add this XmlNamespace attribute to the root element of the markup file where it is
|
||||
/// to be used:
|
||||
///
|
||||
/// xmlns:MyNamespace="clr-namespace:Serein.WorkBench.ControlLibrary.Core;assembly=Serein.WorkBench.ControlLibrary.Core"
|
||||
///
|
||||
/// You will also need to add a project reference from the project where the XAML file lives
|
||||
/// to this project and Rebuild to avoid compilation errors:
|
||||
///
|
||||
/// Right click on the target project in the Solution Explorer and
|
||||
/// "Add Reference"->"Projects"->[Select this project]
|
||||
///
|
||||
///
|
||||
/// Step 2)
|
||||
/// Go ahead and use your control in the XAML file.
|
||||
///
|
||||
/// <MyNamespace:CustomControl1/>
|
||||
///
|
||||
/// </summary>
|
||||
public class CustomControl1 : Control
|
||||
{
|
||||
static CustomControl1()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(CustomControl1), new FrameworkPropertyMetadata(typeof(CustomControl1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<UseWPF>true</UseWPF>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,9 +0,0 @@
|
||||
<Application x:Class="Serein.RemoteWorkBench.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Serein.RemoteWorkBench"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@@ -1,14 +0,0 @@
|
||||
using System.Configuration;
|
||||
using System.Data;
|
||||
using System.Windows;
|
||||
|
||||
namespace Serein.RemoteWorkBench
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for App.xaml
|
||||
/// </summary>
|
||||
public partial class App : Application
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
@@ -1,12 +0,0 @@
|
||||
<Window x:Class="Serein.RemoteWorkBench.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Serein.RemoteWorkBench"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="450" Width="800">
|
||||
<Grid>
|
||||
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Serein.RemoteWorkBench
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,127 +0,0 @@
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 对应的节点实体类
|
||||
/// </summary>
|
||||
internal NodeModelBase Node { get; }
|
||||
|
||||
|
||||
private bool isSelect;
|
||||
/// <summary>
|
||||
/// 表示节点控件是否被选中
|
||||
/// </summary>
|
||||
internal bool IsSelect
|
||||
{
|
||||
get => isSelect;
|
||||
set
|
||||
{
|
||||
isSelect = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public NodeDebugSetting DebugSetting
|
||||
{
|
||||
get => Node.DebugSetting;
|
||||
set
|
||||
{
|
||||
if (value != null)
|
||||
{
|
||||
Node.DebugSetting = value;
|
||||
OnPropertyChanged(/*nameof(DebugSetting)*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public MethodDetails MethodDetails
|
||||
{
|
||||
get => Node.MethodDetails;
|
||||
set
|
||||
{
|
||||
if(value != null)
|
||||
{
|
||||
Node.MethodDetails = value;
|
||||
OnPropertyChanged(/*nameof(MethodDetails)*/);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool isInterrupt;
|
||||
public bool IsInterrupt
|
||||
{
|
||||
get => isInterrupt;
|
||||
set
|
||||
{
|
||||
isInterrupt = value;
|
||||
OnPropertyChanged(/*nameof(IsInterrupt)*/);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//public bool IsInterrupt
|
||||
//{
|
||||
// get => Node.DebugSetting.IsInterrupt;
|
||||
// set
|
||||
// {
|
||||
// if (value)
|
||||
// {
|
||||
// Node.Interrupt();
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// Node.CancelInterrupt();
|
||||
// }
|
||||
// OnPropertyChanged(nameof(IsInterrupt));
|
||||
// }
|
||||
//}
|
||||
|
||||
//public bool IsProtectionParameter
|
||||
//{
|
||||
// get => MethodDetails.IsProtectionParameter;
|
||||
// set
|
||||
// {
|
||||
// MethodDetails.IsProtectionParameter = value;
|
||||
// OnPropertyChanged(nameof(IsInterrupt));
|
||||
// }
|
||||
//}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ActionNodeControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
||||
xmlns:vm="clr-namespace:Serein.WorkBench.Node.ViewModel"
|
||||
xmlns:Converters="clr-namespace:Serein.WorkBench.Tool.Converters"
|
||||
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
|
||||
MaxWidth="300">
|
||||
<UserControl.Resources>
|
||||
<!--<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />-->
|
||||
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Border BorderBrush="#8DE9FD" BorderThickness="1">
|
||||
|
||||
|
||||
<Grid>
|
||||
<Grid.ToolTip>
|
||||
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid.ToolTip>
|
||||
|
||||
<Border>
|
||||
<Border.Style>
|
||||
<Style TargetType="Border">
|
||||
<!-- 默认无边框 -->
|
||||
<Setter Property="BorderBrush" Value="Transparent" />
|
||||
<Setter Property="BorderThickness" Value="0" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsInterrupt}" Value="True">
|
||||
<Setter Property="BorderBrush" Value="Red" />
|
||||
<Setter Property="BorderThickness" Value="2" />
|
||||
<Setter Property="Background" Value="#80000000" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</Border.Style>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Background="#8DE9FD">
|
||||
<CheckBox IsChecked="{Binding DebugSetting.IsEnable, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
||||
<CheckBox IsChecked="{Binding MethodDetails.IsProtectionParameter, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
||||
<TextBlock Text="{Binding MethodDetails.MethodTips}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}"/>
|
||||
<!-- ParameterProtectionMask 参数保护 -->
|
||||
<!--取反 Visibility="{Binding DebugSetting.IsEnable, Converter={StaticResource InvertedBoolConverter}, ConverterParameter=Inverted}"-->
|
||||
<Border Grid.Row="1" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
|
||||
Visibility="{Binding MethodDetails.IsProtectionParameter, Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" />
|
||||
<Grid Grid.Row="2" Background="#D5F0FC" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="0" BorderThickness="1">
|
||||
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||
</Border>
|
||||
<Border Grid.Column="1" BorderThickness="1">
|
||||
<TextBlock Text="{Binding MethodDetails.ReturnType}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
|
||||
</Grid>
|
||||
|
||||
</Border>
|
||||
|
||||
<!--Visibility="{Binding IsEnable, Converter={StaticResource BoolToVisConverter}, ConverterParameter=False}"-->
|
||||
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</local:NodeControlBase>
|
||||
@@ -1,19 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.ViewModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace Serein.WorkBench.Node.View
|
||||
{
|
||||
/// <summary>
|
||||
/// ActionNode.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ActionNodeControl : NodeControlBase
|
||||
{
|
||||
public ActionNodeControl(ActionNodeControlViewModel viewModel):base(viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ActionRegionControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
||||
MaxWidth="300">
|
||||
<Grid>
|
||||
<!--<Border BorderBrush="Black" BorderThickness="1" Padding="10">
|
||||
<StackPanel>
|
||||
<Grid Margin="2,2,2,5">
|
||||
<TextBlock Text="动作区域" FontWeight="Bold" HorizontalAlignment="Left" FontSize="14" Margin="0,1,0,0"/>
|
||||
<Button Content="编辑" FontWeight="Bold" HorizontalAlignment="Right"/>
|
||||
</Grid>
|
||||
<ListBox x:Name="ActionsListBox" AllowDrop="True" Drop="ActionsListBox_Drop" />
|
||||
</StackPanel>
|
||||
</Border>-->
|
||||
|
||||
</Grid>
|
||||
|
||||
</local:NodeControlBase>
|
||||
@@ -1,130 +0,0 @@
|
||||
using Serein.NodeFlow;
|
||||
using Serein.NodeFlow.Model;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Serein.WorkBench.Node.View
|
||||
{
|
||||
/// <summary>
|
||||
/// ActionRegion.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ActionRegionControl : NodeControlBase
|
||||
{
|
||||
private Point _dragStartPoint;
|
||||
|
||||
//private new readonly CompositeActionNode Node;
|
||||
|
||||
//public override NodeControlViewModel ViewModel { get ; set ; }
|
||||
|
||||
public ActionRegionControl() : base(null)
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
//public ActionRegionControl(CompositeActionNode node)
|
||||
//{
|
||||
// InitializeComponent();
|
||||
// //ViewModel = new NodeControlViewModel(node);
|
||||
// DataContext = ViewModel;
|
||||
// base.Name = "动作组合节点";
|
||||
//}
|
||||
|
||||
public void AddAction(NodeControlBase node, bool isTask = false)
|
||||
{
|
||||
/*TextBlock actionText = new TextBlock
|
||||
{
|
||||
Text = node.MethodDetails.MethodName + (isTask ? " (Task)" : ""),
|
||||
Margin = new Thickness(10, 2, 0, 0),
|
||||
Tag = node.MethodDetails,
|
||||
};*/
|
||||
/// Node?.AddNode((SingleActionNode)node.ViewModel.Node);
|
||||
// ActionsListBox.Items.Add(node);
|
||||
}
|
||||
|
||||
/* public async Task ExecuteActions(DynamicContext context)
|
||||
{
|
||||
foreach (TextBlock item in ActionsListBox.Items)
|
||||
{
|
||||
dynamic tag = item.Tag;
|
||||
IAction action = tag.Action;
|
||||
bool isTask = tag.IsTask;
|
||||
|
||||
if (isTask)
|
||||
{
|
||||
await Task.Run(() => action.Execute(Node.MethodDetails, context));
|
||||
}
|
||||
else
|
||||
{
|
||||
action.Execute(Node.MethodDetails, context);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
private void ActionsListBox_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
/*if (e.Data.GetDataPresent("Type"))
|
||||
{
|
||||
Type droppedType = e.Data.GetData("Type") as Type;
|
||||
|
||||
if (droppedType != null && typeof(ICondition).IsAssignableFrom(droppedType) && droppedType.IsClass)
|
||||
{
|
||||
// 创建一个新的 TextBlock 并设置其属性
|
||||
TextBlock conditionText = new TextBlock
|
||||
{
|
||||
Text = droppedType.Name,
|
||||
Margin = new Thickness(10, 2, 0, 0),
|
||||
Tag = droppedType
|
||||
};
|
||||
|
||||
// 为 TextBlock 添加鼠标左键按下事件处理程序
|
||||
// conditionText.MouseLeftButtonDown += TypeText_MouseLeftButtonDown;
|
||||
// 为 TextBlock 添加鼠标移动事件处理程序
|
||||
// conditionText.MouseMove += TypeText_MouseMove;
|
||||
|
||||
// 将 TextBlock 添加到 ActionsListBox 中
|
||||
ActionsListBox.Items.Add(conditionText);
|
||||
}
|
||||
}*/
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
// 用于拖动的鼠标事件处理程序
|
||||
private void TypeText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
_dragStartPoint = e.GetPosition(null);
|
||||
}
|
||||
|
||||
private void TypeText_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
Point mousePos = e.GetPosition(null);
|
||||
Vector diff = _dragStartPoint - mousePos;
|
||||
|
||||
if (e.LeftButton == MouseButtonState.Pressed &&
|
||||
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
||||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
||||
{
|
||||
if (sender is TextBlock typeText)
|
||||
{
|
||||
MoveNodeData moveNodeData = new MoveNodeData
|
||||
{
|
||||
NodeControlType = Library.Enums.NodeControlType.ConditionRegion
|
||||
};
|
||||
|
||||
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
|
||||
DataObject dragData = new DataObject(MouseNodeType.CreateDllNodeInCanvas, moveNodeData);
|
||||
|
||||
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
|
||||
|
||||
|
||||
//var dragData = new DataObject(MouseNodeType.CreateNodeInCanvas, typeText.Tag);
|
||||
//DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ConditionNodeControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
||||
xmlns:vm="clr-namespace:Serein.WorkBench.Node.ViewModel"
|
||||
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
|
||||
MaxWidth="300">
|
||||
|
||||
<UserControl.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BoolToVis" />
|
||||
</UserControl.Resources>
|
||||
|
||||
|
||||
<Grid>
|
||||
<Grid.ToolTip>
|
||||
<ToolTip Background="LightYellow" Foreground="Black" Content="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid.ToolTip>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Border Grid.Row="0" Background="#A8D8EA" BorderBrush="#A8D8EA" BorderThickness="1" HorizontalAlignment="Stretch">
|
||||
<TextBlock Text="条件节点" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<Grid Grid.Row="1" Background="#F1FFDF" HorizontalAlignment="Stretch">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="20"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<CheckBox Grid.Column="0" IsChecked="{Binding IsCustomData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/> <!--Converter={StaticResource BoolToVis}-->
|
||||
<TextBox Grid.Column="1" MinWidth="50" Text="{Binding CustomData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
||||
<TextBox.Style>
|
||||
<Style TargetType="TextBox">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsCustomData}" Value="True">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBox.Style>
|
||||
</TextBox>
|
||||
|
||||
<TextBlock Grid.Column="1" MinWidth="50" Text="上一节点数据" HorizontalAlignment="Stretch" VerticalAlignment="Center">
|
||||
<TextBlock.Style>
|
||||
<Style TargetType="TextBlock">
|
||||
<Setter Property="Visibility" Value="Collapsed" />
|
||||
<Style.Triggers>
|
||||
<DataTrigger Binding="{Binding IsCustomData}" Value="False">
|
||||
<Setter Property="Visibility" Value="Visible" />
|
||||
</DataTrigger>
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</TextBlock.Style>
|
||||
</TextBlock>
|
||||
</Grid>
|
||||
<TextBox Grid.Row="2" Background="#f1F66F" MinWidth="100" Text="{Binding Expression, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"
|
||||
HorizontalAlignment="Stretch" VerticalAlignment="Center"/>
|
||||
|
||||
<!--<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
|
||||
<Border Grid.Row="2" Background="#EAFFD0" BorderBrush="#EAFFD0" BorderThickness="1">
|
||||
<TextBlock Text="{Binding MethodDetails.MethodTips, Converter={StaticResource TypeToStringConverter}, StringFormat=return:{0}, UpdateSourceTrigger=PropertyChanged}"
|
||||
HorizontalAlignment="Center"
|
||||
VerticalAlignment="Center"/>
|
||||
</Border>-->
|
||||
</Grid>
|
||||
</local:NodeControlBase>
|
||||
@@ -1,26 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.ViewModel;
|
||||
|
||||
namespace Serein.WorkBench.Node.View
|
||||
{
|
||||
/// <summary>
|
||||
/// ConditionNode.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ConditionNodeControl : NodeControlBase
|
||||
{
|
||||
public ConditionNodeControl() : base()
|
||||
{
|
||||
// 窗体初始化需要
|
||||
ViewModel = new ConditionNodeControlViewModel (new SingleConditionNode());
|
||||
DataContext = ViewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public ConditionNodeControl(ConditionNodeControlViewModel viewModel):base(viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ConditionRegionControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
||||
MaxWidth="300">
|
||||
<Grid>
|
||||
<Border BorderBrush="Black" BorderThickness="1" Padding="10">
|
||||
<StackPanel>
|
||||
<DockPanel Margin="2,2,2,5">
|
||||
<TextBlock Text="条件区域" FontWeight="Bold" HorizontalAlignment="Left" FontSize="14" Margin="0,1,0,0"/>
|
||||
<Button Content="编辑" FontWeight="Bold" HorizontalAlignment="Right"/>
|
||||
</DockPanel>
|
||||
<ListBox x:Name="ConditionsListBox" AllowDrop="True" Drop="ConditionsListBox_Drop"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</local:NodeControlBase>
|
||||
@@ -1,95 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.ViewModel;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Serein.WorkBench.Node.View
|
||||
{
|
||||
/// <summary>
|
||||
/// ConditionRegion.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ConditionRegionControl : NodeControlBase
|
||||
{
|
||||
|
||||
public ConditionRegionControl() : base()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public ConditionRegionControl(ConditionRegionNodeControlViewModel viewModel) : base(viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 添加条件控件
|
||||
/// </summary>
|
||||
/// <param name="condition"></param>
|
||||
public void AddCondition(NodeControlBase node)
|
||||
{
|
||||
((CompositeConditionNode)ViewModel.Node).AddNode((SingleConditionNode)node.ViewModel.Node);
|
||||
|
||||
this.Width += node.Width;
|
||||
this.Height += node.Height;
|
||||
ConditionsListBox.Items.Add(node);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void ConditionsListBox_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
// Mouse event handlers for dragging
|
||||
//private void TypeText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
//{
|
||||
// _dragStartPoint = e.GetPosition(null);
|
||||
//}
|
||||
|
||||
//private void TypeText_MouseMove(object sender, MouseEventArgs e)
|
||||
//{
|
||||
// Point mousePos = e.GetPosition(null);
|
||||
// Vector diff = _dragStartPoint - mousePos;
|
||||
|
||||
// if (e.LeftButton == MouseButtonState.Pressed &&
|
||||
// (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
||||
// Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
||||
// {
|
||||
// if (sender is TextBlock typeText)
|
||||
// {
|
||||
// var dragData = new DataObject(MouseNodeType.RegionType, typeText.Tag);
|
||||
// DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
|
||||
/*private void TypeText_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
Point mousePos = e.GetPosition(null);
|
||||
Vector diff = _dragStartPoint - mousePos;
|
||||
|
||||
if (e.LeftButton == MouseButtonState.Pressed &&
|
||||
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
||||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
||||
{
|
||||
TextBlock typeText = sender as TextBlock;
|
||||
if (typeText != null)
|
||||
{
|
||||
DataObject dragData = new DataObject("Type", typeText.Tag);
|
||||
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<UserControl x:Class="Serein.WorkBench.Node.View.DllControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
||||
MaxWidth="300"
|
||||
>
|
||||
<DockPanel>
|
||||
<StackPanel DockPanel.Dock="Top" >
|
||||
<TextBlock Text="{Binding Path=Header, RelativeSource={RelativeSource AncestorType=UserControl}}"
|
||||
FontWeight="Bold" FontSize="14" Margin="5" Background="#dbe2ef"/>
|
||||
</StackPanel>
|
||||
<DockPanel>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<!--<RowDefinition Height="*"/>-->
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*" />
|
||||
<!--<ColumnDefinition Width="*" />-->
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<!--<GroupBox Grid.Row="0" Header="条件" Margin="5">
|
||||
<ListBox x:Name="ConditionsListBox" Background="#A8D8EA"/>
|
||||
</GroupBox>-->
|
||||
<GroupBox Grid.Row="0" Header="动作" Margin="5">
|
||||
<ListBox x:Name="ActionsListBox" Background="#D0F1F9"/>
|
||||
</GroupBox>
|
||||
<GroupBox Grid.Row="1" Header="触发器" Margin="5">
|
||||
<ListBox x:Name="FlipflopsListBox" Background="#FACFC1"/>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
||||
|
||||
</DockPanel>
|
||||
</DockPanel>
|
||||
</UserControl>
|
||||
@@ -1,160 +0,0 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Entity;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.NodeFlow;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Automation;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
|
||||
namespace Serein.WorkBench.Node.View
|
||||
{
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// UserControl1.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class DllControl : UserControl
|
||||
{
|
||||
private readonly NodeLibrary nodeLibrary;
|
||||
|
||||
public DllControl()
|
||||
{
|
||||
Header = "DLL文件"; // 设置初始值
|
||||
InitializeComponent();
|
||||
}
|
||||
public DllControl(NodeLibrary nodeLibrary)
|
||||
{
|
||||
this.nodeLibrary = nodeLibrary;
|
||||
Header = "DLL name : " + nodeLibrary.Assembly.GetName().Name;
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Header 依赖属性,用于绑定标题
|
||||
/// </summary>
|
||||
public string Header
|
||||
{
|
||||
get { return (string)GetValue(HeaderProperty); }
|
||||
set { SetValue(HeaderProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty HeaderProperty =
|
||||
DependencyProperty.Register("Header", typeof(string), typeof(DllControl), new PropertyMetadata(string.Empty));
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 向动作面板添加类型的文本块
|
||||
/// </summary>
|
||||
/// <param name="type">要添加的类型</param>
|
||||
public void AddAction(MethodDetails md)
|
||||
{
|
||||
AddTypeToListBox(md, ActionsListBox);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向触发器面板添加类型的文本块
|
||||
/// </summary>
|
||||
/// <param name="type">要添加的类型</param>
|
||||
public void AddFlipflop(MethodDetails md)
|
||||
{
|
||||
AddTypeToListBox(md, FlipflopsListBox);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向指定面板添加类型的文本块
|
||||
/// </summary>
|
||||
/// <param name="type">要添加的类型</param>
|
||||
/// <param name="panel">要添加到的面板</param>
|
||||
private void AddTypeToListBox(MethodDetails md, ListBox listBox)
|
||||
{
|
||||
// 创建一个新的 TextBlock 并设置其属性
|
||||
TextBlock typeText = new TextBlock
|
||||
{
|
||||
Text = $"{md.MethodTips}",
|
||||
Margin = new Thickness(10, 2, 0, 0),
|
||||
Tag = md
|
||||
};
|
||||
// 为 TextBlock 添加鼠标左键按下事件处理程序
|
||||
typeText.MouseLeftButtonDown += TypeText_MouseLeftButtonDown;
|
||||
// 为 TextBlock 添加鼠标移动事件处理程序
|
||||
typeText.MouseMove += TypeText_MouseMove;
|
||||
// 将 TextBlock 添加到指定的面板
|
||||
listBox.Items.Add(typeText);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 存储拖拽开始时的鼠标位置
|
||||
/// </summary>
|
||||
private Point _dragStartPoint;
|
||||
|
||||
/// <summary>
|
||||
/// 处理 TextBlock 的鼠标左键按下事件
|
||||
/// </summary>
|
||||
/// <param name="sender">事件源</param>
|
||||
/// <param name="e">事件参数</param>
|
||||
private void TypeText_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
// 记录鼠标按下时的位置
|
||||
_dragStartPoint = e.GetPosition(null);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 处理 TextBlock 的鼠标移动事件
|
||||
/// </summary>
|
||||
/// <param name="sender">事件源</param>
|
||||
/// <param name="e">事件参数</param>
|
||||
private void TypeText_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
// 获取当前鼠标位置
|
||||
Point mousePos = e.GetPosition(null);
|
||||
// 计算鼠标移动的距离
|
||||
Vector diff = _dragStartPoint - mousePos;
|
||||
|
||||
// 判断是否符合拖拽的最小距离要求
|
||||
if (e.LeftButton == MouseButtonState.Pressed &&
|
||||
(Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
||||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance))
|
||||
{
|
||||
// 获取触发事件的 TextBlock
|
||||
|
||||
|
||||
if (sender is TextBlock typeText && typeText.Tag is MethodDetails md)
|
||||
{
|
||||
MoveNodeData moveNodeData = new MoveNodeData
|
||||
{
|
||||
NodeControlType = md.MethodDynamicType switch
|
||||
{
|
||||
NodeType.Action => NodeControlType.Action,
|
||||
NodeType.Flipflop => NodeControlType.Flipflop,
|
||||
_ => NodeControlType.None,
|
||||
},
|
||||
MethodDetails = md,
|
||||
};
|
||||
if(moveNodeData.NodeControlType == NodeControlType.None)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
|
||||
DataObject dragData = new DataObject(MouseNodeType.CreateDllNodeInCanvas, moveNodeData);
|
||||
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,19 +0,0 @@
|
||||
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.ExpOpNodeControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
||||
MaxWidth="300">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<!--<TextBlock Grid.Row="0" Text=""></TextBlock>-->
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Vertical" Background="LightSteelBlue">
|
||||
<TextBlock Grid.Row="2" Text="表达式"></TextBlock>
|
||||
<TextBox Text="{Binding Expression, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Stretch"></TextBox>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</local:NodeControlBase>
|
||||
@@ -1,24 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.ViewModel;
|
||||
|
||||
namespace Serein.WorkBench.Node.View
|
||||
{
|
||||
/// <summary>
|
||||
/// ExprOpNodeControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ExpOpNodeControl : NodeControlBase
|
||||
{
|
||||
public ExpOpNodeControl() : base()
|
||||
{
|
||||
// 窗体初始化需要
|
||||
ViewModel = new ExpOpNodeViewModel(new SingleExpOpNode());
|
||||
DataContext = ViewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
public ExpOpNodeControl(ExpOpNodeViewModel viewModel) :base(viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
<local:NodeControlBase x:Class="Serein.WorkBench.Node.View.FlipflopNodeControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:Converters="clr-namespace:Serein.WorkBench.Tool.Converters"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Node.View"
|
||||
xmlns:vm="clr-namespace:Serein.WorkBench.Node.ViewModel"
|
||||
xmlns:themes="clr-namespace:Serein.WorkBench.Themes"
|
||||
MaxWidth="300">
|
||||
|
||||
<UserControl.Resources>
|
||||
<vm:TypeToStringConverter x:Key="TypeToStringConverter"/>
|
||||
<!--<themes:ConditionControl x:Key="ConditionControl"/>-->
|
||||
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Border BorderBrush="#FCB334" BorderThickness="1">
|
||||
|
||||
|
||||
<Grid>
|
||||
<Grid.ToolTip>
|
||||
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding MethodDetails.MethodName, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid.ToolTip>
|
||||
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Background="#FCB334">
|
||||
<CheckBox IsChecked="{Binding DebugSetting.IsEnable, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
||||
<CheckBox IsChecked="{Binding MethodDetails.IsProtectionParameter, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
||||
|
||||
<TextBlock Text="{Binding MethodDetails.MethodTips, UpdateSourceTrigger=PropertyChanged}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</StackPanel>
|
||||
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding MethodDetails}" />
|
||||
|
||||
<Border Grid.Row="1" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
|
||||
Visibility="{Binding MethodDetails.IsProtectionParameter, Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" />
|
||||
<!--<Border Grid.Row="0" Background="#FCB334" >
|
||||
|
||||
</Border>-->
|
||||
<!--<themes:ExplicitDataControl Grid.Row="1" ExplicitDatas="{Binding ExplicitDatas}" />-->
|
||||
<Grid Grid.Row="2" Background="#D5F0FC" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Border Grid.Column="0" BorderThickness="1">
|
||||
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
<Border Grid.Column="1" BorderThickness="1">
|
||||
<TextBlock Text="{Binding MethodDetails.ReturnType}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
</Border>
|
||||
</Grid>
|
||||
<!--<themes:ConditionControl Grid.Row="2" ></themes:ConditionControl>-->
|
||||
</Grid>
|
||||
</Border>
|
||||
</local:NodeControlBase>
|
||||
@@ -1,17 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.ViewModel;
|
||||
|
||||
namespace Serein.WorkBench.Node.View
|
||||
{
|
||||
/// <summary>
|
||||
/// StateNode.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class FlipflopNodeControl : NodeControlBase
|
||||
{
|
||||
public FlipflopNodeControl(FlipflopNodeControlViewModel viewModel) : base(viewModel)
|
||||
{
|
||||
DataContext = viewModel;
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
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;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Serein.WorkBench.Node.View
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 节点控件基类(控件)
|
||||
/// </summary>
|
||||
public abstract class NodeControlBase : UserControl, IDynamicFlowNode
|
||||
{
|
||||
public NodeControlViewModelBase ViewModel { get; set; }
|
||||
|
||||
|
||||
protected NodeControlBase()
|
||||
|
||||
{
|
||||
this.Background = Brushes.Transparent;
|
||||
}
|
||||
protected NodeControlBase(NodeControlViewModelBase viewModelBase)
|
||||
{
|
||||
ViewModel = viewModelBase;
|
||||
this.Background = Brushes.Transparent;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class FLowNodeObObservableCollection<T> : ObservableCollection<T>
|
||||
{
|
||||
|
||||
public void AddRange(IEnumerable<T> items)
|
||||
{
|
||||
foreach (var item in items)
|
||||
{
|
||||
this.Items.Add(item);
|
||||
}
|
||||
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.View;
|
||||
|
||||
namespace Serein.WorkBench.Node.ViewModel
|
||||
{
|
||||
public class ActionNodeControlViewModel : NodeControlViewModelBase
|
||||
{
|
||||
private readonly SingleActionNode node;
|
||||
|
||||
public ActionNodeControlViewModel(SingleActionNode node):base(node)
|
||||
{
|
||||
this.node = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.View;
|
||||
|
||||
namespace Serein.WorkBench.Node.ViewModel
|
||||
{
|
||||
public class ConditionNodeControlViewModel : NodeControlViewModelBase
|
||||
{
|
||||
private readonly SingleConditionNode singleConditionNode;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为自定义参数
|
||||
/// </summary>
|
||||
public bool IsCustomData
|
||||
{
|
||||
get => singleConditionNode.IsCustomData;
|
||||
set { singleConditionNode.IsCustomData= value; OnPropertyChanged(); }
|
||||
}
|
||||
/// <summary>
|
||||
/// 自定义参数值
|
||||
/// </summary>
|
||||
public object? CustomData
|
||||
{
|
||||
get => singleConditionNode.CustomData;
|
||||
set { singleConditionNode.CustomData = value ; OnPropertyChanged(); }
|
||||
}
|
||||
/// <summary>
|
||||
/// 表达式
|
||||
/// </summary>
|
||||
public string Expression
|
||||
{
|
||||
get => singleConditionNode.Expression;
|
||||
set { singleConditionNode.Expression = value; OnPropertyChanged(); }
|
||||
}
|
||||
|
||||
public ConditionNodeControlViewModel(SingleConditionNode node) : base(node)
|
||||
{
|
||||
this.singleConditionNode = node;
|
||||
//IsCustomData = false;
|
||||
//CustomData = "";
|
||||
//Expression = "PASS";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.View;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.WorkBench.Node.ViewModel
|
||||
{
|
||||
public class ConditionRegionNodeControlViewModel : NodeControlViewModelBase
|
||||
{
|
||||
public ConditionRegionNodeControlViewModel(CompositeConditionNode node):base(node)
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.View;
|
||||
|
||||
namespace Serein.WorkBench.Node.ViewModel
|
||||
{
|
||||
public class ExpOpNodeViewModel: NodeControlViewModelBase
|
||||
{
|
||||
public readonly SingleExpOpNode node;
|
||||
|
||||
public string Expression
|
||||
{
|
||||
get => node.Expression;
|
||||
set
|
||||
{
|
||||
node.Expression = value;
|
||||
OnPropertyChanged();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public ExpOpNodeViewModel(SingleExpOpNode node) : base(node)
|
||||
{
|
||||
this.node = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.WorkBench.Node.View;
|
||||
|
||||
namespace Serein.WorkBench.Node.ViewModel
|
||||
{
|
||||
public class FlipflopNodeControlViewModel : NodeControlViewModelBase
|
||||
{
|
||||
private readonly SingleFlipflopNode node;
|
||||
public FlipflopNodeControlViewModel(SingleFlipflopNode node) : base(node)
|
||||
{
|
||||
this.node = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Serein.WorkBench.Node.ViewModel
|
||||
{
|
||||
public class TypeToStringConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (value is Type type)
|
||||
{
|
||||
return type.ToString();
|
||||
}
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>WinExe</OutputType>
|
||||
<TargetFramework>net8.0-windows</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UseWPF>true</UseWPF>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Library.Core\Serein.Library.Core.csproj" />
|
||||
<ProjectReference Include="..\Library\Serein.Library.csproj" />
|
||||
<ProjectReference Include="..\NodeFlow\Serein.NodeFlow.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Update="Node\View\ActionRegionControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Node\View\ConditionRegionControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Node\View\DllControlControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Node\View\ExpOpNodeControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Themes\InputDialog.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Themes\IOCObjectViewControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Themes\MethodDetailsControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Themes\NodeTreeItemViewControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Themes\NodeTreeViewControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Themes\ObjectViewerControl.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
<Compile Update="Themes\TypeViewerWindow.xaml.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,16 +0,0 @@
|
||||
<UserControl x:Class="DynamicDemo.Themes.Condition.BoolConditionControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:DynamicDemo.Themes.Condition"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<ComboBox x:Name="ConditionComboBox"
|
||||
SelectedValue="{Binding Condition, Mode=TwoWay}">
|
||||
<ComboBoxItem Content="Is True" Tag="IsTrue" />
|
||||
<ComboBoxItem Content="Is False" Tag="IsFalse" />
|
||||
</ComboBox>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace DynamicDemo.Themes.Condition
|
||||
{
|
||||
/// <summary>
|
||||
/// BoolConditionControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class BoolConditionControl : UserControl
|
||||
{
|
||||
public BoolConditionControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<UserControl x:Class="DynamicDemo.Themes.Condition.IntConditionControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:DynamicDemo.Themes.Condition"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<ComboBox x:Name="ConditionComboBox"
|
||||
SelectedValue="{Binding Condition, Mode=TwoWay}">
|
||||
<ComboBoxItem Content="Greater Than" Tag="GreaterThan" />
|
||||
<ComboBoxItem Content="Less Than" Tag="LessThan" />
|
||||
<ComboBoxItem Content="Equal To" Tag="EqualTo" />
|
||||
<ComboBoxItem Content="Between" Tag="Between" />
|
||||
<ComboBoxItem Content="Not Between" Tag="NotBetween" />
|
||||
<ComboBoxItem Content="Not In Range" Tag="NotInRange" />
|
||||
</ComboBox>
|
||||
<TextBox x:Name="ValueTextBox" Text="{Binding Value, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace DynamicDemo.Themes.Condition
|
||||
{
|
||||
/// <summary>
|
||||
/// IntConditionControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class IntConditionControl : UserControl
|
||||
{
|
||||
public IntConditionControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace DynamicDemo.Themes.Condition
|
||||
{
|
||||
//public class IntConditionNode : ConditionNode
|
||||
//{
|
||||
// public int Value { get; set; }
|
||||
// public int MinValue { get; set; }
|
||||
// public int MaxValue { get; set; }
|
||||
// public List<int> ExcludeValues { get; set; }
|
||||
|
||||
// public override bool Evaluate(object value)
|
||||
// {
|
||||
// if (value is int intValue)
|
||||
// {
|
||||
// switch (Condition)
|
||||
// {
|
||||
// case ConditionType.GreaterThan:
|
||||
// return intValue > Value;
|
||||
// case ConditionType.LessThan:
|
||||
// return intValue < Value;
|
||||
// case ConditionType.EqualTo:
|
||||
// return intValue == Value;
|
||||
// case ConditionType.Between:
|
||||
// return intValue >= MinValue && intValue <= MaxValue;
|
||||
// case ConditionType.NotBetween:
|
||||
// return intValue < MinValue || intValue > MaxValue;
|
||||
// case ConditionType.NotInRange:
|
||||
// return !ExcludeValues.Contains(intValue);
|
||||
// default:
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
|
||||
//public class BoolConditionNode : ConditionNode
|
||||
//{
|
||||
// public override bool Evaluate(object value)
|
||||
// {
|
||||
// if (value is bool boolValue)
|
||||
// {
|
||||
// switch (Condition)
|
||||
// {
|
||||
// case ConditionType.IsTrue:
|
||||
// return boolValue;
|
||||
// case ConditionType.IsFalse:
|
||||
// return !boolValue;
|
||||
// default:
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
//public class StringConditionNode : ConditionNode
|
||||
//{
|
||||
// public string Substring { get; set; }
|
||||
|
||||
// public override bool Evaluate(object value)
|
||||
// {
|
||||
// if (value is string stringValue)
|
||||
// {
|
||||
// switch (Condition)
|
||||
// {
|
||||
// case ConditionType.Contains:
|
||||
// return stringValue.Contains(Substring);
|
||||
// case ConditionType.DoesNotContain:
|
||||
// return !stringValue.Contains(Substring);
|
||||
// case ConditionType.IsNotEmpty:
|
||||
// return !string.IsNullOrEmpty(stringValue);
|
||||
// default:
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
// return false;
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
<UserControl x:Class="DynamicDemo.Themes.Condition.StringConditionControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:DynamicDemo.Themes.Condition"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<ComboBox x:Name="ConditionComboBox"
|
||||
SelectedValue="{Binding Condition, Mode=TwoWay}">
|
||||
<ComboBoxItem Content="Contains" Tag="Contains" />
|
||||
<ComboBoxItem Content="Does Not Contain" Tag="DoesNotContain" />
|
||||
<ComboBoxItem Content="Is Not Empty" Tag="IsNotEmpty" />
|
||||
</ComboBox>
|
||||
<TextBox x:Name="SubstringTextBox" Text="{Binding Substring, Mode=TwoWay}" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,28 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace DynamicDemo.Themes.Condition
|
||||
{
|
||||
/// <summary>
|
||||
/// StringConditionControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class StringConditionControl : UserControl
|
||||
{
|
||||
public StringConditionControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
<UserControl x:Class="DynamicDemo.Themes.ConditionControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:DynamicDemo.Themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<!--<ComboBox x:Name="ConditionTypeComboBox" SelectionChanged="ConditionTypeComboBox_SelectionChanged">
|
||||
<ComboBoxItem Content="GreaterThan" Tag="{x:Static local:ConditionType.GreaterThan}"/>
|
||||
<ComboBoxItem Content="LessThan" Tag="{x:Static local:ConditionType.LessThan}"/>
|
||||
<ComboBoxItem Content="EqualTo" Tag="{x:Static local:ConditionType.EqualTo}"/>
|
||||
<ComboBoxItem Content="InRange" Tag="{x:Static local:ConditionType.InRange}"/>
|
||||
<ComboBoxItem Content="NotInRange" Tag="{x:Static local:ConditionType.NotInRange}"/>
|
||||
<ComboBoxItem Content="NotInSpecificRange" Tag="{x:Static local:ConditionType.NotInSpecificRange}"/>
|
||||
<ComboBoxItem Content="IsTrue" Tag="{x:Static local:ConditionType.IsTrue}"/>
|
||||
<ComboBoxItem Content="IsFalse" Tag="{x:Static local:ConditionType.IsFalse}"/>
|
||||
<ComboBoxItem Content="Contains" Tag="{x:Static local:ConditionType.Contains}"/>
|
||||
<ComboBoxItem Content="DoesNotContain" Tag="{x:Static local:ConditionType.DoesNotContain}"/>
|
||||
<ComboBoxItem Content="IsNotEmpty" Tag="{x:Static local:ConditionType.IsNotEmpty}"/>
|
||||
</ComboBox>
|
||||
<TextBox x:Name="ValueTextBox" Visibility="Collapsed"/>
|
||||
<TextBox x:Name="Value2TextBox" Visibility="Collapsed"/>-->
|
||||
|
||||
<StackPanel Grid.Row="0" x:Name="ConditionsPanel" Orientation="Vertical" Height="400"/>
|
||||
<Button Grid.Row="1" Content="Add Condition" Click="OnAddConditionClicked" />
|
||||
<!-- 其他控件 -->
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,85 +0,0 @@
|
||||
using DynamicDemo.Themes.Condition;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
namespace DynamicDemo.Themes
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// ConditionControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ConditionControl : UserControl
|
||||
{
|
||||
public ConditionControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
//private void ConditionTypeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||||
//{
|
||||
// var selectedType = (ConditionType)((ComboBoxItem)ConditionTypeComboBox.SelectedItem).Tag;
|
||||
// UpdateInputVisibility(selectedType);
|
||||
//}
|
||||
|
||||
//private void UpdateInputVisibility(ConditionType type)
|
||||
//{
|
||||
// ValueTextBox.Visibility = Visibility.Collapsed;
|
||||
// Value2TextBox.Visibility = Visibility.Collapsed;
|
||||
|
||||
// switch (type)
|
||||
// {
|
||||
// case ConditionType.GreaterThan:
|
||||
// case ConditionType.LessThan:
|
||||
// case ConditionType.EqualTo:
|
||||
// case ConditionType.Contains:
|
||||
// case ConditionType.DoesNotContain:
|
||||
// ValueTextBox.Visibility = Visibility.Visible;
|
||||
// break;
|
||||
// case ConditionType.InRange:
|
||||
// case ConditionType.NotInRange:
|
||||
// ValueTextBox.Visibility = Visibility.Visible;
|
||||
// Value2TextBox.Visibility = Visibility.Visible;
|
||||
// break;
|
||||
// case ConditionType.IsTrue:
|
||||
// case ConditionType.IsFalse:
|
||||
// case ConditionType.IsNotEmpty:
|
||||
// // No additional input needed
|
||||
// break;
|
||||
// case ConditionType.NotInSpecificRange:
|
||||
// // Handle specific range input, possibly with a different control
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
|
||||
private void OnAddConditionClicked(object sender, RoutedEventArgs e)
|
||||
{
|
||||
// 示例:添加一个IntConditionNode
|
||||
var intConditionNode = new IntConditionNode { Condition = ConditionType.GreaterThan, Value = 10 };
|
||||
AddConditionNode(intConditionNode);
|
||||
}
|
||||
|
||||
public void AddConditionNode(ConditionNode node)
|
||||
{
|
||||
UserControl control = null;
|
||||
|
||||
if (node is IntConditionNode)
|
||||
{
|
||||
control = new IntConditionControl { DataContext = node };
|
||||
}
|
||||
else if (node is BoolConditionNode)
|
||||
{
|
||||
control = new BoolConditionControl { DataContext = node };
|
||||
}
|
||||
else if (node is StringConditionNode)
|
||||
{
|
||||
control = new StringConditionControl { DataContext = node };
|
||||
}
|
||||
|
||||
if (control != null)
|
||||
{
|
||||
ConditionsPanel.Children.Add(control);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
namespace DynamicDemo.Themes;
|
||||
|
||||
public enum ConditionType
|
||||
{
|
||||
GreaterThan,
|
||||
LessThan,
|
||||
EqualTo,
|
||||
Between,
|
||||
NotBetween,
|
||||
NotInRange,
|
||||
IsTrue,
|
||||
IsFalse,
|
||||
Contains,
|
||||
DoesNotContain,
|
||||
IsNotEmpty
|
||||
}
|
||||
|
||||
public abstract class ConditionNode
|
||||
{
|
||||
public ConditionType Condition { get; set; }
|
||||
public abstract bool Evaluate(object value);
|
||||
}
|
||||
|
||||
public class IntConditionNode : ConditionNode
|
||||
{
|
||||
public int Value { get; set; }
|
||||
public int MinValue { get; set; }
|
||||
public int MaxValue { get; set; }
|
||||
public List<int> ExcludeValues { get; set; }
|
||||
|
||||
public override bool Evaluate(object value)
|
||||
{
|
||||
if (value is int intValue)
|
||||
{
|
||||
switch (Condition)
|
||||
{
|
||||
case ConditionType.GreaterThan:
|
||||
return intValue > Value;
|
||||
case ConditionType.LessThan:
|
||||
return intValue < Value;
|
||||
case ConditionType.EqualTo:
|
||||
return intValue == Value;
|
||||
case ConditionType.Between:
|
||||
return intValue >= MinValue && intValue <= MaxValue;
|
||||
case ConditionType.NotBetween:
|
||||
return intValue < MinValue || intValue > MaxValue;
|
||||
case ConditionType.NotInRange:
|
||||
return !ExcludeValues.Contains(intValue);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class BoolConditionNode : ConditionNode
|
||||
{
|
||||
public override bool Evaluate(object value)
|
||||
{
|
||||
if (value is bool boolValue)
|
||||
{
|
||||
switch (Condition)
|
||||
{
|
||||
case ConditionType.IsTrue:
|
||||
return boolValue;
|
||||
case ConditionType.IsFalse:
|
||||
return !boolValue;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public class StringConditionNode : ConditionNode
|
||||
{
|
||||
public string Substring { get; set; }
|
||||
|
||||
public override bool Evaluate(object value)
|
||||
{
|
||||
if (value is string stringValue)
|
||||
{
|
||||
switch (Condition)
|
||||
{
|
||||
case ConditionType.Contains:
|
||||
return stringValue.Contains(Substring);
|
||||
case ConditionType.DoesNotContain:
|
||||
return !stringValue.Contains(Substring);
|
||||
case ConditionType.IsNotEmpty:
|
||||
return !string.IsNullOrEmpty(stringValue);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<UserControl x:Class="Serein.WorkBench.Themes.IOCObjectViewControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<!--<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>-->
|
||||
</Grid.RowDefinitions>
|
||||
<GroupBox Grid.Row="1" Header="实例视图" Margin="5">
|
||||
<ListBox x:Name="DependenciesListBox" Background="#E3FAE9"/>
|
||||
</GroupBox>
|
||||
<!--<GroupBox Grid.Row="0" Header="正在注册的类型" Margin="5">
|
||||
<ListBox x:Name="TypeListBox" Background="#E3F6FA"/>
|
||||
</GroupBox>-->
|
||||
<!--<GroupBox Grid.Row="1" Header="实例视图" Margin="5">
|
||||
<ListBox x:Name="DependenciesListBox" Background="#E3FAE9"/>
|
||||
</GroupBox>-->
|
||||
<!--<GroupBox Grid.Row="3" Header="未完成注入的实例" Margin="5">
|
||||
<ListBox x:Name="UnfinishedDependenciesListBox" Background="#FFE9D7"/>
|
||||
</GroupBox>-->
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,120 +0,0 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// IOCObjectViewControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class IOCObjectViewControl : UserControl
|
||||
{
|
||||
public Action<string,object> SelectObj { get; set; }
|
||||
|
||||
public IOCObjectViewControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private class IOCObj
|
||||
{
|
||||
public string Key { get; set; }
|
||||
public object Instance { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行环境
|
||||
/// </summary>
|
||||
public IFlowEnvironment FlowEnvironment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 添加一个实例
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="instance"></param>
|
||||
public void AddDependenciesInstance(string key,object instance)
|
||||
{
|
||||
IOCObj iOCObj = new IOCObj
|
||||
{
|
||||
Key = key,
|
||||
Instance = instance,
|
||||
};
|
||||
TextBlock textBlock = new TextBlock();
|
||||
textBlock.Text = key;
|
||||
textBlock.Tag = iOCObj;
|
||||
textBlock.MouseDown += (s, e) =>
|
||||
{
|
||||
if(s is TextBlock block && block.Tag is IOCObj iocObj)
|
||||
{
|
||||
SelectObj?.Invoke(iocObj.Key, iocObj.Instance);
|
||||
//FlowEnvironment.SetMonitorObjState(iocObj.Instance, true); // 通知环境,该节点的数据更新后需要传到UI
|
||||
}
|
||||
};
|
||||
DependenciesListBox.Items.Add(textBlock);
|
||||
SortLisbox(DependenciesListBox);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新一个实例
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="instance"></param>
|
||||
public void RefreshDependenciesInstance(string key, object instance)
|
||||
{
|
||||
foreach (var item in DependenciesListBox.Items)
|
||||
{
|
||||
if (item is TextBlock block && block.Tag is IOCObj iocObj && iocObj.Key.Equals(key))
|
||||
{
|
||||
iocObj.Instance = instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void ClearObjItem()
|
||||
{
|
||||
DependenciesListBox.Items.Clear();
|
||||
}
|
||||
|
||||
private static void SortLisbox(ListBox listBox)
|
||||
{
|
||||
var sortedItems = listBox.Items.Cast<TextBlock>().OrderBy(x => x.Text).ToList();
|
||||
listBox.Items.Clear();
|
||||
foreach (var item in sortedItems)
|
||||
{
|
||||
listBox.Items.Add(item);
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveDependenciesInstance(string key)
|
||||
{
|
||||
object? itemControl = null;
|
||||
foreach (var item in DependenciesListBox.Items)
|
||||
{
|
||||
if (item is TextBlock block && block.Tag is IOCObj iocObj && iocObj.Key.Equals(key))
|
||||
{
|
||||
itemControl = item;
|
||||
}
|
||||
}
|
||||
if (itemControl is not null)
|
||||
{
|
||||
DependenciesListBox.Items.Remove(itemControl);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<Window x:Class="Serein.WorkBench.Themes.InputDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Themes"
|
||||
mc:Ignorable="d"
|
||||
Title="InputDialog" Height="450" Width="800">
|
||||
<StackPanel Margin="10">
|
||||
<TextBox x:Name="InputTextBox" Width="200" Margin="0,0,0,10" />
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Content="确认" Click="ConfirmButton_Click" Margin="5" />
|
||||
<Button Content="取消" Click="CancelButton_Click" Margin="5" />
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Window>
|
||||
@@ -1,42 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// InputDialog.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class InputDialog : Window
|
||||
{
|
||||
public string InputValue { get; private set; }
|
||||
|
||||
public InputDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void ConfirmButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
InputValue = InputTextBox.Text;
|
||||
DialogResult = true; // 设置返回结果为 true
|
||||
Close(); // 关闭窗口
|
||||
}
|
||||
|
||||
private void CancelButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
DialogResult = false; // 设置返回结果为 false
|
||||
Close(); // 关闭窗口
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<ResourceDictionary
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Themes"
|
||||
xmlns:sys="clr-namespace:System;assembly=mscorlib">
|
||||
|
||||
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<Style TargetType="{x:Type local:MethodDetailsControl}">
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type local:MethodDetailsControl}">
|
||||
|
||||
<ItemsControl ItemsSource="{Binding MethodDetails.ParameterDetailss, RelativeSource={RelativeSource TemplatedParent}}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<ContentControl Content="{Binding}">
|
||||
<ContentControl.Style>
|
||||
<Style TargetType="ContentControl">
|
||||
<Style.Triggers>
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsExplicitData}" Value="false" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<Grid Background="#E3FDFD">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
||||
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
<TextBlock Grid.Column="3" MinWidth="50" Text="无须指定参数"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiDataTrigger>
|
||||
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsExplicitData}" Value="true" />
|
||||
<Condition Binding="{Binding ExplicitTypeName}" Value="Select" />
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<Grid Background="#E3FDFD">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
||||
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
<ComboBox Grid.Column="3"
|
||||
MinWidth="50"
|
||||
ItemsSource="{Binding Items}"
|
||||
SelectedItem="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiDataTrigger>
|
||||
|
||||
<MultiDataTrigger>
|
||||
<MultiDataTrigger.Conditions>
|
||||
<Condition Binding="{Binding IsExplicitData}" Value="true" />
|
||||
<Condition Binding="{Binding ExplicitTypeName}" Value="Value" />
|
||||
<!--<Condition Binding="{Binding ExplicitTypeName}" Value="{x:Type sys:String}" />
|
||||
<Condition Binding="{Binding ExplicitTypeName}" Value="{x:Type sys:Double}" />-->
|
||||
</MultiDataTrigger.Conditions>
|
||||
<Setter Property="ContentTemplate">
|
||||
<Setter.Value>
|
||||
<DataTemplate>
|
||||
<Grid Background="#E3FDFD">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
<ColumnDefinition Width="50"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
||||
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||
<TextBox Grid.Column="3" MinWidth="50" Text="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||
</Grid>
|
||||
</DataTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</MultiDataTrigger>
|
||||
|
||||
</Style.Triggers>
|
||||
</Style>
|
||||
</ContentControl.Style>
|
||||
</ContentControl>
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -1,64 +0,0 @@
|
||||
using Serein.Library.Entity;
|
||||
using Serein.NodeFlow;
|
||||
using System.Collections;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
public class MultiConditionConverter : IMultiValueConverter
|
||||
{
|
||||
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
if (values.Length == 2 && values[0] is Type valueType && values[1] is bool isEnabled)
|
||||
{
|
||||
if (isEnabled)
|
||||
{
|
||||
if (valueType == typeof(string) || valueType == typeof(int) || valueType == typeof(double))
|
||||
{
|
||||
return "TextBoxTemplate";
|
||||
}
|
||||
else if (typeof(IEnumerable).IsAssignableFrom(valueType))
|
||||
{
|
||||
return "ComboBoxTemplate";
|
||||
}
|
||||
}
|
||||
}
|
||||
return DependencyProperty.UnsetValue;
|
||||
}
|
||||
|
||||
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
public partial class MethodDetailsControl : UserControl//,ItemsControl
|
||||
{
|
||||
static MethodDetailsControl()
|
||||
{
|
||||
DefaultStyleKeyProperty.OverrideMetadata(typeof(MethodDetailsControl), new FrameworkPropertyMetadata(typeof(MethodDetailsControl)));
|
||||
}
|
||||
|
||||
|
||||
public MethodDetails MethodDetails
|
||||
{
|
||||
get { return (MethodDetails)GetValue(MethodDetailsProperty); }
|
||||
set { SetValue(MethodDetailsProperty, value); }
|
||||
}
|
||||
|
||||
public static readonly DependencyProperty MethodDetailsProperty = DependencyProperty.Register("MethodDetails", typeof(MethodDetails),
|
||||
typeof(MethodDetailsControl), new PropertyMetadata(null, new PropertyChangedCallback(OnPropertyChange)));
|
||||
|
||||
static void OnPropertyChange(DependencyObject sender, DependencyPropertyChangedEventArgs args)
|
||||
{
|
||||
|
||||
var MethodDetails = (MethodDetails)args.NewValue;
|
||||
//MethodDetails.ExplicitDatas[0].
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
|
||||
|
||||
</ResourceDictionary>
|
||||
@@ -1,59 +0,0 @@
|
||||
<UserControl x:Class="Serein.WorkBench.Themes.NodeTreeItemViewControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="400" d:DesignWidth="200">
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="CustomTreeViewItemStyle" TargetType="TreeViewItem">
|
||||
<Setter Property="SnapsToDevicePixels" Value="true" />
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
|
||||
|
||||
</Style>
|
||||
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<Grid Grid.Row="0" x:Name="UpstreamTreeGuid" Margin="0,0,0,0" >
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="0" Width="1" x:Name="UpstreamTreeRectangle" Grid.Row="0" Fill="#4A82E4" Margin="4,1,4,1" IsHitTestVisible="False"/>
|
||||
<TreeView Grid.Column="1" x:Name="UpstreamTreeNodes" BorderThickness="0" ItemContainerStyle="{StaticResource CustomTreeViewItemStyle}"/>
|
||||
</Grid>
|
||||
<Grid Grid.Row="1" x:Name="IsSucceedTreeGuid" Margin="0,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="0" Width="1" x:Name="IsSucceedRectangle" Grid.Row="0" Fill="#04FC10" Margin="4,1,4,1" IsHitTestVisible="False"/>
|
||||
<TreeView Grid.Column="1" x:Name="IsSucceedTreeNodes" BorderThickness="0" ItemContainerStyle="{StaticResource CustomTreeViewItemStyle}"/>
|
||||
</Grid>
|
||||
<Grid Grid.Row="2" x:Name="IsFailTreeGuid" Margin="0,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="0" Width="1" x:Name="IsFailRectangle" Grid.Row="0" Fill="#F18905" Margin="4,1,4,1" IsHitTestVisible="False"/>
|
||||
|
||||
<TreeView Grid.Column="1" x:Name="IsFailTreeNodes" BorderThickness="0" ItemContainerStyle="{StaticResource CustomTreeViewItemStyle}"/>
|
||||
</Grid>
|
||||
<Grid Grid.Row="3" x:Name="IsErrorTreeGuid" Margin="0,0,0,0">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="auto"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Rectangle Grid.Column="0" Width="1" x:Name="IsErrorRectangle" Grid.Row="0" Fill="#FE1343" Margin="4,1,4,1" IsHitTestVisible="False"/>
|
||||
<TreeView Grid.Column="1" x:Name="IsErrorTreeNodes" BorderThickness="0" ItemContainerStyle="{StaticResource CustomTreeViewItemStyle}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,289 +0,0 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Enums;
|
||||
using Serein.NodeFlow;
|
||||
using Serein.NodeFlow.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Xml.Linq;
|
||||
using static Serein.WorkBench.Themes.TypeViewerWindow;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// NodeTreeVIewControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class NodeTreeItemViewControl : UserControl
|
||||
{
|
||||
public NodeTreeItemViewControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
foreach (var ct in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
var guid = ToGridView(this, ct);
|
||||
guid.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 保存的节点数据
|
||||
/// </summary>
|
||||
private NodeModelBase nodeModel;
|
||||
private IFlowEnvironment flowEnvironment { get; set; }
|
||||
|
||||
|
||||
private class NodeTreeModel
|
||||
{
|
||||
public NodeModelBase RootNode { get; set; }
|
||||
public Dictionary<ConnectionType, List<NodeModelBase>> ChildNodes { get; set; }
|
||||
}
|
||||
|
||||
|
||||
public void InitAndLoadTree(IFlowEnvironment flowEnvironment, NodeModelBase nodeModel)
|
||||
{
|
||||
this.flowEnvironment = flowEnvironment;
|
||||
this.nodeModel = nodeModel;
|
||||
RefreshTree();
|
||||
}
|
||||
|
||||
public TreeViewItem RefreshTree()
|
||||
{
|
||||
NodeModelBase rootNodeModel = this.nodeModel;
|
||||
NodeTreeModel nodeTreeModel = new NodeTreeModel
|
||||
{
|
||||
RootNode = rootNodeModel,
|
||||
ChildNodes = new Dictionary<ConnectionType, List<NodeModelBase>>()
|
||||
{
|
||||
{ConnectionType.Upstream, []},
|
||||
{ConnectionType.IsSucceed, [rootNodeModel]},
|
||||
{ConnectionType.IsFail, []},
|
||||
{ConnectionType.IsError, []},
|
||||
}
|
||||
};
|
||||
string? itemName = rootNodeModel.MethodDetails?.MethodTips;
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
{
|
||||
itemName = rootNodeModel.ControlType.ToString();
|
||||
}
|
||||
var rootNode = new TreeViewItem
|
||||
{
|
||||
Header = itemName,
|
||||
Tag = nodeTreeModel,
|
||||
};
|
||||
LoadNodeItem(this, nodeTreeModel);
|
||||
rootNode.Expanded += TreeViewItem_Expanded; // 监听展开事件
|
||||
rootNode.IsExpanded = true;
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 展开子项事件
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is TreeViewItem item && item.Tag is NodeTreeModel nodeTreeModel)
|
||||
{
|
||||
item.Items.Clear();
|
||||
NodeTreeItemViewControl? nodeTreeItemViewControl = LoadTNoderee(nodeTreeModel);
|
||||
|
||||
if (nodeTreeItemViewControl is not null)
|
||||
{
|
||||
LoadNodeItem(nodeTreeItemViewControl, nodeTreeModel);
|
||||
item.Items.Add(nodeTreeItemViewControl);
|
||||
|
||||
}
|
||||
item.IsSelected = false;
|
||||
}
|
||||
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载面板
|
||||
/// </summary>
|
||||
/// <param name="nodeTreeItemViewControl"></param>
|
||||
/// <param name="nodeTreeModel"></param>
|
||||
private void LoadNodeItem(NodeTreeItemViewControl nodeTreeItemViewControl, NodeTreeModel nodeTreeModel)
|
||||
{
|
||||
|
||||
foreach (var ct in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
var treeViewer = ToTreeView(nodeTreeItemViewControl, ct);
|
||||
var guid = ToGridView(nodeTreeItemViewControl, ct);
|
||||
treeViewer.Items.Clear(); // 移除对象树的所有节点
|
||||
var list = nodeTreeModel.ChildNodes[ct];
|
||||
|
||||
if (list.Count > 0)
|
||||
{
|
||||
foreach (var child in list)
|
||||
{
|
||||
NodeTreeModel tmpNodeTreeModel = new NodeTreeModel
|
||||
{
|
||||
RootNode = child,
|
||||
ChildNodes = child.SuccessorNodes,
|
||||
};
|
||||
string? itemName = child?.MethodDetails?.MethodTips;
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
{
|
||||
itemName = child?.ControlType.ToString();
|
||||
}
|
||||
TreeViewItem treeViewItem = new TreeViewItem
|
||||
{
|
||||
Header = itemName,
|
||||
Tag = tmpNodeTreeModel
|
||||
};
|
||||
treeViewItem.Expanded += TreeViewItem_Expanded;
|
||||
|
||||
var contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem("从此节点执行", (s, e) =>
|
||||
{
|
||||
flowEnvironment.StartFlowInSelectNodeAsync(tmpNodeTreeModel.RootNode.Guid);
|
||||
}));
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem("定位", (s, e) => flowEnvironment.NodeLocated(tmpNodeTreeModel.RootNode.Guid)));
|
||||
|
||||
treeViewItem.ContextMenu = contextMenu;
|
||||
treeViewItem.Margin = new Thickness(-20, 0, 0, 0);
|
||||
treeViewer.Items.Add(treeViewItem);
|
||||
}
|
||||
guid.Visibility = Visibility.Visible;
|
||||
}
|
||||
else
|
||||
{
|
||||
guid.Visibility = Visibility.Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载节点子项
|
||||
/// </summary>
|
||||
/// <param name="nodeTreeModel"></param>
|
||||
/// <returns></returns>
|
||||
private NodeTreeItemViewControl? LoadTNoderee(NodeTreeModel nodeTreeModel)
|
||||
{
|
||||
NodeTreeItemViewControl nodeTreeItemViewControl = null;
|
||||
foreach (var connectionType in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
var childNodeModels = nodeTreeModel.ChildNodes[connectionType];
|
||||
if (childNodeModels.Count > 0)
|
||||
{
|
||||
nodeTreeItemViewControl ??= new NodeTreeItemViewControl();
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeView treeView = ToTreeView(nodeTreeItemViewControl, connectionType);
|
||||
foreach (var childNodeModel in childNodeModels)
|
||||
{
|
||||
NodeTreeModel tempNodeTreeModel = new NodeTreeModel
|
||||
{
|
||||
RootNode = childNodeModel,
|
||||
ChildNodes = childNodeModel.SuccessorNodes,
|
||||
};
|
||||
|
||||
string? itemName = childNodeModel?.MethodDetails?.MethodTips;
|
||||
if (string.IsNullOrEmpty(itemName))
|
||||
{
|
||||
itemName = childNodeModel?.ControlType.ToString();
|
||||
}
|
||||
TreeViewItem treeViewItem = new TreeViewItem
|
||||
{
|
||||
Header = itemName,
|
||||
Tag = tempNodeTreeModel
|
||||
};
|
||||
treeViewItem.Margin = new Thickness(-20, 0, 0, 0);
|
||||
treeViewItem.Visibility = Visibility.Visible;
|
||||
treeView.Items.Add(treeViewItem);
|
||||
}
|
||||
}
|
||||
if (nodeTreeItemViewControl is not null)
|
||||
{
|
||||
foreach (var connectionType in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
var childNodeModels = nodeTreeModel.ChildNodes[connectionType];
|
||||
if (childNodeModels.Count > 0)
|
||||
{
|
||||
nodeTreeItemViewControl ??= new NodeTreeItemViewControl();
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodeTreeItemViewControl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 折叠事件
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is TreeViewItem item && item.Items.Count > 0)
|
||||
{
|
||||
item.Items.Clear();
|
||||
}
|
||||
}
|
||||
public static TreeView ToTreeView(NodeTreeItemViewControl item, ConnectionType connectionType)
|
||||
{
|
||||
return connectionType switch
|
||||
{
|
||||
ConnectionType.Upstream => item.UpstreamTreeNodes,
|
||||
ConnectionType.IsError => item.IsErrorTreeNodes,
|
||||
ConnectionType.IsFail => item.IsFailTreeNodes,
|
||||
ConnectionType.IsSucceed => item.IsSucceedTreeNodes,
|
||||
_ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType)
|
||||
};
|
||||
}
|
||||
public static Grid ToGridView(NodeTreeItemViewControl item, ConnectionType connectionType)
|
||||
{
|
||||
return connectionType switch
|
||||
{
|
||||
ConnectionType.Upstream => item.UpstreamTreeGuid,
|
||||
ConnectionType.IsError => item.IsErrorTreeGuid,
|
||||
ConnectionType.IsFail => item.IsFailTreeGuid,
|
||||
ConnectionType.IsSucceed => item.IsSucceedTreeGuid,
|
||||
_ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType)
|
||||
};
|
||||
}
|
||||
|
||||
//public static System.Windows.Shapes.Rectangle ToRectangle(NodeTreeItemViewControl item, ConnectionType connectionType)
|
||||
//{
|
||||
// return connectionType switch
|
||||
// {
|
||||
// ConnectionType.Upstream => item.UpstreamTreeRectangle,
|
||||
// ConnectionType.IsError => item.IsErrorRectangle,
|
||||
// ConnectionType.IsFail => item.IsFailRectangle,
|
||||
// ConnectionType.IsSucceed => item.IsSucceedRectangle,
|
||||
// _ => throw new Exception("LoadNodeItem Error :ConnectionType is " + connectionType)
|
||||
// };
|
||||
//}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<UserControl x:Class="Serein.WorkBench.Themes.NodeTreeViewControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="ListItemNullFocusContainerStyle" TargetType="ListBoxItem">
|
||||
<Setter Property="SnapsToDevicePixels" Value="true" />
|
||||
<Setter Property="FocusVisualStyle" Value="{x:Null}" />
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="ListBoxItem">
|
||||
<Border Name="Border" Background="Transparent" SnapsToDevicePixels="True">
|
||||
<ContentPresenter />
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="auto"/>
|
||||
<!--<RowDefinition Height="*"/>-->
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel Grid.Row="0">
|
||||
<TextBlock Text="起始节点"/>
|
||||
|
||||
<ScrollViewer >
|
||||
<local:NodeTreeItemViewControl x:Name="StartNodeViewer" Margin="4,4,4,4"/>
|
||||
</ScrollViewer >
|
||||
</StackPanel>
|
||||
<StackPanel Grid.Row="1">
|
||||
<TextBlock Text="全局触发器"/>
|
||||
<ListBox x:Name="GlobalFlipflopNodeListbox" BorderThickness="0" ItemContainerStyle="{StaticResource ListItemNullFocusContainerStyle}"></ListBox>
|
||||
</StackPanel>
|
||||
|
||||
<!--<StackPanel Grid.Row="2">
|
||||
<TextBlock Text="无业游民"/>
|
||||
<ListBox x:Name="UnreachableNodeListbox" BorderThickness="0" ItemContainerStyle="{StaticResource ListItemNullFocusContainerStyle}"></ListBox>
|
||||
</StackPanel>-->
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,98 +0,0 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Base;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// NodeTreeViewControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class NodeTreeViewControl : UserControl
|
||||
{
|
||||
public NodeTreeViewControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private string startNodeGuid = string.Empty;
|
||||
private Dictionary<string, NodeTreeItemViewControl> globalFlipflopNodes = [];
|
||||
private Dictionary<string, NodeTreeItemViewControl> unemployedNodes = [];
|
||||
|
||||
public void LoadNodeTreeOfStartNode(IFlowEnvironment flowEnvironment, NodeModelBase nodeModel)
|
||||
{
|
||||
startNodeGuid = nodeModel.Guid;
|
||||
StartNodeViewer.InitAndLoadTree(flowEnvironment, nodeModel);
|
||||
}
|
||||
|
||||
#region 触发器
|
||||
public void AddGlobalFlipFlop(IFlowEnvironment flowEnvironment, NodeModelBase nodeModel)
|
||||
{
|
||||
if (!globalFlipflopNodes.ContainsKey(nodeModel.Guid))
|
||||
{
|
||||
NodeTreeItemViewControl flipflopTreeViewer = new NodeTreeItemViewControl();
|
||||
flipflopTreeViewer.InitAndLoadTree(flowEnvironment, nodeModel);
|
||||
globalFlipflopNodes.Add(nodeModel.Guid, flipflopTreeViewer);
|
||||
GlobalFlipflopNodeListbox.Items.Add(flipflopTreeViewer);
|
||||
}
|
||||
}
|
||||
public void RefreshGlobalFlipFlop(NodeModelBase nodeModel)
|
||||
{
|
||||
if (globalFlipflopNodes.TryGetValue(nodeModel.Guid, out var viewer))
|
||||
{
|
||||
viewer.RefreshTree();
|
||||
}
|
||||
}
|
||||
public void RemoteGlobalFlipFlop(NodeModelBase nodeModel)
|
||||
{
|
||||
if (globalFlipflopNodes.TryGetValue(nodeModel.Guid, out var viewer))
|
||||
{
|
||||
globalFlipflopNodes.Remove(nodeModel.Guid);
|
||||
GlobalFlipflopNodeListbox.Items.Remove(viewer);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region 无业游民(定义:不存在于起始节点与全局触发器的调用链上的节点,只能手动刷新?)
|
||||
public void AddUnemployed(IFlowEnvironment flowEnvironment, NodeModelBase nodeModel)
|
||||
{
|
||||
if (!unemployedNodes.ContainsKey(nodeModel.Guid))
|
||||
{
|
||||
NodeTreeItemViewControl flipflopTreeViewer = new NodeTreeItemViewControl();
|
||||
flipflopTreeViewer.InitAndLoadTree(flowEnvironment, nodeModel);
|
||||
unemployedNodes.Add(nodeModel.Guid, flipflopTreeViewer);
|
||||
GlobalFlipflopNodeListbox.Items.Add(flipflopTreeViewer);
|
||||
}
|
||||
}
|
||||
public void RefreshUnemployed(NodeModelBase nodeModel)
|
||||
{
|
||||
if (unemployedNodes.TryGetValue(nodeModel.Guid, out var viewer))
|
||||
{
|
||||
viewer.RefreshTree();
|
||||
}
|
||||
}
|
||||
public void RemoteUnemployed(NodeModelBase nodeModel)
|
||||
{
|
||||
if (unemployedNodes.TryGetValue(nodeModel.Guid, out var viewer))
|
||||
{
|
||||
unemployedNodes.Remove(nodeModel.Guid);
|
||||
GlobalFlipflopNodeListbox.Items.Remove(viewer);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
<UserControl x:Class="Serein.WorkBench.Themes.ObjectViewerControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Themes"
|
||||
mc:Ignorable="d"
|
||||
d:DesignHeight="400" d:DesignWidth="400">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<!-- 按钮 -->
|
||||
<RowDefinition Height="*" />
|
||||
<!-- 树视图 -->
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<StackPanel Orientation="Horizontal" Grid.Row="0" >
|
||||
<!---->
|
||||
<!--<Button Grid.Row="0" HorizontalAlignment="Left" Margin="14,2,4,2" Content="监视" Width="100" Height="20" Name="TimerRefreshButton"/>-->
|
||||
<!--<Button Grid.Row="0" HorizontalAlignment="Left" Margin="14,2,4,2" Content="添加监视表达式" Width="100" Height="20" Name="AddMonitorExpressionButton" Click="AddMonitorExpressionButton_Click"/>-->
|
||||
<Button Grid.Row="0" HorizontalAlignment="Left" Margin="4,2,4,2" Content="刷新" Width="40" Height="20" Name="RefreshButton" Click="RefreshButton_Click"/>
|
||||
<Button Grid.Row="0" HorizontalAlignment="Left" Margin="4,2,4,2" Content="添加监视表达式" Width="80" Height="20" Name="UpMonitorExpressionButton" Click="UpMonitorExpressionButton_Click"/>
|
||||
<TextBox x:Name="ExpressionTextBox" Margin="4,2,4,2" Width="300"/>
|
||||
|
||||
</StackPanel>
|
||||
<!-- 刷新按钮 -->
|
||||
|
||||
<!-- 树视图,用于显示对象属性 -->
|
||||
<TreeView FontSize="14" x:Name="ObjectTreeView" Grid.Row="1" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,671 +0,0 @@
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Serein.Library.Api;
|
||||
using Serein.NodeFlow.Base;
|
||||
using Serein.NodeFlow.Tool.SereinExpression;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Markup.Primitives;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Navigation;
|
||||
using System.Windows.Shapes;
|
||||
using System.Xml.Linq;
|
||||
using static Serein.WorkBench.Themes.TypeViewerWindow;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
|
||||
public class FlowDataDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// 属性名称
|
||||
/// </summary>
|
||||
public string? Name { get; set; }
|
||||
/// <summary>
|
||||
/// 属性类型
|
||||
/// </summary>
|
||||
public TreeItemType ItemType { get; set; }
|
||||
/// <summary>
|
||||
/// 数据类型
|
||||
/// </summary>
|
||||
public Type? DataType { get; set; }
|
||||
/// <summary>
|
||||
/// 数据
|
||||
/// </summary>
|
||||
public object? DataValue { get; set; }
|
||||
/// <summary>
|
||||
/// 数据路径
|
||||
/// </summary>
|
||||
public string DataPath { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// ObjectViewerControl.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ObjectViewerControl : UserControl
|
||||
{
|
||||
public ObjectViewerControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 监视类型
|
||||
/// </summary>
|
||||
public enum MonitorType
|
||||
{
|
||||
/// <summary>
|
||||
/// 作用于对象(对象的引用)的监视
|
||||
/// </summary>
|
||||
NodeFlowData,
|
||||
/// <summary>
|
||||
/// 作用与节点(FLowData)的监视
|
||||
/// </summary>
|
||||
IOCObj,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行环境
|
||||
/// </summary>
|
||||
public IFlowEnvironment? FlowEnvironment { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 监视对象的键
|
||||
/// </summary>
|
||||
public string? MonitorKey { get => monitorKey; }
|
||||
/// <summary>
|
||||
/// 正在监视的对象
|
||||
/// </summary>
|
||||
public object? MonitorObj { get => monitorObj; }
|
||||
|
||||
/// <summary>
|
||||
/// 监视表达式
|
||||
/// </summary>
|
||||
public string? MonitorExpression { get => ExpressionTextBox.Text.ToString(); }
|
||||
|
||||
private string? monitorKey;
|
||||
private object? monitorObj;
|
||||
|
||||
// 用于存储当前展开的节点路径
|
||||
private HashSet<string> expandedNodePaths = new HashSet<string>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 加载对象信息,展示其成员
|
||||
/// </summary>
|
||||
/// <param name="obj">要展示的对象</param>
|
||||
public void LoadObjectInformation(string key, object obj)
|
||||
{
|
||||
if (obj == null) return;
|
||||
monitorKey = key;
|
||||
monitorObj = obj;
|
||||
expandedNodePaths.Clear();
|
||||
LoadTree(obj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新对象
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
RefreshObjectTree(monitorObj);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新表达式
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void UpMonitorExpressionButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (FlowEnvironment is not null && FlowEnvironment.AddInterruptExpression(monitorKey, MonitorExpression)) // 对象预览器尝试添加中断表达式
|
||||
{
|
||||
if (string.IsNullOrEmpty(MonitorExpression))
|
||||
{
|
||||
ExpressionTextBox.Text = "表达式已清空";
|
||||
}
|
||||
else
|
||||
{
|
||||
UpMonitorExpressionButton.Content = "更新监视表达式";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private TreeViewItem? LoadTree(object? obj)
|
||||
{
|
||||
if (obj is null) return null;
|
||||
var objectType = obj.GetType();
|
||||
FlowDataDetails flowDataDetails = new FlowDataDetails
|
||||
{
|
||||
Name = objectType.Name,
|
||||
DataType = objectType,
|
||||
DataValue = obj,
|
||||
DataPath = ""
|
||||
};
|
||||
var rootNode = new TreeViewItem
|
||||
{
|
||||
Header = objectType.Name,
|
||||
Tag = flowDataDetails,
|
||||
};
|
||||
|
||||
|
||||
ObjectTreeView.Items.Clear(); // 移除对象树的所有节点
|
||||
ObjectTreeView.Items.Add(rootNode); // 添加所有节点
|
||||
rootNode.Expanded += TreeViewItem_Expanded; // 监听展开事件
|
||||
rootNode.Collapsed += TreeViewItem_Collapsed; // 监听折叠事件
|
||||
// 这里创建了一个子项,并给这个子项创建了“正在加载”的子项
|
||||
// 然后移除了原来对象树的所有项,再把这个新创建的子项添加上去
|
||||
// 绑定了展开/折叠事件后,自动展开第一层,开始反射obj的成员,并判断obj的成员生成什么样的节点
|
||||
rootNode.IsExpanded = true;
|
||||
return rootNode;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 刷新对象属性树
|
||||
/// </summary>
|
||||
public void RefreshObjectTree(object? obj)
|
||||
{
|
||||
monitorObj = obj;
|
||||
var rootNode = LoadTree(obj);
|
||||
if (rootNode is not null)
|
||||
{
|
||||
ExpandPreviouslyExpandedNodes(rootNode); // 遍历节点,展开之前记录的节点
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 展开父节点,如果路径存在哈希记录,则将其自动展开,并递归展开后的子节点。
|
||||
/// </summary>
|
||||
/// <param name="node"></param>
|
||||
private void ExpandPreviouslyExpandedNodes(TreeViewItem node)
|
||||
{
|
||||
if (node == null) return;
|
||||
if(node.Tag is FlowDataDetails flowDataDetails)
|
||||
{
|
||||
if (expandedNodePaths.Contains(flowDataDetails.DataPath))
|
||||
{
|
||||
node.IsExpanded = true;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (TreeViewItem child in node.Items)
|
||||
{
|
||||
ExpandPreviouslyExpandedNodes(child);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 展开子项事件
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is TreeViewItem item)
|
||||
{
|
||||
if (item.Tag is FlowDataDetails flowDataDetails) // FlowDataDetails flowDataDetails object obj
|
||||
{
|
||||
if (flowDataDetails.ItemType != TreeItemType.Item && item.Items.Count != 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(flowDataDetails.DataValue is null || flowDataDetails.DataType is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 记录当前节点的路径
|
||||
var path = flowDataDetails.DataPath;
|
||||
expandedNodePaths.Add(path);
|
||||
AddMembersToTreeNode(item, flowDataDetails.DataValue, flowDataDetails.DataType);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 折叠事件
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void TreeViewItem_Collapsed(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (sender is TreeViewItem item && item.Items.Count > 0)
|
||||
{
|
||||
if (item.Tag is FlowDataDetails flowDataDetails)
|
||||
{
|
||||
// 记录当前节点的路径
|
||||
var path = flowDataDetails.DataPath;
|
||||
if(path != "")
|
||||
{
|
||||
expandedNodePaths.Remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 反射对象数据添加子节点
|
||||
/// </summary>
|
||||
/// <param name="treeViewNode"></param>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="type"></param>
|
||||
private void AddMembersToTreeNode(TreeViewItem treeViewNode, object obj, Type type)
|
||||
{
|
||||
// 获取公开的属性
|
||||
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
if (member.Name.StartsWith(".") ||
|
||||
member.Name.StartsWith("get_") ||
|
||||
member.Name.StartsWith("set_")
|
||||
)
|
||||
{
|
||||
// 跳过构造函数、属性的get/set方法
|
||||
continue;
|
||||
}
|
||||
|
||||
TreeViewItem? memberNode = ConfigureTreeViewItem(obj, member); // 根据对象成员生成节点对象
|
||||
if (memberNode is not null)
|
||||
{
|
||||
treeViewNode.Items.Add(memberNode); // 添加到当前节点
|
||||
|
||||
// 配置数据路径
|
||||
FlowDataDetails subFlowDataDetails = (FlowDataDetails)memberNode.Tag;
|
||||
string superPath = ((FlowDataDetails)treeViewNode.Tag).DataPath;
|
||||
string subPath = superPath + "." + subFlowDataDetails.Name;
|
||||
subFlowDataDetails.DataPath = subPath;
|
||||
|
||||
// 配置右键菜单
|
||||
var contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
{
|
||||
ExpressionTextBox.Text = subPath; // 获取表达式
|
||||
|
||||
}));
|
||||
memberNode.ContextMenu = contextMenu;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 配置节点子项
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <returns></returns>
|
||||
private TreeViewItem? ConfigureTreeViewItem(object obj, MemberInfo member)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#region 属性
|
||||
if (member is PropertyInfo property)
|
||||
{
|
||||
#region 集合类型(非字符串)
|
||||
if (property.PropertyType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(property.PropertyType) && property.GetValue(obj) is IEnumerable collection && collection is not null)
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
// 处理集合类型的属性
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.IEnumerable,
|
||||
DataType = property.PropertyType,
|
||||
Name = property.Name,
|
||||
DataValue = collection,
|
||||
};
|
||||
|
||||
int index = 0;
|
||||
foreach (var item in collection)
|
||||
{
|
||||
var itemNode = new TreeViewItem { Header = $"[{index++}] {item}" ?? "null" };
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Item,
|
||||
DataType = item?.GetType(),
|
||||
Name = property.Name,
|
||||
DataValue = itemNode,
|
||||
};
|
||||
memberNode.Items.Add(itemNode);
|
||||
}
|
||||
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} [{index}]";
|
||||
if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string))
|
||||
{
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
memberNode.Collapsed += TreeViewItem_Collapsed;
|
||||
}
|
||||
return memberNode;
|
||||
}
|
||||
#endregion
|
||||
#region 值类型与未判断的类型
|
||||
else
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
string propertyValue = GetPropertyValue(obj, property, out object? value);
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Property,
|
||||
DataType = property.PropertyType,
|
||||
Name = property.Name,
|
||||
DataValue = value,
|
||||
}; ;
|
||||
|
||||
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} = {propertyValue}";
|
||||
if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string))
|
||||
{
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
memberNode.Collapsed += TreeViewItem_Collapsed;
|
||||
}
|
||||
return memberNode;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
#region 字段
|
||||
else if (member is FieldInfo field)
|
||||
{
|
||||
#region 集合类型(非字符串)
|
||||
if (field.FieldType != typeof(string) && typeof(IEnumerable).IsAssignableFrom(field.FieldType) && field.GetValue(obj) is IEnumerable collection && collection is not null)
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
// 处理集合类型的字段
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.IEnumerable,
|
||||
DataType = field.FieldType,
|
||||
Name = field.Name,
|
||||
DataValue = collection,
|
||||
};
|
||||
|
||||
int index = 0;
|
||||
foreach (var item in collection)
|
||||
{
|
||||
var itemNode = new TreeViewItem { Header = $"[{index++}] {item}" ?? "null" };
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Item,
|
||||
DataType = item?.GetType(),
|
||||
Name = field.Name,
|
||||
DataValue = itemNode,
|
||||
};
|
||||
//collectionNode.Items.Add(itemNode);
|
||||
memberNode.Items.Add(itemNode);
|
||||
}
|
||||
memberNode.Header = $"{field.Name} : {field.FieldType.Name} [{index}]";
|
||||
if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
|
||||
{
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
memberNode.Collapsed += TreeViewItem_Collapsed;
|
||||
}
|
||||
return memberNode;
|
||||
}
|
||||
#endregion
|
||||
#region 值类型与未判断的类型
|
||||
else
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
string fieldValue = GetFieldValue(obj, field, out object? value);
|
||||
|
||||
memberNode.Tag = new FlowDataDetails
|
||||
{
|
||||
ItemType = TreeItemType.Field,
|
||||
DataType = field.FieldType,
|
||||
Name = field.Name,
|
||||
DataValue = value,
|
||||
|
||||
};
|
||||
memberNode.Header = $"{field.Name} : {field.FieldType.Name} = {fieldValue}";
|
||||
|
||||
if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
|
||||
{
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
memberNode.Collapsed += TreeViewItem_Collapsed;
|
||||
}
|
||||
return memberNode;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
#endregion
|
||||
#region 返回null
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取属性类型的成员
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="property"></param>
|
||||
/// <returns></returns>
|
||||
private string GetPropertyValue(object obj, PropertyInfo property,out object? value)
|
||||
{
|
||||
try
|
||||
{
|
||||
if(obj is null)
|
||||
{
|
||||
value = null;
|
||||
return "Error";
|
||||
}
|
||||
var properties = obj.GetType().GetProperties();
|
||||
|
||||
// 获取实例属性值
|
||||
value = property.GetValue(obj);
|
||||
return value?.ToString() ?? "null"; // 返回值或“null”
|
||||
}
|
||||
catch
|
||||
{
|
||||
value = null;
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取字段类型的成员
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="field"></param>
|
||||
/// <returns></returns>
|
||||
private string GetFieldValue(object obj, FieldInfo field, out object? value)
|
||||
{
|
||||
try
|
||||
{
|
||||
value = field.GetValue(obj);
|
||||
return value?.ToString() ?? "null";
|
||||
}
|
||||
catch
|
||||
{
|
||||
value = null;
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 上次刷新时间
|
||||
/// </summary>
|
||||
//private DateTime lastRefreshTime = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// 刷新间隔
|
||||
/// </summary>
|
||||
//private readonly TimeSpan refreshInterval = TimeSpan.FromSeconds(0.1);
|
||||
// 当前时间
|
||||
//var currentTime = DateTime.Now;
|
||||
//if (currentTime - lastRefreshTime < refreshInterval)
|
||||
//{
|
||||
// return; // 跳过过于频繁的刷新调用
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// lastRefreshTime = currentTime;// 记录这次的刷新时间
|
||||
//}
|
||||
//
|
||||
|
||||
/// <summary>
|
||||
/// 从当前节点获取至父节点的路径,例如 "node1.node2.node3.node4"
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>节点路径</returns>
|
||||
//private string GetNodeFullPath(TreeViewItem node)
|
||||
//{
|
||||
// if (node == null)
|
||||
// return string.Empty;
|
||||
|
||||
// FlowDataDetails flowDataDetails = (FlowDataDetails)node.Tag;
|
||||
// var parent = GetParentTreeViewItem(node);
|
||||
// if (parent != null)
|
||||
// {
|
||||
// // 递归获取父节点的路径,并拼接当前节点的 Header
|
||||
// return $"{GetNodeFullPath(parent)}.{flowDataDetails.Name}";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// // 没有父节点,则说明这是根节点,直接返回 Header
|
||||
// return "";
|
||||
// }
|
||||
//}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定节点的父级节点
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>父节点</returns>
|
||||
//private TreeViewItem GetParentTreeViewItem(TreeViewItem node)
|
||||
//{
|
||||
// DependencyObject parent = VisualTreeHelper.GetParent(node);
|
||||
// while (parent != null && !(parent is TreeViewItem))
|
||||
// {
|
||||
// parent = VisualTreeHelper.GetParent(parent);
|
||||
// }
|
||||
// return parent as TreeViewItem;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 根据成员类别配置右键菜单
|
||||
/// </summary>
|
||||
/// <param name="memberNode"></param>
|
||||
/// <param name="member"></param>
|
||||
/// <param name="contextMenu"></param>
|
||||
/// <returns></returns>
|
||||
//private bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member, out ContextMenu? contextMenu)
|
||||
//{
|
||||
// if (ConfigureTreeItemMenu(memberNode, member, out ContextMenu? contextMenu))
|
||||
// {
|
||||
// memberNode.ContextMenu = contextMenu; // 设置子项节点的事件
|
||||
// }
|
||||
|
||||
// bool isChange = false;
|
||||
// if (member is PropertyInfo property)
|
||||
// {
|
||||
// isChange = true;
|
||||
// contextMenu = new ContextMenu();
|
||||
// contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
// {
|
||||
// string fullPath = GetNodeFullPath(memberNode);
|
||||
// string copyValue = /*"@Get " + */fullPath;
|
||||
// ExpressionTextBox.Text = copyValue;
|
||||
// // Clipboard.SetDataObject(copyValue);
|
||||
|
||||
// }));
|
||||
// }
|
||||
// else if (member is MethodInfo method)
|
||||
// {
|
||||
// //isChange = true;
|
||||
// contextMenu = new ContextMenu();
|
||||
// }
|
||||
// else if (member is FieldInfo field)
|
||||
// {
|
||||
// isChange = true;
|
||||
// contextMenu = new ContextMenu();
|
||||
// contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) =>
|
||||
// {
|
||||
// string fullPath = GetNodeFullPath(memberNode);
|
||||
// string copyValue = /*"@Get " +*/ fullPath;
|
||||
// ExpressionTextBox.Text = copyValue;
|
||||
// // Clipboard.SetDataObject(copyValue);
|
||||
// }));
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// contextMenu = new ContextMenu();
|
||||
// }
|
||||
// return isChange;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 刷新按钮的点击事件
|
||||
///// </summary>
|
||||
//private void RefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
// RefreshObjectTree();
|
||||
//}
|
||||
|
||||
//private bool IsTimerRefres = false;
|
||||
//private void TimerRefreshButton_Click(object sender, RoutedEventArgs e)
|
||||
//{
|
||||
// if (IsTimerRefres)
|
||||
// {
|
||||
// IsTimerRefres = false;
|
||||
// TimerRefreshButton.Content = "定时刷新";
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// IsTimerRefres = true;
|
||||
// TimerRefreshButton.Content = "取消刷新";
|
||||
|
||||
// _ = Task.Run(async () => {
|
||||
// while (true)
|
||||
// {
|
||||
// if (IsTimerRefres)
|
||||
// {
|
||||
// Application.Current.Dispatcher.Invoke(() =>
|
||||
// {
|
||||
// RefreshObjectTree(); // 刷新UI
|
||||
// });
|
||||
// await Task.Delay(100);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// IsTimerRefres = false;
|
||||
// });
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<UserControl x:Class="RealTimeObjectViewer.ObjectViewerControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Themes">
|
||||
<Grid>
|
||||
<TreeView x:Name="ObjectTreeView" />
|
||||
</Grid>
|
||||
</UserControl>
|
||||
@@ -1,146 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// ObjectViewerWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class ObjectViewerControl : UserControl
|
||||
{
|
||||
public ObjectViewerControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private object _objectInstance;
|
||||
private Action _closeCallback;
|
||||
|
||||
public void LoadObjectInformation(object obj,Action closeCallback)
|
||||
{
|
||||
if (obj == null || closeCallback == null)
|
||||
return;
|
||||
_closeCallback = closeCallback;
|
||||
_objectInstance = obj;
|
||||
var objectType = obj.GetType();
|
||||
var rootNode = new TreeViewItem { Header = objectType.Name, Tag = obj };
|
||||
|
||||
// 添加占位符节点
|
||||
AddPlaceholderNode(rootNode);
|
||||
ObjectTreeView.Items.Clear();
|
||||
ObjectTreeView.Items.Add(rootNode);
|
||||
|
||||
// 监听展开事件
|
||||
rootNode.Expanded += TreeViewItem_Expanded;
|
||||
}
|
||||
|
||||
private void AddPlaceholderNode(TreeViewItem node)
|
||||
{
|
||||
node.Items.Add(new TreeViewItem { Header = "Loading..." });
|
||||
}
|
||||
|
||||
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var item = (TreeViewItem)sender;
|
||||
|
||||
if (item.Items.Count == 1 && item.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...")
|
||||
{
|
||||
item.Items.Clear();
|
||||
if (item.Tag is object obj)
|
||||
{
|
||||
var objectType = obj.GetType();
|
||||
AddMembersToTreeNode(item, obj, objectType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void AddMembersToTreeNode(TreeViewItem node, object obj, Type type)
|
||||
{
|
||||
// 获取属性和字段
|
||||
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
TreeViewItem memberNode = ConfigureTreeViewItem(obj, member);
|
||||
node.Items.Add(memberNode);
|
||||
}
|
||||
}
|
||||
|
||||
private TreeViewItem ConfigureTreeViewItem(object obj, MemberInfo member)
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
|
||||
if (member is PropertyInfo property)
|
||||
{
|
||||
string propertyValue = GetPropertyValue(obj, property);
|
||||
memberNode.Header = $"{property.Name} : {property.PropertyType.Name} = {propertyValue}";
|
||||
|
||||
if (!property.PropertyType.IsPrimitive && property.PropertyType != typeof(string))
|
||||
{
|
||||
AddPlaceholderNode(memberNode);
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
}
|
||||
}
|
||||
else if (member is FieldInfo field)
|
||||
{
|
||||
string fieldValue = GetFieldValue(obj, field);
|
||||
memberNode.Header = $"{field.Name} : {field.FieldType.Name} = {fieldValue}";
|
||||
|
||||
if (!field.FieldType.IsPrimitive && field.FieldType != typeof(string))
|
||||
{
|
||||
AddPlaceholderNode(memberNode);
|
||||
memberNode.Expanded += TreeViewItem_Expanded;
|
||||
}
|
||||
}
|
||||
|
||||
return memberNode;
|
||||
}
|
||||
|
||||
private string GetPropertyValue(object obj, PropertyInfo property)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = property.GetValue(obj);
|
||||
return value?.ToString() ?? "null";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
private string GetFieldValue(object obj, FieldInfo field)
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = field.GetValue(obj);
|
||||
return value?.ToString() ?? "null";
|
||||
}
|
||||
catch
|
||||
{
|
||||
return "Error";
|
||||
}
|
||||
}
|
||||
|
||||
private void Window_Closed(object sender, EventArgs e)
|
||||
{
|
||||
}
|
||||
|
||||
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
|
||||
_closeCallback?.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
<Window x:Class="Serein.WorkBench.Themes.TypeViewerWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:Serein.WorkBench.Themes"
|
||||
mc:Ignorable="d"
|
||||
Topmost="True"
|
||||
|
||||
Title="TypeViewerWindow" Height="300" Width="300">
|
||||
<Grid>
|
||||
<Grid>
|
||||
<TreeView x:Name="TypeTreeView"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Window>
|
||||
@@ -1,279 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Documents;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Imaging;
|
||||
using System.Windows.Shapes;
|
||||
|
||||
namespace Serein.WorkBench.Themes
|
||||
{
|
||||
/// <summary>
|
||||
/// TypeViewerWindow.xaml 的交互逻辑
|
||||
/// </summary>
|
||||
public partial class TypeViewerWindow : Window
|
||||
{
|
||||
public TypeViewerWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
public Type Type { get; set; }
|
||||
|
||||
public void LoadTypeInformation()
|
||||
{
|
||||
if (Type == null)
|
||||
return;
|
||||
|
||||
NodeFlowDataObjectDetails typeNodeDetails = new NodeFlowDataObjectDetails
|
||||
{
|
||||
Name = Type.Name,
|
||||
DataType = Type,
|
||||
};
|
||||
var rootNode = new TreeViewItem { Header = Type.Name, Tag = typeNodeDetails };
|
||||
AddPlaceholderNode(rootNode); // 添加占位符节点
|
||||
TypeTreeView.Items.Clear();
|
||||
TypeTreeView.Items.Add(rootNode);
|
||||
|
||||
rootNode.Expanded += TreeViewItem_Expanded; // 监听节点展开事件
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加占位符节点
|
||||
/// </summary>
|
||||
private void AddPlaceholderNode(TreeViewItem node)
|
||||
{
|
||||
node.Items.Add(new TreeViewItem { Header = "Loading..." });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点展开事件,延迟加载子节点
|
||||
/// </summary>
|
||||
private void TreeViewItem_Expanded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var item = (TreeViewItem)sender;
|
||||
|
||||
// 如果已经加载过子节点,则不再重复加载
|
||||
if (item.Items.Count == 1 && item.Items[0] is TreeViewItem placeholder && placeholder.Header.ToString() == "Loading...")
|
||||
{
|
||||
item.Items.Clear();
|
||||
if (item.Tag is NodeFlowDataObjectDetails typeNodeDetails)
|
||||
{
|
||||
AddMembersToTreeNode(item, typeNodeDetails.DataType);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加属性节点
|
||||
/// </summary>
|
||||
private void AddMembersToTreeNode(TreeViewItem node, Type type)
|
||||
{
|
||||
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.DeclaredOnly);
|
||||
foreach (var member in members)
|
||||
{
|
||||
TreeViewItem memberNode = ConfigureTreeViewItem(member); // 生成类型节点的子项
|
||||
if (ConfigureTreeItemMenu(memberNode,member, out ContextMenu? contextMenu))
|
||||
{
|
||||
memberNode.ContextMenu = contextMenu; // 设置子项节点的事件
|
||||
}
|
||||
|
||||
node.Items.Add(memberNode); // 添加到父节点中
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 生成类型节点的子项
|
||||
/// </summary>
|
||||
/// <param name="member"></param>
|
||||
/// <returns></returns>
|
||||
private TreeViewItem ConfigureTreeViewItem(MemberInfo member)
|
||||
{
|
||||
TreeViewItem memberNode = new TreeViewItem { Header = member.Name };
|
||||
if (member is PropertyInfo property)
|
||||
{
|
||||
NodeFlowDataObjectDetails typeNodeDetails = new NodeFlowDataObjectDetails
|
||||
{
|
||||
ItemType = TreeItemType.Property,
|
||||
DataType = property.PropertyType,
|
||||
Name = property.Name,
|
||||
DataValue = property,
|
||||
};
|
||||
memberNode.Tag = typeNodeDetails;
|
||||
|
||||
var propertyType = typeNodeDetails.DataType;
|
||||
memberNode.Header = $"{member.Name} : {propertyType.Name}";
|
||||
|
||||
if (!propertyType.IsPrimitive && propertyType != typeof(string))
|
||||
{
|
||||
// 延迟加载类型的子属性,添加占位符节点
|
||||
AddPlaceholderNode(memberNode);
|
||||
memberNode.Expanded += TreeViewItem_Expanded; // 监听展开事件
|
||||
}
|
||||
}
|
||||
else if (member is MethodInfo method)
|
||||
{
|
||||
NodeFlowDataObjectDetails typeNodeDetails = new NodeFlowDataObjectDetails
|
||||
{
|
||||
ItemType = TreeItemType.Method,
|
||||
DataType = typeof(MethodInfo),
|
||||
Name = method.Name,
|
||||
DataValue = null,
|
||||
};
|
||||
memberNode.Tag = typeNodeDetails;
|
||||
|
||||
var parameters = method.GetParameters();
|
||||
var paramStr = string.Join(", ", parameters.Select(p => $"{p.ParameterType.Name} {p.Name}"));
|
||||
memberNode.Header = $"{member.Name}({paramStr})";
|
||||
}
|
||||
else if (member is FieldInfo field)
|
||||
{
|
||||
NodeFlowDataObjectDetails typeNodeDetails = new NodeFlowDataObjectDetails
|
||||
{
|
||||
ItemType = TreeItemType.Field,
|
||||
DataType = field.FieldType,
|
||||
Name = field.Name,
|
||||
DataValue = field,
|
||||
};
|
||||
memberNode.Tag = typeNodeDetails;
|
||||
memberNode.Header = $"{member.Name} : {field.FieldType.Name}";
|
||||
}
|
||||
return memberNode;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置子项节点的事件
|
||||
/// </summary>
|
||||
/// <param name="member"></param>
|
||||
/// <returns></returns>
|
||||
private bool ConfigureTreeItemMenu(TreeViewItem memberNode, MemberInfo member,out ContextMenu? contextMenu)
|
||||
{
|
||||
bool isChange = false;
|
||||
if (member is PropertyInfo property)
|
||||
{
|
||||
isChange = true;
|
||||
contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"取值表达式", (s, e) =>
|
||||
{
|
||||
string fullPath = GetNodeFullPath(memberNode);
|
||||
string copyValue = "@Get " + fullPath;
|
||||
Clipboard.SetDataObject(copyValue);
|
||||
}));
|
||||
}
|
||||
else if (member is MethodInfo method)
|
||||
{
|
||||
//isChange = true;
|
||||
contextMenu = new ContextMenu();
|
||||
}
|
||||
else if (member is FieldInfo field)
|
||||
{
|
||||
isChange = true;
|
||||
contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem($"取值表达式", (s, e) =>
|
||||
{
|
||||
string fullPath = GetNodeFullPath(memberNode);
|
||||
string copyValue = "@Get " + fullPath;
|
||||
Clipboard.SetDataObject(copyValue);
|
||||
}));
|
||||
}
|
||||
else
|
||||
{
|
||||
contextMenu = new ContextMenu();
|
||||
}
|
||||
return isChange;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取当前节点的完整路径,例如 "node1.node2.node3.node4"
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>节点路径</returns>
|
||||
private string GetNodeFullPath(TreeViewItem node)
|
||||
{
|
||||
if (node == null)
|
||||
return string.Empty;
|
||||
|
||||
NodeFlowDataObjectDetails typeNodeDetails = (NodeFlowDataObjectDetails)node.Tag;
|
||||
var parent = GetParentTreeViewItem(node);
|
||||
if (parent != null)
|
||||
{
|
||||
// 递归获取父节点的路径,并拼接当前节点的 Header
|
||||
return $"{GetNodeFullPath(parent)}.{typeNodeDetails.Name}";
|
||||
}
|
||||
else
|
||||
{
|
||||
// 没有父节点,则说明这是根节点,直接返回 Header
|
||||
return "";
|
||||
// return typeNodeDetails.Name.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定节点的父级节点
|
||||
/// </summary>
|
||||
/// <param name="node">目标节点</param>
|
||||
/// <returns>父节点</returns>
|
||||
private TreeViewItem? GetParentTreeViewItem(TreeViewItem node)
|
||||
{
|
||||
DependencyObject parent = VisualTreeHelper.GetParent(node);
|
||||
while (parent != null && parent is not TreeViewItem)
|
||||
{
|
||||
parent = VisualTreeHelper.GetParent(parent);
|
||||
}
|
||||
return parent as TreeViewItem;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public class NodeFlowDataObjectDetails
|
||||
{
|
||||
/// <summary>
|
||||
/// 属性名称
|
||||
/// </summary>
|
||||
public string Name { get; set; }
|
||||
/// <summary>
|
||||
/// 属性类型
|
||||
/// </summary>
|
||||
public TreeItemType ItemType { get; set; }
|
||||
/// <summary>
|
||||
/// 数据类型
|
||||
/// </summary>
|
||||
public Type DataType { get; set; }
|
||||
/// <summary>
|
||||
/// 数据(调试用?)
|
||||
/// </summary>
|
||||
public object DataValue { get; set; }
|
||||
/// <summary>
|
||||
/// 数据路径
|
||||
/// </summary>
|
||||
public string DataPath { get; set; }
|
||||
}
|
||||
|
||||
public enum TreeItemType
|
||||
{
|
||||
Property,
|
||||
Method,
|
||||
Field,
|
||||
IEnumerable,
|
||||
Item,
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
using System.Windows;
|
||||
|
||||
namespace Serein.WorkBench.Tool.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据bool类型控制可见性
|
||||
/// </summary>
|
||||
[ValueConversion(typeof(bool), typeof(Visibility))]
|
||||
public class InvertableBooleanToVisibilityConverter : IValueConverter
|
||||
{
|
||||
enum Parameters
|
||||
{
|
||||
Normal, Inverted
|
||||
}
|
||||
|
||||
public object Convert(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
var boolValue = (bool)value;
|
||||
var direction = (Parameters)Enum.Parse(typeof(Parameters), (string)parameter);
|
||||
|
||||
if (direction == Parameters.Inverted)
|
||||
return !boolValue ? Visibility.Visible : Visibility.Collapsed;
|
||||
|
||||
return boolValue ? Visibility.Visible : Visibility.Collapsed;
|
||||
}
|
||||
|
||||
public object? ConvertBack(object value, Type targetType,
|
||||
object parameter, CultureInfo culture)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Serein.WorkBench.Tool.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// 画布拉动范围距离计算器
|
||||
/// </summary>
|
||||
public class RightThumbPositionConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if (value is double width)
|
||||
return width - 10; // Adjust for Thumb width
|
||||
return 0;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 画布拉动范围距离计算器
|
||||
/// </summary>
|
||||
public class BottomThumbPositionConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if (value is double height)
|
||||
return height - 10; // Adjust for Thumb height
|
||||
return 0;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 画布拉动范围距离计算器
|
||||
/// </summary>
|
||||
public class VerticalCenterThumbPositionConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if (value is double height)
|
||||
return height / 2 - 5; // Centering Thumb vertically
|
||||
return 0;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 画布拉动范围距离计算器
|
||||
/// </summary>
|
||||
public class HorizontalCenterThumbPositionConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
if (value is double width)
|
||||
return width / 2 - 5; // Centering Thumb horizontally
|
||||
return 0;
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,26 +0,0 @@
|
||||
using Serein.Library.Enums;
|
||||
using System.Globalization;
|
||||
using System.Windows.Data;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Serein.WorkBench.Tool.Converters
|
||||
{
|
||||
/// <summary>
|
||||
/// 根据控件类型切换颜色
|
||||
/// </summary>
|
||||
public class TypeToColorConverter : IValueConverter
|
||||
{
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
// 根据 ControlType 返回颜色
|
||||
return value switch
|
||||
{
|
||||
NodeControlType.Action => Brushes.Blue,
|
||||
NodeControlType.Flipflop => Brushes.Green,
|
||||
_ => Brushes.Black,
|
||||
};
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading.Channels;
|
||||
|
||||
namespace Serein.WorkBench.tool
|
||||
{
|
||||
/// <summary>
|
||||
/// 可以捕获类库输出的打印输出
|
||||
/// </summary>
|
||||
public class LogTextWriter : TextWriter
|
||||
{
|
||||
private readonly Action<string> logAction; // 更新日志UI的委托
|
||||
private readonly StringWriter stringWriter = new(); // 缓存日志内容
|
||||
private readonly Channel<string> logChannel = Channel.CreateUnbounded<string>(); // 日志管道
|
||||
private readonly Action clearTextBoxAction; // 清空日志UI的委托
|
||||
private int writeCount = 0; // 写入计数器
|
||||
private const int maxWrites = 500; // 写入最大计数
|
||||
|
||||
public LogTextWriter(Action<string> logAction, Action clearTextBoxAction)
|
||||
{
|
||||
this.logAction = logAction;
|
||||
this.clearTextBoxAction = clearTextBoxAction;
|
||||
|
||||
// 异步启动日志处理任务,不阻塞主线程
|
||||
Task.Run(ProcessLogQueueAsync);
|
||||
}
|
||||
|
||||
public override Encoding Encoding => Encoding.UTF8;
|
||||
|
||||
public override void Write(char value)
|
||||
{
|
||||
stringWriter.Write(value);
|
||||
if (value == '\n')
|
||||
{
|
||||
EnqueueLog();
|
||||
}
|
||||
}
|
||||
|
||||
public override void Write(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return;
|
||||
stringWriter.Write(value);
|
||||
if (value.Contains('\n'))
|
||||
{
|
||||
EnqueueLog();
|
||||
}
|
||||
}
|
||||
|
||||
public override void WriteLine(string? value)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(value)) return;
|
||||
stringWriter.WriteLine(value);
|
||||
EnqueueLog();
|
||||
}
|
||||
|
||||
// 将日志加入通道
|
||||
private void EnqueueLog()
|
||||
{
|
||||
var log = stringWriter.ToString();
|
||||
stringWriter.GetStringBuilder().Clear();
|
||||
|
||||
if (!logChannel.Writer.TryWrite(log))
|
||||
{
|
||||
// 如果写入失败(不太可能),则直接丢弃日志或处理
|
||||
}
|
||||
}
|
||||
|
||||
// 异步处理日志队列
|
||||
private async Task ProcessLogQueueAsync()
|
||||
{
|
||||
await foreach (var log in logChannel.Reader.ReadAllAsync()) // 异步读取日志通道
|
||||
{
|
||||
logAction?.Invoke(log); // 执行日志写入到UI的委托
|
||||
|
||||
writeCount++;
|
||||
if (writeCount >= maxWrites)
|
||||
{
|
||||
clearTextBoxAction?.Invoke(); // 清空文本框
|
||||
writeCount = 0; // 重置计数器
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,20 +1,7 @@
|
||||
using Dm.parser;
|
||||
using NetTaste;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.Script;
|
||||
using SqlSugar;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection;
|
||||
using System.Windows;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Threading;
|
||||
using Expression = System.Linq.Expressions.Expression;
|
||||
|
||||
namespace Serein.Workbench
|
||||
{
|
||||
|
||||
@@ -4,23 +4,15 @@ using Newtonsoft.Json.Linq;
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
using Serein.NodeFlow;
|
||||
using Serein.NodeFlow.Env;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using Serein.Workbench.Extension;
|
||||
using Serein.Workbench.Node;
|
||||
using Serein.Workbench.Node.View;
|
||||
using Serein.Workbench.Node.ViewModel;
|
||||
using Serein.Workbench.Themes;
|
||||
using Serein.Workbench.Tool;
|
||||
using SqlSugar.Extensions;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Reflection;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
@@ -28,8 +20,6 @@ using System.Windows.Controls.Primitives;
|
||||
using System.Windows.Input;
|
||||
using System.Windows.Media;
|
||||
using System.Windows.Media.Animation;
|
||||
using System.Windows.Shapes;
|
||||
using static Dm.net.buffer.ByteArrayBuffer;
|
||||
using DataObject = System.Windows.DataObject;
|
||||
|
||||
namespace Serein.Workbench
|
||||
@@ -209,22 +199,23 @@ namespace Serein.Workbench
|
||||
EnvDecorator.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt;
|
||||
EnvDecorator.OnNodeCreate += FlowEnvironment_NodeCreateEvent;
|
||||
EnvDecorator.OnNodeRemove += FlowEnvironment_NodeRemoteEvent;
|
||||
EnvDecorator.OnNodeParentChildChange += EnvDecorator_OnNodeParentChildChange;
|
||||
EnvDecorator.OnFlowRunComplete += FlowEnvironment_OnFlowRunComplete;
|
||||
EnvDecorator.OnNodePlace += EnvDecorator_OnNodePlaceEvent;
|
||||
EnvDecorator.OnNodeTakeOut += EnvDecorator_OnNodeTakeOutEvent;
|
||||
EnvDecorator.OnFlowRunComplete += FlowEnvironment_OnFlowRunCompleteEvent;
|
||||
|
||||
|
||||
EnvDecorator.OnMonitorObjectChange += FlowEnvironment_OnMonitorObjectChange;
|
||||
EnvDecorator.OnNodeInterruptStateChange += FlowEnvironment_OnNodeInterruptStateChange;
|
||||
EnvDecorator.OnInterruptTrigger += FlowEnvironment_OnInterruptTrigger;
|
||||
EnvDecorator.OnMonitorObjectChange += FlowEnvironment_OnMonitorObjectChangeEvent;
|
||||
EnvDecorator.OnNodeInterruptStateChange += FlowEnvironment_OnNodeInterruptStateChangeEvent;
|
||||
EnvDecorator.OnInterruptTrigger += FlowEnvironment_OnInterruptTriggerEvent;
|
||||
|
||||
EnvDecorator.OnIOCMembersChanged += FlowEnvironment_OnIOCMembersChanged;
|
||||
EnvDecorator.OnIOCMembersChanged += FlowEnvironment_OnIOCMembersChangedEvent;
|
||||
|
||||
EnvDecorator.OnNodeLocated += FlowEnvironment_OnNodeLocate;
|
||||
EnvDecorator.OnNodeMoved += FlowEnvironment_OnNodeMoved;
|
||||
EnvDecorator.OnEnvOut += FlowEnvironment_OnEnvOut;
|
||||
EnvDecorator.OnNodeLocated += FlowEnvironment_OnNodeLocateEvent;
|
||||
EnvDecorator.OnNodeMoved += FlowEnvironment_OnNodeMovedEvent;
|
||||
EnvDecorator.OnEnvOut += FlowEnvironment_OnEnvOutEvent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 移除环境事件
|
||||
@@ -238,32 +229,50 @@ namespace Serein.Workbench
|
||||
EnvDecorator.OnNodeConnectChange -= FlowEnvironment_NodeConnectChangeEvemt;
|
||||
EnvDecorator.OnNodeCreate -= FlowEnvironment_NodeCreateEvent;
|
||||
EnvDecorator.OnNodeRemove -= FlowEnvironment_NodeRemoteEvent;
|
||||
EnvDecorator.OnNodeParentChildChange -= EnvDecorator_OnNodeParentChildChange;
|
||||
EnvDecorator.OnFlowRunComplete -= FlowEnvironment_OnFlowRunComplete;
|
||||
EnvDecorator.OnNodePlace -= EnvDecorator_OnNodePlaceEvent;
|
||||
EnvDecorator.OnNodeTakeOut -= EnvDecorator_OnNodeTakeOutEvent;
|
||||
EnvDecorator.OnFlowRunComplete -= FlowEnvironment_OnFlowRunCompleteEvent;
|
||||
|
||||
|
||||
EnvDecorator.OnMonitorObjectChange -= FlowEnvironment_OnMonitorObjectChange;
|
||||
EnvDecorator.OnNodeInterruptStateChange -= FlowEnvironment_OnNodeInterruptStateChange;
|
||||
EnvDecorator.OnInterruptTrigger -= FlowEnvironment_OnInterruptTrigger;
|
||||
EnvDecorator.OnMonitorObjectChange -= FlowEnvironment_OnMonitorObjectChangeEvent;
|
||||
EnvDecorator.OnNodeInterruptStateChange -= FlowEnvironment_OnNodeInterruptStateChangeEvent;
|
||||
EnvDecorator.OnInterruptTrigger -= FlowEnvironment_OnInterruptTriggerEvent;
|
||||
|
||||
EnvDecorator.OnIOCMembersChanged -= FlowEnvironment_OnIOCMembersChanged;
|
||||
EnvDecorator.OnNodeLocated -= FlowEnvironment_OnNodeLocate;
|
||||
EnvDecorator.OnNodeMoved -= FlowEnvironment_OnNodeMoved;
|
||||
EnvDecorator.OnIOCMembersChanged -= FlowEnvironment_OnIOCMembersChangedEvent;
|
||||
EnvDecorator.OnNodeLocated -= FlowEnvironment_OnNodeLocateEvent;
|
||||
EnvDecorator.OnNodeMoved -= FlowEnvironment_OnNodeMovedEvent;
|
||||
|
||||
EnvDecorator.OnEnvOut -= FlowEnvironment_OnEnvOut;
|
||||
EnvDecorator.OnEnvOut -= FlowEnvironment_OnEnvOutEvent;
|
||||
|
||||
}
|
||||
|
||||
#region 窗体加载方法
|
||||
private void Window_Loaded(object sender, RoutedEventArgs e)
|
||||
{
|
||||
var currentPath = System.IO.Directory.GetCurrentDirectory(); // 当前目录
|
||||
var baseLibraryFilePath = Path.Combine(currentPath, FlowLibraryManagement.SereinBaseLibrary);
|
||||
if (File.Exists(baseLibraryFilePath))
|
||||
{
|
||||
EnvDecorator.LoadLibrary(baseLibraryFilePath); // 默认加载
|
||||
}
|
||||
|
||||
if (App.FlowProjectData is not null)
|
||||
{
|
||||
_ = Task.Run(() =>
|
||||
try
|
||||
{
|
||||
_ = Task.Run(() =>
|
||||
{
|
||||
EnvDecorator.LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); // 加载项目
|
||||
});
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
EnvDecorator.LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); // 加载项目
|
||||
});
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
}
|
||||
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
|
||||
{
|
||||
@@ -315,12 +324,11 @@ namespace Serein.Workbench
|
||||
/// </summary>
|
||||
/// <param name="type"></param>
|
||||
/// <param name="value"></param>
|
||||
private void FlowEnvironment_OnEnvOut(InfoType type, string value)
|
||||
private void FlowEnvironment_OnEnvOutEvent(InfoType type, string value)
|
||||
{
|
||||
LogOutWindow.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}");
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 需要保存项目
|
||||
/// </summary>
|
||||
@@ -328,8 +336,18 @@ namespace Serein.Workbench
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
private void EnvDecorator_OnProjectSaving(ProjectSavingEventArgs eventArgs)
|
||||
{
|
||||
var projectData = EnvDecorator.GetProjectInfoAsync()
|
||||
.GetAwaiter().GetResult(); // 保存项目
|
||||
SereinProjectData projectData;
|
||||
try
|
||||
{
|
||||
projectData = EnvDecorator.GetProjectInfoAsync()
|
||||
.GetAwaiter().GetResult(); // 保存项目
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
projectData.Basic = new Basic
|
||||
{
|
||||
@@ -427,7 +445,6 @@ namespace Serein.Workbench
|
||||
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 加载完成
|
||||
/// </summary>
|
||||
@@ -441,7 +458,7 @@ namespace Serein.Workbench
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
private void FlowEnvironment_OnFlowRunComplete(FlowEventArgs eventArgs)
|
||||
private void FlowEnvironment_OnFlowRunCompleteEvent(FlowEventArgs eventArgs)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, "-------运行完成---------\r\n");
|
||||
this.Dispatcher.Invoke(() =>
|
||||
@@ -480,7 +497,7 @@ namespace Serein.Workbench
|
||||
var menu = new ContextMenu();
|
||||
menu.Items.Add(CreateMenuItem("卸载", (s, e) =>
|
||||
{
|
||||
if (this.EnvDecorator.UnloadLibrary(nodeLibraryInfo.AssemblyName))
|
||||
if (this.EnvDecorator.TryUnloadLibrary(nodeLibraryInfo.AssemblyName))
|
||||
{
|
||||
DllStackPanel.Children.Remove(dllControl);
|
||||
}
|
||||
@@ -596,7 +613,7 @@ namespace Serein.Workbench
|
||||
{
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
await Task.Delay(1000);
|
||||
await Task.Delay(500);
|
||||
FlowEnvironment_NodeConnectChangeEvemt(eventArgs);
|
||||
});
|
||||
return;
|
||||
@@ -717,20 +734,27 @@ namespace Serein.Workbench
|
||||
return;
|
||||
}
|
||||
|
||||
NodeControlBase nodeControl = CreateNodeControl(nodeMVVM.ControlType, nodeMVVM.ViewModelType, nodeModelBase); // 创建控件
|
||||
|
||||
if (nodeControl is null)
|
||||
var nodeCanvas = FlowChartCanvas;
|
||||
NodeControlBase nodeControl;
|
||||
try
|
||||
{
|
||||
nodeControl = CreateNodeControl(nodeMVVM.ControlType, // 控件UI类型
|
||||
nodeMVVM.ViewModelType, // 控件VIewModel类型
|
||||
nodeModelBase, // 控件数据实体
|
||||
nodeCanvas); // 所在画布
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
NodeControls.TryAdd(nodeModelBase.Guid, nodeControl); // 添加到
|
||||
if (TryPlaceNodeInRegion(nodeControl, position, out var regionControl)) // 判断添加到区域容器
|
||||
if (TryPlaceNodeInRegion(nodeControl, position, out var regionControl)) // 判断添加到区域容器
|
||||
{
|
||||
// 通知运行环境调用加载节点子项的方法
|
||||
_ = EnvDecorator.ChangeNodeContainerChild(nodeControl.ViewModel.NodeModel.Guid,
|
||||
regionControl.ViewModel.NodeModel.Guid,
|
||||
true);
|
||||
_ = EnvDecorator.PlaceNodeToContainerAsync(nodeControl.ViewModel.NodeModel.Guid, // 待移动的节点
|
||||
regionControl.ViewModel.NodeModel.Guid); // 目标的容器节点
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -759,33 +783,38 @@ namespace Serein.Workbench
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
private void EnvDecorator_OnNodeParentChildChange(NodeContainerChildChangeEventArgs eventArgs)
|
||||
private void EnvDecorator_OnNodePlaceEvent(NodePlaceEventArgs eventArgs)
|
||||
{
|
||||
string childNodeGuid = eventArgs.ChildNodeGuid;
|
||||
string nodeGuid = eventArgs.NodeGuid;
|
||||
string containerNodeGuid = eventArgs.ContainerNodeGuid;
|
||||
if (!TryGetControl(childNodeGuid, out var childNodeControl)
|
||||
if (!TryGetControl(nodeGuid, out var nodeControl)
|
||||
|| !TryGetControl(containerNodeGuid, out var containerNodeControl))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if(containerNodeControl is not INodeContainerControl containerControl)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"节点[{childNodeGuid}]无法放置在节点[{containerNodeGuid}],因为后者并不实现 INodeContainerControl 接口");
|
||||
SereinEnv.WriteLine(InfoType.WARN,
|
||||
$"节点[{nodeGuid}]无法放置于节点[{containerNodeGuid}]," +
|
||||
$"因为后者并不实现 INodeContainerControl 接口");
|
||||
return;
|
||||
}
|
||||
|
||||
if (eventArgs.State == NodeContainerChildChangeEventArgs.Type.Place)
|
||||
nodeControl.PlaceToContainer(containerControl); // 放置在容器节点中
|
||||
}
|
||||
|
||||
private void EnvDecorator_OnNodeTakeOutEvent(NodeTakeOutEventArgs eventArgs)
|
||||
{
|
||||
string nodeGuid = eventArgs.NodeGuid;
|
||||
if (!TryGetControl(nodeGuid, out var nodeControl))
|
||||
{
|
||||
FlowChartCanvas.Children.Remove(childNodeControl);
|
||||
containerControl.PlaceNode(childNodeControl); // 放置
|
||||
}
|
||||
else
|
||||
{
|
||||
containerControl.TakeOutNode(childNodeControl); // 取出
|
||||
return;
|
||||
}
|
||||
nodeControl.TakeOutContainer(); // 从容器节点中取出
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 设置了流程起始控件
|
||||
/// </summary>
|
||||
@@ -817,7 +846,7 @@ namespace Serein.Workbench
|
||||
/// 被监视的对象发生改变
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
private void FlowEnvironment_OnMonitorObjectChange(MonitorObjectEventArgs eventArgs)
|
||||
private void FlowEnvironment_OnMonitorObjectChangeEvent(MonitorObjectEventArgs eventArgs)
|
||||
{
|
||||
string nodeGuid = eventArgs.NodeGuid;
|
||||
|
||||
@@ -850,7 +879,7 @@ namespace Serein.Workbench
|
||||
/// 节点中断状态改变。
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
private void FlowEnvironment_OnNodeInterruptStateChange(NodeInterruptStateChangeEventArgs eventArgs)
|
||||
private void FlowEnvironment_OnNodeInterruptStateChangeEvent(NodeInterruptStateChangeEventArgs eventArgs)
|
||||
{
|
||||
string nodeGuid = eventArgs.NodeGuid;
|
||||
if (!TryGetControl(nodeGuid, out var nodeControl)) return;
|
||||
@@ -890,7 +919,7 @@ namespace Serein.Workbench
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
private void FlowEnvironment_OnInterruptTrigger(InterruptTriggerEventArgs eventArgs)
|
||||
private void FlowEnvironment_OnInterruptTriggerEvent(InterruptTriggerEventArgs eventArgs)
|
||||
{
|
||||
string nodeGuid = eventArgs.NodeGuid;
|
||||
if (!TryGetControl(nodeGuid, out var nodeControl)) return;
|
||||
@@ -909,7 +938,7 @@ namespace Serein.Workbench
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
private void FlowEnvironment_OnIOCMembersChanged(IOCMembersChangedEventArgs eventArgs)
|
||||
private void FlowEnvironment_OnIOCMembersChangedEvent(IOCMembersChangedEventArgs eventArgs)
|
||||
{
|
||||
IOCObjectViewer.AddDependenciesInstance(eventArgs.Key, eventArgs.Instance);
|
||||
|
||||
@@ -920,7 +949,7 @@ namespace Serein.Workbench
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
/// <exception cref="NotImplementedException"></exception>
|
||||
private void FlowEnvironment_OnNodeLocate(NodeLocatedEventArgs eventArgs)
|
||||
private void FlowEnvironment_OnNodeLocateEvent(NodeLocatedEventArgs eventArgs)
|
||||
{
|
||||
if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl)) return;
|
||||
//scaleTransform.ScaleX = 1;
|
||||
@@ -1008,7 +1037,7 @@ namespace Serein.Workbench
|
||||
/// 节点移动
|
||||
/// </summary>
|
||||
/// <param name="eventArgs"></param>
|
||||
private void FlowEnvironment_OnNodeMoved(NodeMovedEventArgs eventArgs)
|
||||
private void FlowEnvironment_OnNodeMovedEvent(NodeMovedEventArgs eventArgs)
|
||||
{
|
||||
if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl)) return;
|
||||
nodeControl.UpdateLocationConnections();
|
||||
@@ -1088,7 +1117,7 @@ namespace Serein.Workbench
|
||||
nodeControl.MouseLeftButtonUp += Block_MouseLeftButtonUp;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region 配置右键菜单
|
||||
@@ -1096,7 +1125,12 @@ namespace Serein.Workbench
|
||||
/// <summary>
|
||||
/// 配置节点右键菜单
|
||||
/// </summary>
|
||||
/// <param name="nodeControl"><para> 任何情景下都尽量避免直接操作 ViewModel 中的 NodeModel 节点,而是应该调用 FlowEnvironment 提供接口进行操作。</para> 因为 Workbench 应该更加关注UI视觉效果,而非直接干扰流程环境运行的逻辑。<para> 之所以暴露 NodeModel 属性,因为有些场景下不可避免的需要直接获取节点的属性。</para> </param>
|
||||
/// <param name="nodeControl">
|
||||
/// <para> 任何情景下都尽量避免直接修改 ViewModel 中的 NodeModel 节点实体相关数据。</para>
|
||||
/// <para> 而是应该调用 FlowEnvironment 提供接口进行操作。</para>
|
||||
/// <para> 因为 Workbench 应该更加关注UI视觉效果,而非直接干扰流程环境运行的逻辑。</para>
|
||||
/// <para> 之所以暴露 NodeModel 属性,因为有些场景下不可避免的需要直接获取节点的属性。</para>
|
||||
/// </param>
|
||||
private void ConfigureContextMenu(NodeControlBase nodeControl)
|
||||
{
|
||||
|
||||
@@ -1162,7 +1196,7 @@ namespace Serein.Workbench
|
||||
#endregion
|
||||
|
||||
|
||||
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => EnvDecorator.SetStartNode(nodeGuid)));
|
||||
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => EnvDecorator.SetStartNodeAsync(nodeGuid)));
|
||||
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => EnvDecorator.RemoveNodeAsync(nodeGuid)));
|
||||
|
||||
#region 右键菜单功能 - 控件对齐
|
||||
@@ -1240,7 +1274,15 @@ namespace Serein.Workbench
|
||||
{
|
||||
if (file.EndsWith(".dll"))
|
||||
{
|
||||
EnvDecorator.LoadLibrary(file);
|
||||
try
|
||||
{
|
||||
EnvDecorator.LoadLibrary(file);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2368,9 +2410,10 @@ namespace Serein.Workbench
|
||||
/// <param name="controlType">节点控件视图控件类型</param>
|
||||
/// <param name="viewModelType">节点控件ViewModel类型</param>
|
||||
/// <param name="model">节点Model实例</param>
|
||||
/// <param name="nodeCanvas">节点所在画布</param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception">无法创建节点控件</exception>
|
||||
private static NodeControlBase CreateNodeControl(Type controlType, Type viewModelType, NodeModelBase model)
|
||||
private static NodeControlBase CreateNodeControl(Type controlType, Type viewModelType, NodeModelBase model, Canvas nodeCanvas)
|
||||
{
|
||||
if ((controlType is null)
|
||||
|| viewModelType is null
|
||||
@@ -2392,6 +2435,7 @@ namespace Serein.Workbench
|
||||
var controlObj = Activator.CreateInstance(controlType, [viewModel]);
|
||||
if (controlObj is NodeControlBase nodeControl)
|
||||
{
|
||||
nodeControl.NodeCanvas = nodeCanvas;
|
||||
return nodeControl;
|
||||
}
|
||||
else
|
||||
@@ -2474,7 +2518,7 @@ namespace Serein.Workbench
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ButtonDebugRun_Click(object sender, RoutedEventArgs e)
|
||||
private async void ButtonDebugRun_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
LogOutWindow?.Show();
|
||||
|
||||
@@ -2490,10 +2534,15 @@ namespace Serein.Workbench
|
||||
Action<SynchronizationContext, Action> uiInvoke = (uiContext, action) => uiContext?.Post(state => action?.Invoke(), null);
|
||||
|
||||
SereinEnv.WriteLine(InfoType.INFO, "流程开始运行");
|
||||
_ = Task.Run(async () =>
|
||||
try
|
||||
{
|
||||
await EnvDecorator.StartAsync();
|
||||
});
|
||||
await EnvDecorator.StartFlowAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
|
||||
// await EnvDecorator.StartAsync();
|
||||
//await Task.Factory.StartNew(FlowEnvironment.StartAsync);
|
||||
@@ -2504,9 +2553,17 @@ namespace Serein.Workbench
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void ButtonDebugFlipflopNode_Click(object sender, RoutedEventArgs e)
|
||||
private async void ButtonDebugFlipflopNode_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
EnvDecorator?.ExitFlow(); // 在运行平台上点击了退出
|
||||
try
|
||||
{
|
||||
await EnvDecorator.ExitFlowAsync(); // 在运行平台上点击了退出
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2524,11 +2581,15 @@ namespace Serein.Workbench
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.INFO, "请只选择一个节点");
|
||||
}
|
||||
else
|
||||
try
|
||||
{
|
||||
await this.EnvDecorator.StartAsyncInSelectNode(selectNodeControls[0].ViewModel.NodeModel.Guid);
|
||||
}
|
||||
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2546,7 +2607,15 @@ namespace Serein.Workbench
|
||||
/// <param name="e"></param>
|
||||
private async void ButtonSaveFile_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
EnvDecorator.SaveProject();
|
||||
try
|
||||
{
|
||||
EnvDecorator.SaveProject();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2607,8 +2676,15 @@ namespace Serein.Workbench
|
||||
#region 顶部菜单栏 - 远程管理
|
||||
private async void ButtonStartRemoteServer_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
|
||||
await this.EnvDecorator.StartRemoteServerAsync();
|
||||
try
|
||||
{
|
||||
await this.EnvDecorator.StartRemoteServerAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -2628,9 +2704,16 @@ namespace Serein.Workbench
|
||||
// 连接成功,加载远程项目
|
||||
_ = Task.Run(async () =>
|
||||
{
|
||||
var flowEnvInfo = await EnvDecorator.GetEnvInfoAsync();
|
||||
EnvDecorator.LoadProject(flowEnvInfo, string.Empty);// 加载远程环境的项目
|
||||
|
||||
try
|
||||
{
|
||||
var flowEnvInfo = await EnvDecorator.GetEnvInfoAsync();
|
||||
EnvDecorator.LoadProject(flowEnvInfo, string.Empty);// 加载远程环境的项目
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
@@ -2906,32 +2989,6 @@ namespace Serein.Workbench
|
||||
#endregion
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 卸载DLL文件,清空当前项目
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="e"></param>
|
||||
private void UnloadAllButton_Click(object sender, RoutedEventArgs e)
|
||||
{
|
||||
EnvDecorator.ClearAll();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载DLL文件,清空当前项目
|
||||
/// </summary>
|
||||
private void UnloadAllAssemblies()
|
||||
{
|
||||
DllStackPanel.Children.Clear();
|
||||
FlowChartCanvas.Children.Clear();
|
||||
Connections.Clear();
|
||||
NodeControls.Clear();
|
||||
//currentLine = null;
|
||||
//startConnectNodeControl = null;
|
||||
MessageBox.Show("所有DLL已卸载。", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* /// <summary>
|
||||
/// 对象装箱测试
|
||||
|
||||
@@ -20,10 +20,10 @@
|
||||
<ColumnDefinition Width="*" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<GroupBox x:Name="Ac" Grid.Row="0" Header="动作" Margin="5">
|
||||
<GroupBox x:Name="ActionNodeGroupBox" Grid.Row="0" Header="动作" Margin="5" Visibility="Collapsed">
|
||||
<ListBox x:Name="ActionsListBox" Background="#D0F1F9"/>
|
||||
</GroupBox>
|
||||
<GroupBox x:Name="FlipflopNodes" Grid.Row="1" Header="触发器" Margin="5" >
|
||||
<GroupBox x:Name="FlipflopNodeGroupBox" Grid.Row="1" Header="触发器" Margin="5" Visibility="Collapsed">
|
||||
<ListBox x:Name="FlipflopsListBox" Background="#FACFC1"/>
|
||||
</GroupBox>
|
||||
</Grid>
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Serein.Workbench.Node.View
|
||||
public void AddAction(MethodDetailsInfo mdInfo)
|
||||
{
|
||||
AddTypeToListBox(mdInfo, ActionsListBox);
|
||||
ActionNodeGroupBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -61,6 +62,7 @@ namespace Serein.Workbench.Node.View
|
||||
public void AddFlipflop(MethodDetailsInfo mdInfo)
|
||||
{
|
||||
AddTypeToListBox(mdInfo, FlipflopsListBox);
|
||||
FlipflopNodeGroupBox.Visibility = Visibility.Visible;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -57,9 +57,9 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||
|
||||
<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
||||
<!--<PackageReference Include="MySqlConnector" Version="2.4.0" />
|
||||
<PackageReference Include="SqlSugarCore" Version="5.1.4.170" />
|
||||
<PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.171" />
|
||||
<PackageReference Include="SqlSugarCoreNoDrive" Version="5.1.4.171" />-->
|
||||
|
||||
<!--<PackageReference Include="LivetCask2" Version="4.0.2" />-->
|
||||
<!--<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />-->
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
|
||||
@@ -136,9 +137,17 @@ namespace Serein.Workbench.Themes
|
||||
treeViewItem.Expanded += TreeViewItem_Expanded;
|
||||
|
||||
var contextMenu = new ContextMenu();
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem("从此节点执行", (s, e) =>
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem("从此节点执行", async (s, e) =>
|
||||
{
|
||||
flowEnvironment.StartAsyncInSelectNode(tmpNodeTreeModel.RootNode.Guid);
|
||||
try
|
||||
{
|
||||
await flowEnvironment.StartAsyncInSelectNode(tmpNodeTreeModel.RootNode.Guid);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(ex);
|
||||
return;
|
||||
}
|
||||
}));
|
||||
contextMenu.Items.Add(MainWindow.CreateMenuItem("定位", (s, e) => flowEnvironment.NodeLocated(tmpNodeTreeModel.RootNode.Guid)));
|
||||
|
||||
|
||||
@@ -15,6 +15,17 @@ namespace Serein.Workbench.Node.View
|
||||
/// </summary>
|
||||
public abstract class NodeControlBase : UserControl, IDynamicFlowNode
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点所在的画布(以后需要将画布封装出来,实现多画布的功能)
|
||||
/// </summary>
|
||||
public Canvas NodeCanvas { get; set; }
|
||||
|
||||
private INodeContainerControl nodeContainerControl;
|
||||
/// <summary>
|
||||
/// 如果该节点放置在了某个容器节点,就会记录这个容器节点
|
||||
/// </summary>
|
||||
private INodeContainerControl NodeContainerControl { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 记录与该节点控件有关的所有连接
|
||||
/// </summary>
|
||||
@@ -36,6 +47,25 @@ namespace Serein.Workbench.Node.View
|
||||
SetBinding();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 放置在某个节点容器中
|
||||
/// </summary>
|
||||
public void PlaceToContainer(INodeContainerControl nodeContainerControl)
|
||||
{
|
||||
this.nodeContainerControl = nodeContainerControl;
|
||||
NodeCanvas.Children.Remove(this); // 从画布上移除
|
||||
nodeContainerControl.PlaceNode(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从某个节点容器取出
|
||||
/// </summary>
|
||||
public void TakeOutContainer()
|
||||
{
|
||||
nodeContainerControl.TakeOutNode(this);
|
||||
NodeCanvas.Children.Add(this); // 重新添加到画布上
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加与该节点有关的连接后,记录下来
|
||||
/// </summary>
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Serein.Workbench.Node.View
|
||||
|
||||
public void PlaceNode(NodeControlBase nodeControl)
|
||||
{
|
||||
GlobalDataPanel.Children.Clear();
|
||||
//GlobalDataPanel.Children.Clear();
|
||||
GlobalDataPanel.Children.Add(nodeControl);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user