实现了多画布下,节点的复制粘贴功能

This commit is contained in:
fengjiayi
2025-05-27 18:32:40 +08:00
parent 7ad6041be6
commit 7848af0363
53 changed files with 1187 additions and 499 deletions

View File

@@ -1097,16 +1097,19 @@ namespace Serein.Library.Api
void SetUIContextOperation(UIContextOperation uiContextOperation); void SetUIContextOperation(UIContextOperation uiContextOperation);
/// <summary> /// <summary>
/// 开始运行 /// 开始运行流程
/// </summary> /// </summary>
Task<bool> StartFlowAsync(); /// <param name="canvasGuids">需要运行的流程Guid</param>
/// <returns></returns>
Task<bool> StartFlowAsync(string[] canvasGuids);
/// <summary> /// <summary>
/// 从选定的节点开始运行 /// 从选定的节点开始运行
/// </summary> /// </summary>
/// <param name="startNodeGuid"></param> /// <param name="startNodeGuid"></param>
/// <returns></returns> /// <returns></returns>
Task<bool> StartAsyncInSelectNode(string startNodeGuid); Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid);
/// <summary> /// <summary>
/// 结束运行 /// 结束运行

View File

@@ -7,12 +7,5 @@ using System.Threading.Tasks;
namespace Serein.Library namespace Serein.Library
{ {
/// <summary>
/// 拖拽创建节点使用的数据
/// </summary>
public class MoveNodeData
{
public NodeControlType NodeControlType { get; set; }
public MethodDetailsInfo MethodDetailsInfo { get; set; }
}
} }

View File

@@ -101,10 +101,10 @@ namespace Serein.Library
// 生成参数列表 // 生成参数列表
ParameterData[] parameterData = nodeModel.SaveParameterInfo(); ParameterData[] parameterData = nodeModel.SaveParameterInfo();
NodeInfo nodeInfo = new NodeInfo var nodeInfo = new NodeInfo
{ {
Guid = nodeModel.Guid,
CanvasGuid = nodeModel.CanvasGuid, CanvasGuid = nodeModel.CanvasGuid,
Guid = nodeModel.Guid,
AssemblyName = nodeModel.MethodDetails.AssemblyName, AssemblyName = nodeModel.MethodDetails.AssemblyName,
MethodName = nodeModel.MethodDetails?.MethodName, MethodName = nodeModel.MethodDetails?.MethodName,
Label = nodeModel.MethodDetails?.MethodAnotherName, Label = nodeModel.MethodDetails?.MethodAnotherName,
@@ -130,10 +130,12 @@ namespace Serein.Library
/// <summary> /// <summary>
/// 从节点信息加载节点 /// 从节点信息加载节点
/// </summary> /// </summary>
/// <param name="nodeModel"></param>
/// <param name="nodeInfo"></param> /// <param name="nodeInfo"></param>
/// <returns></returns> /// <returns></returns>
public static void LoadInfo(this NodeModelBase nodeModel, NodeInfo nodeInfo) public static void LoadInfo(this NodeModelBase nodeModel, NodeInfo nodeInfo)
{ {
nodeModel.CanvasGuid = nodeInfo.CanvasGuid;
nodeModel.Guid = nodeInfo.Guid; nodeModel.Guid = nodeInfo.Guid;
nodeModel.Position = nodeInfo.Position ?? new PositionOfUI(0, 0);// 加载位置信息 nodeModel.Position = nodeInfo.Position ?? new PositionOfUI(0, 0);// 加载位置信息
var md = nodeModel.MethodDetails; // 当前节点的方法说明 var md = nodeModel.MethodDetails; // 当前节点的方法说明
@@ -191,6 +193,7 @@ namespace Serein.Library
/// <summary> /// <summary>
/// 开始执行 /// 开始执行
/// </summary> /// </summary>
/// <param name="nodeModel"></param>
/// <param name="context"></param> /// <param name="context"></param>
/// <param name="token">流程运行</param> /// <param name="token">流程运行</param>
/// <returns></returns> /// <returns></returns>

View File

@@ -23,6 +23,7 @@ namespace Serein.Library
Env = env; Env = env;
} }
public IFlowEnvironment Env { get; } public IFlowEnvironment Env { get; }
/// <summary> /// <summary>
@@ -79,8 +80,6 @@ namespace Serein.Library
/// </summary> /// </summary>
private string _startNode; private string _startNode;
} }

View File

@@ -27,7 +27,7 @@ namespace Serein.Library
public string NodeType { get; set; } public string NodeType { get; set; }
/// <summary> /// <summary>
/// 方法说明 /// 方法别名
/// </summary> /// </summary>
public string MethodAnotherName { get; set; } public string MethodAnotherName { get; set; }

View File

@@ -336,6 +336,7 @@ namespace Serein.Library
_x = x; _y = y; _x = x; _y = y;
} }
/// <summary> /// <summary>
/// 指示控件在画布的横向向方向上的位置 /// 指示控件在画布的横向向方向上的位置
/// </summary> /// </summary>

View File

@@ -14,6 +14,7 @@
<PackageLicenseExpression>MIT</PackageLicenseExpression> <PackageLicenseExpression>MIT</PackageLicenseExpression>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance> <PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
<SatelliteResourceLanguages>no</SatelliteResourceLanguages>
<GenerateDocumentationFile>true</GenerateDocumentationFile> <GenerateDocumentationFile>true</GenerateDocumentationFile>
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>

View File

@@ -368,27 +368,87 @@ namespace Serein.NodeFlow.Env
/// 异步运行 /// 异步运行
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task<bool> StartFlowAsync() public async Task<bool> StartFlowAsync(string[] canvasGuids)
{ {
#region
HashSet<string> guids = new HashSet<string>();
bool isBreak = false;
foreach (var canvasGuid in canvasGuids)
{
if (guids.Contains(canvasGuid))
{
SereinEnv.WriteLine(InfoType.WARN, $"画布重复,停止运行。{canvasGuid}");
isBreak = true;
}
if (!FlowCanvass.ContainsKey(canvasGuid))
{
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{canvasGuid}");
isBreak = true;
}
var count = NodeModels.Values.Count(n => n.CanvasGuid.Equals(canvasGuid));
if(count == 0)
{
SereinEnv.WriteLine(InfoType.WARN, $"画布没有节点,停止运行。{canvasGuid}");
isBreak = true;
}
else
{
guids.Add(canvasGuid);
}
}
if (isBreak)
{
guids.Clear();
return false;
}
#endregion
#region
Dictionary<string, FlowTask> flowTasks = [];
foreach (var guid in guids)
{
if (!TryGetCanvasModel(guid, out var canvasModel))
{
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在,停止运行。{guid}");
return false;
}
var ft = new FlowTask();
ft.GetNodes = () => NodeModels.Values.Where(node => node.CanvasGuid.Equals(guid)).ToList();
var startNodeModel = NodeModels.GetValueOrDefault(canvasModel.StartNode);
if(startNodeModel is null)
{
SereinEnv.WriteLine(InfoType.WARN, $"画布不存在起始节点,将停止运行。{guid}");
return false;
}
ft.GetStartNode = () => startNodeModel;
flowTasks.Add(guid, ft);
}
#endregion
IOC.Reset(); IOC.Reset();
IOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口 IOC.Register<IScriptFlowApi, ScriptFlowApi>(); // 注册脚本接口
var flowTaskOptions = new FlowWorkOptions var flowTaskOptions = new FlowWorkOptions
{ {
Environment = this, Environment = this, // 流程
FlowContextPool = new ObjectPool<IDynamicContext>(() => new DynamicContext(this)), Flows = flowTasks,
//Nodes = NodeModels.Values.ToList(), FlowContextPool = new ObjectPool<IDynamicContext>(() => new DynamicContext(this)), // 上下文对象池
AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(), AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(), // 需要自动实例化的类型
InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init), InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init),
LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading), LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading),
ExitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit), ExitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit),
}; };
flowTaskManagement = new FlowWorkManagement(flowTaskOptions); flowTaskManagement = new FlowWorkManagement(flowTaskOptions);
var cts = new CancellationTokenSource(); var cts = new CancellationTokenSource();
try try
{ {
var t =await flowTaskManagement.RunAsync(cts.Token); var t = await flowTaskManagement.RunAsync(cts.Token);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -405,12 +465,13 @@ namespace Serein.NodeFlow.Env
} }
/// <summary> /// <summary>
/// 从选定节点开始运行 /// 从选定节点开始运行
/// </summary> /// </summary>
/// <param name="startNodeGuid"></param> /// <param name="startNodeGuid"></param>
/// <returns></returns> /// <returns></returns>
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid) public async Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
{ {
if (flowTaskManagement is null) if (flowTaskManagement is null)
@@ -425,12 +486,6 @@ namespace Serein.NodeFlow.Env
{ {
return false; return false;
} }
//var getExp = "@get .DebugSetting.IsEnable";
//var getExpResult1 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel,out _);
//var setExp = "@set .DebugSetting.IsEnable = false";
//SerinExpressionEvaluator.Evaluate(setExp, nodeModel,out _);
//var getExpResult2 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _);
await flowTaskManagement.StartFlowInSelectNodeAsync(this, nodeModel); await flowTaskManagement.StartFlowInSelectNodeAsync(this, nodeModel);
return true; return true;
} }
@@ -559,9 +614,22 @@ namespace Serein.NodeFlow.Env
LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集 LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集
} }
_ = Task.Run( async () => _ = Task.Run( async () =>
{ {
// 加载画布
foreach (var canvasInfo in projectData.Canvass)
{
LoadCanvas(canvasInfo);
}
await LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息 await LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息
// 加载画布
foreach (var canvasInfo in projectData.Canvass)
{
await SetStartNodeAsync(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点
}
//await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点 //await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点
}); });
@@ -779,20 +847,31 @@ namespace Serein.NodeFlow.Env
/// <returns></returns> /// <returns></returns>
public async Task<FlowCanvasDetailsInfo> CreateCanvasAsync(string canvasName, int width, int height) public async Task<FlowCanvasDetailsInfo> CreateCanvasAsync(string canvasName, int width, int height)
{ {
var model = new FlowCanvasDetails(this) var info = new FlowCanvasDetailsInfo()
{ {
Guid = Guid.NewGuid().ToString(), Guid = Guid.NewGuid().ToString(),
Height = height, Height = height,
Width = width, Width = width,
ViewX = 0,
ViewY = 0,
ScaleY = 1,
ScaleX = 1,
Name = !string.IsNullOrWhiteSpace(canvasName) ? canvasName : $"流程图{_addCanvasCount++}", Name = !string.IsNullOrWhiteSpace(canvasName) ? canvasName : $"流程图{_addCanvasCount++}",
}; };
var model = LoadCanvas(info);
return info;
}
private FlowCanvasDetails LoadCanvas(FlowCanvasDetailsInfo info)
{
var model = new FlowCanvasDetails(this);
model.LoadInfo(info);
FlowCanvass.Add(model.Guid, model); FlowCanvass.Add(model.Guid, model);
await UIContextOperation.InvokeAsync(() => UIContextOperation.InvokeAsync(() =>
{ {
OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model)); OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model));
}); });
var info = model.ToInfo(); return model;
return info;
} }
/// <summary> /// <summary>
@@ -957,7 +1036,7 @@ namespace Serein.NodeFlow.Env
#region #region
foreach (var toNode in NodeModels.Values) foreach (var toNode in NodeModels.Values)
{ {
var canvasGuid = toNode.Guid; var canvasGuid = toNode.CanvasGuid;
if (toNode.MethodDetails.ParameterDetailss == null) if (toNode.MethodDetails.ParameterDetailss == null)
{ {
continue; continue;
@@ -1727,7 +1806,7 @@ namespace Serein.NodeFlow.Env
private bool TryAddNode(NodeModelBase nodeModel) private bool TryAddNode(NodeModelBase nodeModel)
{ {
nodeModel.Guid ??= Guid.NewGuid().ToString(); nodeModel.Guid ??= Guid.NewGuid().ToString();
NodeModels[nodeModel.Guid] = nodeModel; NodeModels.TryAdd(nodeModel.Guid, nodeModel);
// 如果是触发器,则需要添加到专属集合中 // 如果是触发器,则需要添加到专属集合中
if (nodeModel is SingleFlipflopNode flipflopNode) if (nodeModel is SingleFlipflopNode flipflopNode)
@@ -1944,32 +2023,22 @@ namespace Serein.NodeFlow.Env
/// <summary> /// <summary>
/// 更改起点节点 /// 更改起点节点
/// </summary> /// </summary>
/// <param name="newStartNode"></param> /// <param name="cavnasModel">节点所在的画布</param>
/// <param name="oldStartNode"></param> /// <param name="newStartNode">起始节点</param>
private void SetStartNode(FlowCanvasDetails cavnasModel, NodeModelBase newStartNode) private void SetStartNode(FlowCanvasDetails cavnasModel, NodeModelBase newStartNode)
{ {
var oldNodeGuid = cavnasModel.StartNode; var oldNodeGuid = cavnasModel.StartNode;
/*if(TryGetNodeModel(oldNodeGuid, out var newStartNodeModel))
{
newStartNode.IsStart = false;
}*/
cavnasModel.StartNode = newStartNode.Guid; cavnasModel.StartNode = newStartNode.Guid;
//newStartNode.IsStart = true;
UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode))); UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode)));
//if (OperatingSystem.IsWindows())
//{
// }
} }
///// <summary>
///// 输出内容
///// </summary>
///// <param name="msg"></param>
//private void Output(string msg)
//{
// if (OperatingSystem.IsWindows())
// {
// UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg));
// }
//}
/// <summary> /// <summary>
/// 向容器登记缓存的持久化实例 /// 向容器登记缓存的持久化实例
/// </summary> /// </summary>

View File

@@ -263,7 +263,7 @@ namespace Serein.NodeFlow.Env
/// <param name="toNodeJunctionType">目标节点控制点</param> /// <param name="toNodeJunctionType">目标节点控制点</param>
/// <param name="invokeType">决定了方法执行后的后继行为</param> /// <param name="invokeType">决定了方法执行后的后继行为</param>
public async Task<bool> ConnectInvokeNodeAsync(string canvasGuid, public async Task<bool> ConnectInvokeNodeAsync(string canvasGuid,
string fromNodeGuid, string fromNodeGuid,
string toNodeGuid, string toNodeGuid,
JunctionType fromNodeJunctionType, JunctionType fromNodeJunctionType,
JunctionType toNodeJunctionType, JunctionType toNodeJunctionType,
@@ -518,14 +518,16 @@ namespace Serein.NodeFlow.Env
return await currentFlowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid); return await currentFlowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid);
} }
public async Task<bool> StartFlowAsync() public async Task<bool> StartFlowAsync(string[] canvasGuids)
{ {
return await currentFlowEnvironment.StartFlowAsync(); return await currentFlowEnvironment.StartFlowAsync(canvasGuids);
} }
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid)
public async Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
{ {
return await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid); return await currentFlowEnvironment.StartFlowFromSelectNodeAsync(startNodeGuid);
} }

View File

@@ -173,10 +173,10 @@ namespace Serein.NodeFlow.Env
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlow)] [AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlow)]
private async Task<object> StartAsync() private async Task<object> StartAsync(string[] canvasGuid)
{ {
var uiContextOperation = environment.IOC.Get<UIContextOperation>(); var uiContextOperation = environment.IOC.Get<UIContextOperation>();
var state = await environment.StartFlowAsync(); var state = await environment.StartFlowAsync(canvasGuid);
return new return new
{ {
state = state, state = state,
@@ -191,7 +191,7 @@ namespace Serein.NodeFlow.Env
[AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlowInSelectNode)] [AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlowInSelectNode)]
private async Task<object> StartAsyncInSelectNode(string nodeGuid) private async Task<object> StartAsyncInSelectNode(string nodeGuid)
{ {
var state = await environment.StartAsyncInSelectNode(nodeGuid); var state = await environment.StartFlowFromSelectNodeAsync(nodeGuid);
return new return new
{ {
state = state, state = state,

View File

@@ -420,18 +420,22 @@ namespace Serein.NodeFlow.Env
/// 启动远程环境的流程 /// 启动远程环境的流程
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task<bool> StartFlowAsync() public async Task<bool> StartFlowAsync(string[] canvasGuids)
{ {
// 远程环境下不需要UI上下文 // 远程环境下不需要UI上下文
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.StartFlow); var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.StartFlow, new
{
canvasGuids
});
return result; return result;
} }
/// <summary> /// <summary>
/// 从选定的节点开始运行 /// 从选定的节点开始运行
/// </summary> /// </summary>
/// <param name="startNodeGuid"></param> /// <param name="startNodeGuid"></param>
/// <returns></returns> /// <returns></returns>
public async Task<bool> StartAsyncInSelectNode(string startNodeGuid) public async Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
{ {
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.StartFlowInSelectNode, new var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.StartFlowInSelectNode, new
{ {

View File

@@ -43,7 +43,7 @@ namespace Serein.NodeFlow
} }
/// <summary> /// <summary>
/// 初始化 /// 初始化
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public async Task<bool> RunAsync(CancellationToken token) public async Task<bool> RunAsync(CancellationToken token)
@@ -85,22 +85,19 @@ namespace Serein.NodeFlow
var flowNodes = flow.GetNodes(); var flowNodes = flow.GetNodes();
// 找到流程的起始节点,开始运行 // 找到流程的起始节点,开始运行
NodeModelBase? startNode = flowNodes.FirstOrDefault(node => node.IsStart); NodeModelBase startNode = flow.GetStartNode();
if (startNode is null)
{
return false;
}
// 是否后台运行当前画布流程 // 是否后台运行当前画布流程
if (flow.IsTaskAsync) if (flow.IsTaskAsync)
{ {
_ = Task.Run(async () => await CallStartNode(startNode)); _ = Task.Run(async () => await CallStartNode(startNode), token); // 后台调用流程中的触发器
} }
else else
{ {
await CallStartNode(startNode); await CallStartNode(startNode);
} }
var flipflopTasks = CallFlipflopNode(flow); // 获取所有触发器异步任务 _ = Task.Run(async () => await CallFlipflopNode(flow), token); // 后台调用流程中的触发器
_ = Task.Run(async () => await flipflopTasks); // 后台调用流程中的触发器
} }
// 等待流程运行完成 // 等待流程运行完成
@@ -226,7 +223,7 @@ namespace Serein.NodeFlow
return isSuccessful; return isSuccessful;
} }
private Task CallFlipflopNode(FlowTask flow) private async Task CallFlipflopNode(FlowTask flow)
{ {
var env = WorkOptions.Environment; var env = WorkOptions.Environment;
var flipflopNodes = flow.GetNodes().Where(item => item is SingleFlipflopNode node var flipflopNodes = flow.GetNodes().Where(item => item is SingleFlipflopNode node
@@ -242,9 +239,8 @@ namespace Serein.NodeFlow
{ {
await RunGlobalFlipflopAsync(env, node); // 启动流程时启动全局触发器 await RunGlobalFlipflopAsync(env, node); // 启动流程时启动全局触发器
}); });
Task.WhenAll(tasks); await Task.WhenAll(tasks);
} }
return Task.CompletedTask;
} }
/// <summary> /// <summary>
@@ -257,6 +253,7 @@ namespace Serein.NodeFlow
var pool = WorkOptions.FlowContextPool; var pool = WorkOptions.FlowContextPool;
var token = WorkOptions.CancellationTokenSource.Token; var token = WorkOptions.CancellationTokenSource.Token;
var context = pool.Allocate(); var context = pool.Allocate();
context.Reset();
await startNode.StartFlowAsync(context, token); await startNode.StartFlowAsync(context, token);
context.Exit(); context.Exit();
pool.Free(context); pool.Free(context);

View File

@@ -46,7 +46,7 @@ namespace Serein.NodeFlow
/// <summary> /// <summary>
/// 上下文线程池 /// 上下文线程池
/// </summary> /// </summary>
public Serein.Library.Utils.ObjectPool<IDynamicContext> FlowContextPool { get; set; } public Serein.Library.Utils.ObjectPool<IDynamicContext> FlowContextPool { get; set; }
/// <summary> /// <summary>
/// 每个画布需要启用的节点 /// 每个画布需要启用的节点

View File

@@ -69,6 +69,11 @@ namespace Serein.NodeFlow.Tool
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
if (!Directory.Exists(path))
{
SereinEnv.WriteLine(InfoType.ERROR, $"尝试加载Dll时失败路径不存在。{path}");
return;
}
foreach (var file in Directory.GetFiles(path, "*.dll")) foreach (var file in Directory.GetFiles(path, "*.dll"))
{ {
LoadWindowsLibrarie(file); LoadWindowsLibrarie(file);

View File

@@ -23,6 +23,11 @@ namespace Serein.Workbench.Api
/// </summary> /// </summary>
string Name { get; } string Name { get; }
/// <summary>
/// 数据
/// </summary>
FlowCanvasDetails Model { get; }
/// <summary> /// <summary>
/// 移除节点 /// 移除节点
/// </summary> /// </summary>

View File

@@ -5,8 +5,6 @@
xmlns:view="clr-namespace:Serein.Workbench.Views" xmlns:view="clr-namespace:Serein.Workbench.Views"
StartupUri="Views/FlowWorkbenchView.xaml" StartupUri="Views/FlowWorkbenchView.xaml"
Startup="Application_Startup"> Startup="Application_Startup">
<!--StartupUri="Views/FlowWorkbenchView.xaml"-->
<!--StartupUri="MainWindow.xaml"-->
<Application.Resources> <Application.Resources>
<ResourceDictionary> <ResourceDictionary>

View File

@@ -10,6 +10,7 @@ using Serein.Workbench.ViewModels;
using System.Diagnostics; using System.Diagnostics;
using System.IO; using System.IO;
using System.Windows; using System.Windows;
using System.Windows.Input;
using System.Windows.Threading; using System.Windows.Threading;
namespace Serein.Workbench namespace Serein.Workbench
@@ -38,10 +39,9 @@ namespace Serein.Workbench
public static void AddWorkbenchServices(this IServiceCollection collection) public static void AddWorkbenchServices(this IServiceCollection collection)
{ {
collection.AddSingleton<IFlowEEForwardingService, FlowEEForwardingService>(); // 流程事件管理 collection.AddSingleton<IFlowEEForwardingService, FlowEEForwardingService>(); // 流程事件管理
collection.AddSingleton<IKeyEventService, KeyEventService>();// 按键事件管理
collection.AddSingleton<IWorkbenchEventService, WorkbenchEventService>(); // 流程事件管理 collection.AddSingleton<IWorkbenchEventService, WorkbenchEventService>(); // 流程事件管理
collection.AddSingleton<FlowNodeService>(); // 节点操作管理 collection.AddSingleton<FlowNodeService>(); // 节点操作管理
// collection.AddSingleton<IKeyEventService, KeyEventService>(); // 按键事件管理
//collection.AddSingleton<FlowNodeControlService>(); // 流程节点控件管理
} }
@@ -87,27 +87,30 @@ namespace Serein.Workbench
{ {
return ServiceProvider?.GetService<T>() ?? throw new NullReferenceException(); return ServiceProvider?.GetService<T>() ?? throw new NullReferenceException();
} }
public App() public App()
{ {
var collection = new ServiceCollection(); var collection = new ServiceCollection();
collection.AddWorkbenchServices(); collection.AddWorkbenchServices();
collection.AddFlowServices(); collection.AddFlowServices();
collection.AddViewModelServices(); collection.AddViewModelServices();
var services = collection.BuildServiceProvider(); // 绑定并返回获取实例的服务接口 var services = collection.BuildServiceProvider(); // 绑定并返回获取实例的服务接口
App.ServiceProvider = services; App.ServiceProvider = services;
#if DEBUG
_ = this.LoadLocalProjectAsync(); _ = this.LoadLocalProjectAsync();
#endif
} }
private async Task LoadLocalProjectAsync() private async Task LoadLocalProjectAsync()
{ {
await Task.Delay(500); await Task.Delay(500);
#if DEBUG #if DEBUG
if (1 == 10) if (1 ==1)
{ {
// 这里是测试代码,可以删除 // 这里是测试代码,可以删除
string filePath; string filePath;
@@ -115,12 +118,17 @@ namespace Serein.Workbench
filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\banyunqi\project.dnf"; filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\banyunqi\project.dnf";
filePath = @"F:\临时\project\project.dnf"; filePath = @"F:\临时\project\project.dnf";
filePath = @"F:\TempFile\flow\qrcode\project.dnf"; filePath = @"F:\TempFile\flow\qrcode\project.dnf";
//filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\test.dnf"; filePath = @"F:\TempFile\flow\temp\project.dnf";
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 if (File.Exists(filePath))
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content); {
App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;// string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
var dir = Path.GetDirectoryName(filePath); App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
App.GetService<IFlowEnvironment>().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
var dir = Path.GetDirectoryName(filePath);
App.GetService<IFlowEnvironment>().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath);
}
} }
#endif #endif
} }

View File

@@ -11,16 +11,16 @@
<converter:CountToVisibilityConverter x:Key="CountToVisibilityConverter"/> <converter:CountToVisibilityConverter x:Key="CountToVisibilityConverter"/>
</UserControl.Resources> </UserControl.Resources>
<ListBox ItemsSource="{Binding Nodes}" <ListBox ItemsSource="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}}"
Visibility="{Binding Nodes, Converter={StaticResource CountToVisibilityConverter}}" Visibility="{Binding ItemsSource, RelativeSource={RelativeSource AncestorType=UserControl}, Converter={StaticResource CountToVisibilityConverter}}"
Background="{Binding BackgroundColor}"> Background="{Binding Background, RelativeSource={RelativeSource AncestorType=UserControl}}">
<ListBox.ItemTemplate> <ListBox.ItemTemplate>
<DataTemplate> <DataTemplate>
<Grid Margin="2"> <Grid Margin="2" MouseLeftButtonDown="Grid_MouseLeftButtonDown" MouseMove="Grid_MouseMove" DataContext="{Binding}">
<StackPanel Orientation="Horizontal"> <StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding NodeType}"></TextBlock> <TextBlock Text="{Binding NodeType}"/>
<TextBlock Text="{Binding AnotherName}" Margin="4,0,0,0"></TextBlock> <TextBlock Text="{Binding MethodAnotherName}" Margin="4,0,0,0"/>
<TextBlock Text="{Binding MethodName}" Margin="6,0,0,0"></TextBlock> <TextBlock Text="{Binding MethodName}" Margin="6,0,0,0"/>
</StackPanel> </StackPanel>
</Grid> </Grid>
</DataTemplate> </DataTemplate>

View File

@@ -1,4 +1,5 @@
using Serein.Library; using Serein.Library;
using Serein.Library.Utils;
using Serein.Workbench.Models; using Serein.Workbench.Models;
using System; using System;
using System.Collections; using System.Collections;
@@ -20,10 +21,19 @@ using System.Windows.Shapes;
namespace Serein.Workbench.Customs namespace Serein.Workbench.Customs
{ {
/// <summary>
public class Test: DependencyObject /// 拖拽创建节点类型
/// </summary>
public static class MouseNodeType
{ {
/// <summary>
/// 创建来自DLL的节点
/// </summary>
public static string CreateDllNodeInCanvas { get; } = nameof(CreateDllNodeInCanvas);
/// <summary>
/// 创建基础节点
/// </summary>
public static string CreateBaseNodeInCanvas { get; } = nameof(CreateBaseNodeInCanvas);
} }
/// <summary> /// <summary>
@@ -35,66 +45,90 @@ namespace Serein.Workbench.Customs
public FlowMethodInfoListBox() public FlowMethodInfoListBox()
{ {
this.DataContext = this;
InitializeComponent(); InitializeComponent();
} }
public IEnumerable<FlowLibraryMethodDetailsInfo> Nodes public static readonly DependencyProperty ItemsSourceProperty =
DependencyProperty.Register(nameof(ItemsSource), typeof(IEnumerable), typeof(FlowMethodInfoListBox), new PropertyMetadata(null));
public IEnumerable ItemsSource
{ {
get { return (IEnumerable<FlowLibraryMethodDetailsInfo>)GetValue(NodesProperty); } get => (IEnumerable)GetValue(ItemsSourceProperty);
set { SetValue(NodesProperty, value); } set => SetValue(ItemsSourceProperty, value);
} }
//public ItemCollection Items public static readonly DependencyProperty BackgroundProperty =
//{ DependencyProperty.Register(nameof(Background), typeof(Brush), typeof(FlowMethodInfoListBox), new PropertyMetadata(Brushes.Transparent));
// get
// {
// return (ItemCollection)GetValue(ItemsProperty);
// }
// set
// {
// SetValue(ItemsProperty, value);
// }
//}
public Brush Background
public static readonly DependencyProperty NodesProperty = DependencyProperty.Register("NodesProperty", typeof(IEnumerable<FlowLibraryMethodDetailsInfo>), typeof(FlowMethodInfoListBox));
//public int TurnValue
//{
// get
// {
// return (int)GetValue(TurnValueProperty);
// }
// set
// {
// SetValue(TurnValueProperty, value);
// }
//}
// public static readonly DependencyProperty NodesProperty = DependencyProperty.Register(nameof(Nodes), typeof(IEnumerable<FlowLibraryMethodDetailsInfo>), typeof(FlowMethodInfoListBox), new PropertyMetadata(null));
public Brush BackgroundColor
{ {
get { return (Brush)GetValue(BackgroundColorProperty); } get => (Brush)GetValue(BackgroundProperty);
set { SetValue(BackgroundColorProperty, value); } set => SetValue(BackgroundProperty, value);
} }
public static readonly DependencyProperty BackgroundColorProperty =
DependencyProperty.Register(nameof(BackgroundColor), typeof(Brush), typeof(FlowMethodInfoListBox), new PropertyMetadata(Brushes.White));
/// <summary>
/// 存储拖拽开始时的鼠标位置
/// </summary>
private Point _dragStartPoint;
private void Grid_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
// 记录鼠标按下时的位置
_dragStartPoint = e.GetPosition(null);
}
private void Grid_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 Grid grid && grid.DataContext is MethodDetailsInfo mdInfo)
{
if (!EnumHelper.TryConvertEnum<Library.NodeType>(mdInfo.NodeType, out var nodeType))
{
return;
}
MoveNodeModel moveNodeModel = new MoveNodeModel()
{
NodeControlType = nodeType switch
{
NodeType.Action => NodeControlType.Action,
NodeType.Flipflop => NodeControlType.Flipflop,
NodeType.UI => NodeControlType.UI,
_ => NodeControlType.None,
},
MethodDetailsInfo = mdInfo
};
//MoveNodeData moveNodeData = new MoveNodeData
//{
// MethodDetailsInfo = mdInfo,
//};
if (moveNodeModel.NodeControlType == NodeControlType.None)
{
return;
}
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
DataObject dragData = new DataObject(MouseNodeType.CreateDllNodeInCanvas, moveNodeModel);
DragDrop.DoDragDrop(grid, dragData, DragDropEffects.Move);
}
}
}
} }
} }

View File

@@ -17,6 +17,10 @@ namespace Serein.Workbench
/// </summary> /// </summary>
public partial class LogWindow : Window public partial class LogWindow : Window
{ {
private static LogWindow instance = new LogWindow();
public static LogWindow Instance => instance;
private StringBuilder logBuffer = new StringBuilder(); private StringBuilder logBuffer = new StringBuilder();
private int logUpdateInterval = 200; // 批量更新的时间间隔(毫秒) private int logUpdateInterval = 200; // 批量更新的时间间隔(毫秒)
private Timer logUpdateTimer; private Timer logUpdateTimer;

View File

@@ -9,6 +9,7 @@ using Serein.NodeFlow.Env;
using Serein.NodeFlow.Tool; using Serein.NodeFlow.Tool;
using Serein.Workbench.Api; using Serein.Workbench.Api;
using Serein.Workbench.Extension; using Serein.Workbench.Extension;
using Serein.Workbench.Models;
using Serein.Workbench.Node; using Serein.Workbench.Node;
using Serein.Workbench.Node.View; using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel; using Serein.Workbench.Node.ViewModel;
@@ -27,20 +28,7 @@ using DataObject = System.Windows.DataObject;
namespace Serein.Workbench namespace Serein.Workbench
{ {
/// <summary>
/// 拖拽创建节点类型
/// </summary>
public static class MouseNodeType
{
/// <summary>
/// 创建来自DLL的节点
/// </summary>
public static string CreateDllNodeInCanvas { get; } = nameof(CreateDllNodeInCanvas);
/// <summary>
/// 创建基础节点
/// </summary>
public static string CreateBaseNodeInCanvas { get; } = nameof(CreateBaseNodeInCanvas);
}
@@ -1257,6 +1245,7 @@ namespace Serein.Workbench
#endregion #endregion
#region DLL文件到左侧功能区 #region DLL文件到左侧功能区
/// <summary> /// <summary>
/// 当拖动文件到窗口时触发加载DLL文件 /// 当拖动文件到窗口时触发加载DLL文件
/// </summary> /// </summary>
@@ -1308,6 +1297,7 @@ namespace Serein.Workbench
/// </summary> /// </summary>
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e) private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
{ {
dynamic GlobalJunctionData = "";
var myData = GlobalJunctionData.MyGlobalConnectingData; var myData = GlobalJunctionData.MyGlobalConnectingData;
if (myData.IsCreateing && e.LeftButton == MouseButtonState.Pressed) if (myData.IsCreateing && e.LeftButton == MouseButtonState.Pressed)
{ {
@@ -1406,12 +1396,12 @@ namespace Serein.Workbench
PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y); PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y);
if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas)) if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas))
{ {
if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeData nodeData) if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeModel moveModel)
{ {
Task.Run(async () => Task.Run(async () =>
{ {
await EnvDecorator.CreateNodeAsync("MainCanvas", nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象 await EnvDecorator.CreateNodeAsync("MainCanvas", moveModel.NodeControlType, position, moveModel.MethodDetailsInfo); // 创建DLL文件的节点对象
}); });
} }
} }
@@ -1704,7 +1694,8 @@ namespace Serein.Workbench
Mouse.OverrideCursor = null; // 恢复视觉效果 Mouse.OverrideCursor = null; // 恢复视觉效果
ViewModel.IsConnectionArgSourceNode = false; ViewModel.IsConnectionArgSourceNode = false;
ViewModel.IsConnectionInvokeNode = false; ViewModel.IsConnectionInvokeNode = false;
GlobalJunctionData.OK(); dynamic GlobalJunctionData = "";
} }
#region #region
@@ -1908,6 +1899,8 @@ namespace Serein.Workbench
/// <param name="e"></param> /// <param name="e"></param>
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{ {
dynamic GlobalJunctionData = "";
if (GlobalJunctionData.MyGlobalConnectingData.IsCreateing) if (GlobalJunctionData.MyGlobalConnectingData.IsCreateing)
{ {
return; return;
@@ -1963,6 +1956,7 @@ namespace Serein.Workbench
FlowChartCanvas.ReleaseMouseCapture(); FlowChartCanvas.ReleaseMouseCapture();
} }
dynamic GlobalJunctionData = "";
// 创建连线 // 创建连线
if (GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing) if (GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing)
{ {
@@ -2818,8 +2812,9 @@ public class FlowLibrary
CancelSelectNode(); CancelSelectNode();
EndConnection(); EndConnection();
} }
dynamic GlobalJunctionData = "";
if(GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing) if (GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing)
{ {
if(myData.Type == JunctionOfConnectionType.Invoke) if(myData.Type == JunctionOfConnectionType.Invoke)
{ {

View File

@@ -9,21 +9,10 @@ using System.Threading.Tasks;
namespace Serein.Workbench.Models namespace Serein.Workbench.Models
{ {
public partial class FlowLibraryMethodDetailsInfo(MethodDetailsInfo info): ObservableObject
{
[ObservableProperty]
private string _anotherName = info.MethodAnotherName;
[ObservableProperty]
private string _assmblyName = info.AssemblyName;
[ObservableProperty]
private string _methodName = info.MethodName;
[ObservableProperty]
private string _nodeType = info.NodeType;
}
/// <summary>
/// 依赖信息
/// </summary>
internal partial class FlowLibraryInfo : ObservableObject internal partial class FlowLibraryInfo : ObservableObject
{ {
[ObservableProperty] [ObservableProperty]
@@ -33,12 +22,11 @@ namespace Serein.Workbench.Models
private string _libraryName; private string _libraryName;
[ObservableProperty] [ObservableProperty]
private ObservableCollection<FlowLibraryMethodDetailsInfo> _methodInfo; private ObservableCollection<MethodDetailsInfo> _methodInfo;
public List<MethodDetailsInfo> ActionNodes { get => MethodInfo.Where(x => x.NodeType == NodeType.Action.ToString()).ToList(); set { } }
public List<FlowLibraryMethodDetailsInfo> ActionNodes { get => MethodInfo.Where(x => x.NodeType == NodeType.Action.ToString()).ToList(); set { } } public List<MethodDetailsInfo> FlipflopNodes { get => MethodInfo.Where(x => x.NodeType == NodeType.Flipflop.ToString()).ToList(); set { } }
public List<FlowLibraryMethodDetailsInfo> FlipflopNodes { get => MethodInfo.Where(x => x.NodeType == NodeType.Flipflop.ToString()).ToList(); set { } } public List<MethodDetailsInfo> UINodes { get => MethodInfo.Where(x => x.NodeType == NodeType.UI.ToString()).ToList(); set { } }
public List<FlowLibraryMethodDetailsInfo> UINodes { get => MethodInfo.Where(x => x.NodeType == NodeType.UI.ToString()).ToList(); set { } }
} }
} }

View File

@@ -0,0 +1,18 @@
using Serein.Library;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Models
{
/// <summary>
/// 拖拽创建节点使用的数据
/// </summary>
internal class MoveNodeModel
{
public NodeControlType NodeControlType { get; set; }
public MethodDetailsInfo MethodDetailsInfo { get; set; }
}
}

View File

@@ -12,9 +12,15 @@ using System.Windows.Shapes;
using System.Windows.Media.Media3D; using System.Windows.Media.Media3D;
using System.Windows.Documents; using System.Windows.Documents;
using System.Threading; using System.Threading;
using Serein.Workbench.Services;
using Serein.Workbench.Tool;
namespace Serein.Workbench.Node.View namespace Serein.Workbench.Node.View
{ {
/// <summary>
/// 控制带的拓展方法
/// </summary>
internal static class MyUIFunc internal static class MyUIFunc
{ {
public static Pen CreateAndFreezePen() public static Pen CreateAndFreezePen()
@@ -31,10 +37,11 @@ namespace Serein.Workbench.Node.View
} }
} }
/// <summary>
/// 入参控件
/// </summary>
public class ParamsArgControl: Shape public class ParamsArgControl: Shape
{ {
public ParamsArgControl() public ParamsArgControl()
{ {
this.MouseDown += ParamsArg_OnMouseDown; // 增加或删除 this.MouseDown += ParamsArg_OnMouseDown; // 增加或删除
@@ -159,8 +166,10 @@ namespace Serein.Workbench.Node.View
public abstract class JunctionControlBase : Shape public abstract class JunctionControlBase : Shape
{ {
private readonly FlowNodeService flowNodeService;
protected JunctionControlBase() protected JunctionControlBase()
{ {
flowNodeService = App.GetService<FlowNodeService>();
this.Width = 25; this.Width = 25;
this.Height = 20; this.Height = 20;
this.MouseDown += JunctionControlBase_MouseDown; this.MouseDown += JunctionControlBase_MouseDown;
@@ -229,7 +238,7 @@ namespace Serein.Workbench.Node.View
{ {
if(_isMouseOver != value) if(_isMouseOver != value)
{ {
GlobalJunctionData.MyGlobalConnectingData.CurrentJunction = this; flowNodeService.ConnectingData.CurrentJunction = this;
_isMouseOver = value; _isMouseOver = value;
InvalidateVisual(); InvalidateVisual();
} }
@@ -252,22 +261,22 @@ namespace Serein.Workbench.Node.View
/// <returns></returns> /// <returns></returns>
protected Brush GetBackgrounp() protected Brush GetBackgrounp()
{ {
var myData = GlobalJunctionData.MyGlobalConnectingData; var cd = flowNodeService.ConnectingData;
if(!myData.IsCreateing) if(!cd.IsCreateing)
{ {
return Brushes.Transparent; return Brushes.Transparent;
} }
if (IsMouseOver) if (IsMouseOver)
{ {
if (myData.IsCanConnected) if (cd.IsCanConnected)
{ {
if (myData.Type == JunctionOfConnectionType.Invoke) if (cd.Type == JunctionOfConnectionType.Invoke)
{ {
return myData.ConnectionInvokeType.ToLineColor(); return cd.ConnectionInvokeType.ToLineColor();
} }
else else
{ {
return myData.ConnectionArgSourceType.ToLineColor(); return cd.ConnectionArgSourceType.ToLineColor();
} }
} }
else else
@@ -320,15 +329,15 @@ namespace Serein.Workbench.Node.View
{ {
if (e.LeftButton == MouseButtonState.Pressed) if (e.LeftButton == MouseButtonState.Pressed)
{ {
var canvas = MainWindow.GetParentOfType<Canvas>(this); var canvas = WpfFuncTool.GetParentOfType<Canvas>(this);
if (canvas != null) if (canvas != null)
{ {
var myData = GlobalJunctionData.MyGlobalConnectingData; var cd = flowNodeService.ConnectingData;
myData.Reset(); cd.Reset();
myData.IsCreateing = true; // 表示开始连接 cd.IsCreateing = true; // 表示开始连接
myData.StartJunction = this; cd.StartJunction = this;
myData.CurrentJunction = this; cd.CurrentJunction = this;
myData.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas); cd.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas);
var junctionOfConnectionType = this.JunctionType.ToConnectyionType(); var junctionOfConnectionType = this.JunctionType.ToConnectyionType();
ConnectionLineShape bezierLine; // 类别 ConnectionLineShape bezierLine; // 类别
@@ -346,13 +355,13 @@ namespace Serein.Workbench.Node.View
return; return;
} }
bezierLine = new ConnectionLineShape(LineType.Bezier, bezierLine = new ConnectionLineShape(LineType.Bezier,
myData.StartPoint, cd.StartPoint,
myData.StartPoint, cd.StartPoint,
brushColor, brushColor,
isTop: true); // 绘制临时的线 isTop: true); // 绘制临时的线
Mouse.OverrideCursor = Cursors.Cross; // 设置鼠标为正在创建连线 Mouse.OverrideCursor = Cursors.Cross; // 设置鼠标为正在创建连线
myData.MyLine = new MyLine(canvas, bezierLine); cd.MyLine = new MyLine(canvas, bezierLine);
} }
} }
e.Handled = true; e.Handled = true;

View File

@@ -123,7 +123,7 @@ namespace Serein.Workbench.Node.View
} }
/// <summary> /// <summary>
/// 重置 /// 重置连线状态
/// </summary> /// </summary>
public void Reset() public void Reset()
{ {
@@ -139,7 +139,7 @@ namespace Serein.Workbench.Node.View
} }
public static class GlobalJunctionData public static class GlobalJunctionData1
{ {
//private static ConnectingData? myGlobalData; //private static ConnectingData? myGlobalData;
//private static object _lockObj = new object(); //private static object _lockObj = new object();

View File

@@ -1,6 +1,7 @@
using Serein.Library; using Serein.Library;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Workbench.Extension; using Serein.Workbench.Extension;
using Serein.Workbench.Tool;
using System; using System;
using System.Net; using System.Net;
using System.Windows; using System.Windows;
@@ -224,8 +225,8 @@ namespace Serein.Workbench.Node.View
private void ConfigureLineContextMenu() private void ConfigureLineContextMenu()
{ {
var contextMenu = new ContextMenu(); var contextMenu = new ContextMenu();
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => Remote())); contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除连线", (s, e) => Remote()));
contextMenu.Items.Add(MainWindow.CreateMenuItem("于父节点调用顺序中置顶", (s, e) => Topping())); contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("于父节点调用顺序中置顶", (s, e) => Topping()));
BezierLine.ContextMenu = contextMenu; BezierLine.ContextMenu = contextMenu;
} }

View File

@@ -140,26 +140,26 @@ namespace Serein.Workbench.Node.View
return; return;
} }
MoveNodeData moveNodeData = new MoveNodeData //MoveNodeData moveNodeData = new MoveNodeData
{ //{
NodeControlType = nodeType switch // NodeControlType = nodeType switch
{ // {
NodeType.Action => NodeControlType.Action, // NodeType.Action => NodeControlType.Action,
NodeType.Flipflop => NodeControlType.Flipflop, // NodeType.Flipflop => NodeControlType.Flipflop,
NodeType.UI => NodeControlType.UI, // NodeType.UI => NodeControlType.UI,
_ => NodeControlType.None, // _ => NodeControlType.None,
}, // },
MethodDetailsInfo = mdInfo, // MethodDetailsInfo = mdInfo,
}; //};
if(moveNodeData.NodeControlType == NodeControlType.None) //if(moveNodeData.NodeControlType == NodeControlType.None)
{ //{
return; // return;
} //}
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果 //// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
DataObject dragData = new DataObject(MouseNodeType.CreateDllNodeInCanvas, moveNodeData); //DataObject dragData = new DataObject(MouseNodeType.CreateDllNodeInCanvas, moveNodeData);
DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move); //DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move);
} }
} }
} }

View File

@@ -24,6 +24,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Remove="MainWindow.xaml.cs" />
<Compile Remove="MainWindowViewModel.cs" />
<Compile Remove="Node\FlipflopRegionControl.xaml.cs" /> <Compile Remove="Node\FlipflopRegionControl.xaml.cs" />
<Compile Remove="Node\INodeContainerControl.cs" /> <Compile Remove="Node\INodeContainerControl.cs" />
<Compile Remove="Node\Junction\NodeJunctionViewBase.cs" /> <Compile Remove="Node\Junction\NodeJunctionViewBase.cs" />
@@ -38,6 +40,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Page Remove="MainWindow.xaml" />
<Page Remove="Node\FlipflopRegionControl.xaml" /> <Page Remove="Node\FlipflopRegionControl.xaml" />
<Page Remove="Node\View\ActionRegionControl.xaml" /> <Page Remove="Node\View\ActionRegionControl.xaml" />
<Page Remove="Themes\ConditionControl.xaml" /> <Page Remove="Themes\ConditionControl.xaml" />

View File

@@ -26,17 +26,21 @@ namespace Serein.Workbench.Services
/// </summary> /// </summary>
private readonly IFlowEnvironment flowEnvironment; private readonly IFlowEnvironment flowEnvironment;
private readonly IFlowEnvironmentEvent flowEnvironmentEvent; private readonly IFlowEnvironmentEvent flowEnvironmentEvent;
private readonly UIContextOperation uIContextOperation;
/// <summary> /// <summary>
/// 转发流程运行环境各个事件的实现类 /// 转发流程运行环境各个事件的实现类
/// </summary> /// </summary>
/// <param name="flowEnvironment"></param> /// <param name="flowEnvironment"></param>
/// <param name="flowEnvironmentEvent"></param> /// <param name="flowEnvironmentEvent"></param>
/// <param name="uIContextOperation"></param>
public FlowEEForwardingService(IFlowEnvironment flowEnvironment, public FlowEEForwardingService(IFlowEnvironment flowEnvironment,
IFlowEnvironmentEvent flowEnvironmentEvent) IFlowEnvironmentEvent flowEnvironmentEvent,
UIContextOperation uIContextOperation)
{ {
this.flowEnvironment = flowEnvironment; this.flowEnvironment = flowEnvironment;
this.flowEnvironmentEvent = flowEnvironmentEvent; this.flowEnvironmentEvent = flowEnvironmentEvent;
this.uIContextOperation = uIContextOperation;
InitFlowEnvironmentEvent(); InitFlowEnvironmentEvent();
} }
@@ -185,7 +189,10 @@ namespace Serein.Workbench.Services
/// <param name="value"></param> /// <param name="value"></param>
private void FlowEnvironment_OnEnvOutEvent(InfoType type, string value) private void FlowEnvironment_OnEnvOutEvent(InfoType type, string value)
{ {
//LogOutWindow.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}"); uIContextOperation.Invoke(() =>
{
OnEnvOut?.Invoke(type, value);
});
} }
/// <summary> /// <summary>

View File

@@ -1,4 +1,6 @@
using Serein.Library; using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Serein.Library;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Workbench.Api; using Serein.Workbench.Api;
using Serein.Workbench.Node; using Serein.Workbench.Node;
@@ -6,7 +8,10 @@ using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel; using Serein.Workbench.Node.ViewModel;
using Serein.Workbench.ViewModels; using Serein.Workbench.ViewModels;
using Serein.Workbench.Views; using Serein.Workbench.Views;
using System.Text;
using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Media;
namespace Serein.Workbench.Services namespace Serein.Workbench.Services
{ {
@@ -34,7 +39,6 @@ namespace Serein.Workbench.Services
#endregion #endregion
#region #region
/// <summary> /// <summary>
/// 当前查看的画布 /// 当前查看的画布
@@ -51,7 +55,6 @@ namespace Serein.Workbench.Services
/// </summary> /// </summary>
public NodeControlType CurrentNodeControlType { get; set; } = NodeControlType.None; public NodeControlType CurrentNodeControlType { get; set; } = NodeControlType.None;
/// <summary> /// <summary>
/// 当前鼠标位置 /// 当前鼠标位置
/// </summary> /// </summary>
@@ -61,6 +64,12 @@ namespace Serein.Workbench.Services
/// 当前选中的节点 /// 当前选中的节点
/// </summary> /// </summary>
public NodeControlBase? CurrentSelectNodeControl { get; set; } public NodeControlBase? CurrentSelectNodeControl { get; set; }
/// <summary>
/// 连接数据
/// </summary>
public ConnectingData ConnectingData { get; } = new ConnectingData();
#endregion #endregion
/// <summary> /// <summary>
@@ -72,6 +81,11 @@ namespace Serein.Workbench.Services
/// </summary> /// </summary>
public NodeControlBase? ConnectionEndNode { get; set; } public NodeControlBase? ConnectionEndNode { get; set; }
/// <summary>
/// 当前所有画布
/// </summary>
public FlowCanvasView[] FlowCanvass => Canvass.Select(c => c.Value).ToArray();
/// <summary> /// <summary>
/// 记录流程画布 /// 记录流程画布
/// </summary> /// </summary>
@@ -134,7 +148,27 @@ namespace Serein.Workbench.Services
flowEEForwardingService.OnNodeTakeOut += FlowEEForwardingService_OnNodeTakeOut; ; // 节点从容器中取出 flowEEForwardingService.OnNodeTakeOut += FlowEEForwardingService_OnNodeTakeOut; ; // 节点从容器中取出
flowEEForwardingService.OnNodeConnectChange += FlowEEForwardingService_OnNodeConnectChange; // 节点连接状态改变事件 flowEEForwardingService.OnNodeConnectChange += FlowEEForwardingService_OnNodeConnectChange; // 节点连接状态改变事件
flowEEForwardingService.OnStartNodeChange += FlowEEForwardingService_OnStartNodeChange; // 画布起始节点改变
}
private void FlowEEForwardingService_OnStartNodeChange(StartNodeChangeEventArgs eventArgs)
{
string oldNodeGuid = eventArgs.OldNodeGuid;
string newNodeGuid = eventArgs.NewNodeGuid;
if (!TryGetControl(newNodeGuid, out var newStartNodeControl)) return;
if (!string.IsNullOrEmpty(oldNodeGuid))
{
if (!TryGetControl(oldNodeGuid, out var oldStartNodeControl)) return;
oldStartNodeControl.BorderBrush = Brushes.Black;
oldStartNodeControl.BorderThickness = new Thickness(0);
}
newStartNodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"));
newStartNodeControl.BorderThickness = new Thickness(2);
var node = newStartNodeControl?.ViewModel?.NodeModel;
} }
private void FlowEEForwardingService_OnNodeConnectChange(NodeConnectChangeEventArgs e) private void FlowEEForwardingService_OnNodeConnectChange(NodeConnectChangeEventArgs e)
@@ -387,7 +421,167 @@ namespace Serein.Workbench.Services
return Canvass.TryGetValue(nodeGuid, out flowCanvas); return Canvass.TryGetValue(nodeGuid, out flowCanvas);
} }
#region
/// <summary>
/// 从节点信息转换为Json文本数据
/// </summary>
public string CpoyNodeInfo(List<NodeModelBase> dictSelection)
{
// 遍历当前已选节点
foreach (var node in dictSelection.ToArray())
{
if (node.ChildrenNode.Count == 0)
{
continue;
}
// 遍历这些节点的子节点,添加过来
foreach (var childNode in node.ChildrenNode)
{
dictSelection.Add(childNode);
}
}
var nodeInfos = dictSelection.Select(item => item.ToInfo());
JObject json = new JObject()
{
["nodes"] = JArray.FromObject(nodeInfos)
};
var jsonText = json.ToString();
try
{
SereinEnv.WriteLine(InfoType.INFO, $"复制已选节点({dictSelection.Count}个)");
return jsonText;
}
catch (Exception ex)
{
SereinEnv.WriteLine(InfoType.ERROR, $"复制失败:{ex.Message}");
return string.Empty;
}
}
/// <summary>
/// 从Json中加载节点
/// </summary>
/// <param name="canvasGuid">需要加载在哪个画布上</param>
/// <param name="jsonText">文本内容</param>
/// <param name="positionOfUI">需要加载的位置</param>
public void PasteNodeInfo(string canvasGuid, string jsonText, PositionOfUI positionOfUI)
{
try
{
List<NodeInfo>? nodes = JsonConvert.DeserializeObject<List<NodeInfo>>(jsonText);
if (nodes is not null && nodes.Count != 0)
{
}
if (nodes is null || nodes.Count < 0)
{
return;
}
#region
Dictionary<string, string> guids = new Dictionary<string, string>(); // 记录 Guid
// 遍历当前节点
foreach (var node in nodes.ToArray())
{
if (NodeControls.ContainsKey(node.Guid) && !guids.ContainsKey(node.Guid))
{
// 如果是没出现过、且在当前记录中重复的Guid则记录并新增对应的映射。
guids.TryAdd(node.Guid, Guid.NewGuid().ToString());
}
else
{
// 出现过的Guid说明重复添加了。应该不会走到这。
continue;
}
if (node.ChildNodeGuids is null)
{
continue; // 跳过没有子节点的节点
}
// 遍历这些节点的子节点,获得完整的已选节点信息
foreach (var childNodeGuid in node.ChildNodeGuids)
{
if (NodeControls.ContainsKey(node.Guid) && !NodeControls.ContainsKey(node.Guid))
{
// 当前Guid并不重复跳过替换
continue;
}
if (!guids.ContainsKey(childNodeGuid))
{
// 如果是没出现过的Guid则记录并新增对应的映射。
guids.TryAdd(node.Guid, Guid.NewGuid().ToString());
}
if (!string.IsNullOrEmpty(childNodeGuid)
&& NodeControls.TryGetValue(childNodeGuid, out var nodeControl))
{
var newNodeInfo = nodeControl.ViewModel.NodeModel.ToInfo();
nodes.Add(newNodeInfo);
}
}
}
// Guid去重
StringBuilder sb = new StringBuilder(jsonText);
foreach (var kv in guids)
{
sb.Replace(kv.Key, kv.Value);
}
string result = sb.ToString();
/*var replacer = new GuidReplacer();
foreach (var kv in guids)
{
replacer.AddReplacement(kv.Key, kv.Value);
}
string result = replacer.Replace(jsonText);*/
//SereinEnv.WriteLine(InfoType.ERROR, result);
nodes = JsonConvert.DeserializeObject<List<NodeInfo>>(result);
if (nodes is null || nodes.Count < 0)
{
return;
}
#endregion
// 获取第一个节点的原始位置
var index0NodeX = nodes[0].Position.X;
var index0NodeY = nodes[0].Position.Y;
// 计算所有节点相对于第一个节点的偏移量
foreach (var node in nodes)
{
node.CanvasGuid = canvasGuid; // 替换画布Guid
var offsetX = node.Position.X - index0NodeX;
var offsetY = node.Position.Y - index0NodeY;
// 根据鼠标位置平移节点
node.Position = new PositionOfUI(positionOfUI.X + offsetX, positionOfUI.Y + offsetY);
}
_ = flowEnvironment.LoadNodeInfosAsync(nodes);
}
catch (Exception ex)
{
//SereinEnv.WriteLine(InfoType.ERROR, $"粘贴节点时发生异常:{ex}");
}
// SereinEnv.WriteLine(InfoType.INFO, $"剪贴板文本内容: {clipboardText}");
}
#endregion
#region #region

View File

@@ -6,19 +6,20 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using static System.Windows.Forms.AxHost;
namespace Serein.Workbench.Services namespace Serein.Workbench.Services
{ {
delegate void KeyDownEventHandler(Key key); public delegate void KeyDownEventHandler(Key key);
delegate void KeyUpEventHandler(Key key); public delegate void KeyUpEventHandler(Key key);
/// <summary> /// <summary>
/// 全局事件服务 /// 全局按键事件服务
/// </summary> /// </summary>
internal interface IKeyEventService public interface IKeyEventService
{ {
event KeyDownEventHandler KeyDown; event KeyDownEventHandler OnKeyDown;
event KeyUpEventHandler KeyUp; event KeyUpEventHandler OnKeyUp;
/// <summary> /// <summary>
/// 获取某个按键状态 /// 获取某个按键状态
@@ -26,27 +27,34 @@ namespace Serein.Workbench.Services
/// <param name="key"></param> /// <param name="key"></param>
/// <returns></returns> /// <returns></returns>
bool GetKeyState(Key key); bool GetKeyState(Key key);
/// <summary> /// <summary>
/// 设置某个按键的状态 /// 按下了某个键
/// </summary> /// </summary>
/// <param name="key"></param> /// <param name="key"></param>
/// <param name="state"></param> void KeyDown(Key key);
void SetKeyState(Key key, bool statestate);
/// <summary>
/// 抬起了某个键
/// </summary>
/// <param name="key"></param>
void KeyUp(Key key);
} }
/// <summary> /// <summary>
/// 管理按键状态 /// 管理按键状态
/// </summary> /// </summary>
internal class KeyEventService : IKeyEventService public class KeyEventService : IKeyEventService
{ {
/// <summary> /// <summary>
/// 按键按下 /// 按键按下
/// </summary> /// </summary>
public event KeyDownEventHandler KeyDown; public event KeyDownEventHandler OnKeyDown;
/// <summary> /// <summary>
/// 按键松开 /// 按键松开
/// </summary> /// </summary>
public event KeyUpEventHandler KeyUp; public event KeyUpEventHandler OnKeyUp;
public KeyEventService() public KeyEventService()
{ {
@@ -62,18 +70,23 @@ namespace Serein.Workbench.Services
{ {
return KeysState[(int)key]; return KeysState[(int)key];
} }
public void SetKeyState(Key key, bool state)
public void KeyDown(Key key)
{ {
if (state) KeysState[(int)key] = true;
{ OnKeyDown?.Invoke(key);
KeyDown?.Invoke(key); Debug.WriteLine($"按键按下事件:{key}");
} }
else
{ public void KeyUp(Key key)
KeyUp?.Invoke(key); {
} KeysState[(int)key] = false;
//Debug.WriteLine($"按键事件:{key} - {state}"); OnKeyUp?.Invoke(key);
KeysState[(int)key] = state; Debug.WriteLine($"按键抬起事件:{key}");
} }
} }
} }

View File

@@ -1,55 +0,0 @@
using Newtonsoft.Json;
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow;
using Serein.NodeFlow.Env;
using Serein.Workbench.Api;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Node;
using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace Serein.Workbench.Services
{
/// <summary>
/// 节点操作相关服务
/// </summary>
internal class NodeControlService
{
public NodeControlService(IFlowEnvironment flowEnvironment,
IFlowEEForwardingService feefService)
{
/* this.flowEnvironment = flowEnvironment;
this.feefService = feefService;
feefService.OnNodeCreate += FeefService_OnNodeCreate; // 订阅运行环境创建节点事件
feefService.OnNodeConnectChange += FeefService_OnNodeConnectChange; // 订阅运行环境连接了节点事件
// 手动加载项目
_ = Task.Run(async delegate
{
await Task.Delay(1000);
var flowEnvironment = new FlowEnvironment();// App.GetService<IFlowEnvironment>();
var filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\project.dnf";
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
var projectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
var projectDfilePath = System.IO.Path.GetDirectoryName(filePath)!;
flowEnvironment.LoadProject(new FlowEnvInfo { Project = projectData }, projectDfilePath);
}, CancellationToken.None);*/
}
}
}

View File

@@ -7,6 +7,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates; using System.Security.Cryptography.X509Certificates;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
@@ -71,9 +72,15 @@ namespace Serein.Workbench.Services
private void InitEvents() private void InitEvents()
{ {
flowEEForwardingService.OnProjectSaving += SaveProjectToLocalFile; flowEEForwardingService.OnProjectSaving += SaveProjectToLocalFile;
flowEEForwardingService.OnEnvOut += FlowEEForwardingService_OnEnvOut;
} }
private void FlowEEForwardingService_OnEnvOut(InfoType type, string value)
{
LogWindow.Instance.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}");
}
/// <summary> /// <summary>
/// 预览了某个方法信息(待创建) /// 预览了某个方法信息(待创建)
@@ -103,6 +110,8 @@ namespace Serein.Workbench.Services
private void SaveProjectToLocalFile(ProjectSavingEventArgs e) private void SaveProjectToLocalFile(ProjectSavingEventArgs e)
{ {
var project = e.ProjectData; var project = e.ProjectData;
#region
// 创建一个新的保存文件对话框 // 创建一个新的保存文件对话框
SaveFileDialog saveFileDialog = new() SaveFileDialog saveFileDialog = new()
{ {
@@ -128,8 +137,10 @@ namespace Serein.Workbench.Services
SereinEnv.WriteLine(InfoType.ERROR, "保存项目DLL时返回了意外的文件保存路径"); SereinEnv.WriteLine(InfoType.ERROR, "保存项目DLL时返回了意外的文件保存路径");
return; return;
} }
#endregion
#region Dll输出到指定路径
Uri saveProjectFileUri = new Uri(savePath); Uri saveProjectFileUri = new Uri(savePath);
SereinEnv.WriteLine(InfoType.INFO, "项目文件保存路径:" + savePath); SereinEnv.WriteLine(InfoType.INFO, "项目文件保存路径:" + savePath);
for (int index = 0; index < project.Librarys.Length; index++) for (int index = 0; index < project.Librarys.Length; index++)
@@ -180,12 +191,104 @@ namespace Serein.Workbench.Services
} }
} }
#endregion
#region
JObject projectJsonData = JObject.FromObject(project); JObject projectJsonData = JObject.FromObject(project);
File.WriteAllText(savePath, projectJsonData.ToString()); File.WriteAllText(savePath, projectJsonData.ToString());
#endregion
} }
} }
} }
#region net运行时
/*
1. 扫描目录并计算哈希
string[] directories = new[] { "path1", "path2" };
var fileHashMap = new Dictionary<string, List<string>>(); // hash -> List<full paths>
foreach (var dir in directories)
{
foreach (var file in Directory.EnumerateFiles(dir, "*.*", SearchOption.AllDirectories))
{
using var stream = File.OpenRead(file);
using var sha = SHA256.Create();
var hash = Convert.ToHexString(sha.ComputeHash(stream));
if (!fileHashMap.ContainsKey(hash))
fileHashMap[hash] = new List<string>();
fileHashMap[hash].Add(file);
}
}
2. 将重复文件压缩并保存
string archiveDir = "compressed_output";
Directory.CreateDirectory(archiveDir);
var manifest = new List<FileRecord>();
foreach (var kvp in fileHashMap.Where(kvp => kvp.Value.Count > 1))
{
var hash = kvp.Key;
var originalFile = kvp.Value[0];
var archivePath = Path.Combine(archiveDir, $"{hash}.gz");
using (var input = File.OpenRead(originalFile))
using (var output = File.Create(archivePath))
using (var gzip = new GZipStream(output, CompressionLevel.Optimal))
{
input.CopyTo(gzip);
}
manifest.Add(new FileRecord
{
Hash = hash,
ArchiveFile = $"{hash}.gz",
OriginalPaths = kvp.Value
});
}
3. 生成清单文件JSON
public class FileRecord
{
public string Hash { get; set; }
public string ArchiveFile { get; set; }
public List<string> OriginalPaths { get; set; }
}
File.WriteAllText("manifest.json", JsonSerializer.Serialize(manifest, new JsonSerializerOptions { WriteIndented = true }));
4. 根据清单还原原始文件结构
var manifestJson = File.ReadAllText("manifest.json");
var manifest = JsonSerializer.Deserialize<List<FileRecord>>(manifestJson);
foreach (var record in manifest)
{
var archivePath = Path.Combine("compressed_output", record.ArchiveFile);
foreach (var path in record.OriginalPaths)
{
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
using var input = File.OpenRead(archivePath);
using var gzip = new GZipStream(input, CompressionMode.Decompress);
using var output = File.Create(path);
gzip.CopyTo(output);
}
}
*/
#endregion

View File

@@ -1,6 +1,7 @@
using Serein.Library; using Serein.Library;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Utils; using Serein.Library.Utils;
using Serein.Workbench.Tool;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
@@ -137,11 +138,11 @@ namespace Serein.Workbench.Themes
treeViewItem.Expanded += TreeViewItem_Expanded; treeViewItem.Expanded += TreeViewItem_Expanded;
var contextMenu = new ContextMenu(); var contextMenu = new ContextMenu();
contextMenu.Items.Add(MainWindow.CreateMenuItem("从此节点执行", async (s, e) => contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("从此节点执行", async (s, e) =>
{ {
try try
{ {
await flowEnvironment.StartAsyncInSelectNode(tmpNodeTreeModel.RootNode.Guid); await flowEnvironment.StartFlowFromSelectNodeAsync(tmpNodeTreeModel.RootNode.Guid);
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -149,7 +150,7 @@ namespace Serein.Workbench.Themes
return; return;
} }
})); }));
contextMenu.Items.Add(MainWindow.CreateMenuItem("定位", (s, e) => flowEnvironment.NodeLocated(tmpNodeTreeModel.RootNode.Guid))); contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("定位", (s, e) => flowEnvironment.NodeLocated(tmpNodeTreeModel.RootNode.Guid)));
treeViewItem.ContextMenu = contextMenu; treeViewItem.ContextMenu = contextMenu;
treeViewItem.Margin = new Thickness(-20, 0, 0, 0); treeViewItem.Margin = new Thickness(-20, 0, 0, 0);

View File

@@ -1,6 +1,7 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Library.Utils.SereinExpression; using Serein.Library.Utils.SereinExpression;
using Serein.Workbench.Tool;
using System; using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
@@ -294,7 +295,7 @@ namespace Serein.Workbench.Themes
// 配置右键菜单 // 配置右键菜单
var contextMenu = new ContextMenu(); var contextMenu = new ContextMenu();
contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) => contextMenu.Items.Add(WpfFuncTool.CreateMenuItem($"表达式", (s, e) =>
{ {
ExpressionTextBox.Text = subPath; // 获取表达式 ExpressionTextBox.Text = subPath; // 获取表达式

View File

@@ -1,4 +1,5 @@
using System; using Serein.Workbench.Tool;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
@@ -165,7 +166,7 @@ namespace Serein.Workbench.Themes
{ {
isChange = true; isChange = true;
contextMenu = new ContextMenu(); contextMenu = new ContextMenu();
contextMenu.Items.Add(MainWindow.CreateMenuItem($"取值表达式", (s, e) => contextMenu.Items.Add(WpfFuncTool.CreateMenuItem($"取值表达式", (s, e) =>
{ {
string fullPath = GetNodeFullPath(memberNode); string fullPath = GetNodeFullPath(memberNode);
string copyValue = "@Get " + fullPath; string copyValue = "@Get " + fullPath;
@@ -181,7 +182,7 @@ namespace Serein.Workbench.Themes
{ {
isChange = true; isChange = true;
contextMenu = new ContextMenu(); contextMenu = new ContextMenu();
contextMenu.Items.Add(MainWindow.CreateMenuItem($"取值表达式", (s, e) => contextMenu.Items.Add(WpfFuncTool.CreateMenuItem($"取值表达式", (s, e) =>
{ {
string fullPath = GetNodeFullPath(memberNode); string fullPath = GetNodeFullPath(memberNode);
string copyValue = "@Get " + fullPath; string copyValue = "@Get " + fullPath;

View File

@@ -0,0 +1,48 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows;
namespace Serein.Workbench.Tool
{
internal static class WpfFuncTool
{ /// <summary>
/// 创建菜单子项
/// </summary>
/// <param name="header"></param>
/// <param name="handler"></param>
/// <returns></returns>
public static MenuItem CreateMenuItem(string header, RoutedEventHandler handler)
{
var menuItem = new MenuItem { Header = header };
menuItem.Click += handler;
return menuItem;
}
/// <summary>
/// 穿透元素获取区域容器
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="element"></param>
/// <returns></returns>
public static T? GetParentOfType<T>(DependencyObject element) where T : DependencyObject
{
while (element != null)
{
if (element is T e)
{
return e;
}
element = VisualTreeHelper.GetParent(element);
}
return null;
}
}
}

View File

@@ -19,9 +19,9 @@ namespace Serein.Workbench.ViewModels
{ {
/// <summary> /// <summary>
/// 画布当前选中的节点 /// 画布当前的节点
/// </summary> /// </summary>
public NodeControlBase CurrentSelectNode { get; set; } public Dictionary<string, NodeControlBase> NodeControls { get; set; } = [];
/// <summary> /// <summary>
/// 正在创建节点方法调用关系 /// 正在创建节点方法调用关系

View File

@@ -23,9 +23,6 @@ namespace Serein.Workbench.ViewModels
public partial class FlowEditViewModel : ObservableObject public partial class FlowEditViewModel : ObservableObject
{ {
public ObservableCollection<FlowEditorTabModel> CanvasTabs { get; set; } = []; public ObservableCollection<FlowEditorTabModel> CanvasTabs { get; set; } = [];
public ICommand AddTabCommand { get; set; }
public ICommand RemoveTabCommand { get; set; }
public ICommand RenameTabCommand { get; set; }
/// <summary> /// <summary>
@@ -39,8 +36,7 @@ namespace Serein.Workbench.ViewModels
public FlowEditViewModel(FlowNodeService flowNodeService) public FlowEditViewModel(FlowNodeService flowNodeService)
{ {
this.flowNodeService = flowNodeService; this.flowNodeService = flowNodeService;
AddTabCommand = new RelayCommand(AddTab);
RemoveTabCommand = new RelayCommand(RemoveTab);
flowNodeService.OnCreateFlowCanvasView += OnCreateFlowCanvasView; // 创建了画布 flowNodeService.OnCreateFlowCanvasView += OnCreateFlowCanvasView; // 创建了画布
flowNodeService.OnRemoveFlowCanvasView += OnRemoveFlowCanvasView; // 移除了画布 flowNodeService.OnRemoveFlowCanvasView += OnRemoveFlowCanvasView; // 移除了画布
@@ -81,13 +77,6 @@ namespace Serein.Workbench.ViewModels
#endregion #endregion
private void AddTab() => flowNodeService.CreateFlowCanvas();
private void RemoveTab()
{
if (CanvasTabs.Count > 0 && SelectedTab != null) flowNodeService.RemoveFlowCanvas();
}
/// <summary> /// <summary>

View File

@@ -1,5 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library; using Serein.Library;
using Serein.Library.Api;
using Serein.NodeFlow.Env;
using Serein.Workbench.Api; using Serein.Workbench.Api;
using Serein.Workbench.Models; using Serein.Workbench.Models;
using Serein.Workbench.Services; using Serein.Workbench.Services;
@@ -15,16 +17,33 @@ namespace Serein.Workbench.ViewModels
internal partial class FlowLibrarysViewModel : ObservableObject internal partial class FlowLibrarysViewModel : ObservableObject
{ {
private readonly IFlowEEForwardingService flowEEForwardingService; private readonly IFlowEEForwardingService flowEEForwardingService;
private readonly IFlowEnvironment flowEnvironment;
[ObservableProperty] [ObservableProperty]
private ObservableCollection<FlowLibraryInfo> flowLibraryInfos; private ObservableCollection<FlowLibraryInfo> flowLibraryInfos;
public FlowLibrarysViewModel(IFlowEEForwardingService flowEEForwardingService) public FlowLibrarysViewModel(IFlowEEForwardingService flowEEForwardingService,IFlowEnvironment flowEnvironment)
{ {
this.flowEEForwardingService = flowEEForwardingService; this.flowEEForwardingService = flowEEForwardingService;
this.flowEnvironment = flowEnvironment;
FlowLibraryInfos = new ObservableCollection<FlowLibraryInfo>(); FlowLibraryInfos = new ObservableCollection<FlowLibraryInfo>();
flowEEForwardingService.OnDllLoad += FlowEEForwardingService_OnDllLoad; flowEEForwardingService.OnDllLoad += FlowEEForwardingService_OnDllLoad;
} }
/// <summary>
/// 加载文件依赖
/// </summary>
/// <param name="filePath"></param>
public void LoadFileLibrary(string filePath)
{
try
{
flowEnvironment.LoadLibrary(filePath);
}
catch (Exception ex)
{
flowEnvironment.WriteLine(Library.InfoType.ERROR, ex.ToString());
return;
}
}
private void FlowEEForwardingService_OnDllLoad(Library.Api.LoadDllEventArgs eventArgs) private void FlowEEForwardingService_OnDllLoad(Library.Api.LoadDllEventArgs eventArgs)
{ {
@@ -32,10 +51,10 @@ namespace Serein.Workbench.ViewModels
List<MethodDetailsInfo> mds = eventArgs.MethodDetailss; List<MethodDetailsInfo> mds = eventArgs.MethodDetailss;
NodeLibraryInfo libraryInfo = eventArgs.NodeLibraryInfo; NodeLibraryInfo libraryInfo = eventArgs.NodeLibraryInfo;
var methodInfo = new ObservableCollection<FlowLibraryMethodDetailsInfo>(); var methodInfo = new ObservableCollection<MethodDetailsInfo>();
foreach (var md in mds) foreach (var md in mds)
{ {
methodInfo.Add(new FlowLibraryMethodDetailsInfo(md)); methodInfo.Add(md);
} }
var flInfo = new FlowLibraryInfo var flInfo = new FlowLibraryInfo
{ {

View File

@@ -1,4 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library.Api;
using Serein.NodeFlow.Env;
using Serein.Workbench.Api; using Serein.Workbench.Api;
using Serein.Workbench.Models; using Serein.Workbench.Models;
using Serein.Workbench.Services; using Serein.Workbench.Services;
@@ -8,19 +10,36 @@ using System.Collections.ObjectModel;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;
namespace Serein.Workbench.ViewModels namespace Serein.Workbench.ViewModels
{ {
internal partial class FlowWorkbenchViewModel : ObservableObject internal partial class FlowWorkbenchViewModel : ObservableObject
{ {
private readonly IFlowEEForwardingService flowEEForwardingService; private readonly IFlowEnvironment flowEnvironment;
private readonly IWorkbenchEventService workbenchEventService; private readonly IWorkbenchEventService workbenchEventService;
private readonly IKeyEventService keyEventService;
public FlowWorkbenchViewModel(IFlowEEForwardingService flowEEForwardingService, IWorkbenchEventService workbenchEventService) public FlowWorkbenchViewModel(IFlowEnvironment flowEnvironment,
IWorkbenchEventService workbenchEventService,
IKeyEventService keyEventService)
{ {
this.flowEEForwardingService = flowEEForwardingService; this.flowEnvironment = flowEnvironment;
this.workbenchEventService = workbenchEventService; this.workbenchEventService = workbenchEventService;
//flowEEForwardingService.OnDllLoad += FlowEEForwardingService_OnDllLoad; this.keyEventService = keyEventService;
EventManager.RegisterClassHandler(typeof(Window), Keyboard.KeyDownEvent, new KeyEventHandler(OnKeyDown)); // 按下事件
EventManager.RegisterClassHandler(typeof(Window), Keyboard.KeyUpEvent, new KeyEventHandler(OnKeyUp)); // 松开事件
} }
private void OnKeyDown(object sender, KeyEventArgs e)
{
keyEventService.KeyDown(e.Key);
}
private void OnKeyUp(object sender, KeyEventArgs e)
{
keyEventService.KeyUp(e.Key);
}
} }
} }

View File

@@ -1,6 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.Workbench.Services;
using System.Windows.Input; using System.Windows.Input;
namespace Serein.Workbench.ViewModels namespace Serein.Workbench.ViewModels
@@ -8,6 +9,7 @@ namespace Serein.Workbench.ViewModels
public class MainMenuBarViewModel : ObservableObject public class MainMenuBarViewModel : ObservableObject
{ {
private readonly IFlowEnvironment environment; private readonly IFlowEnvironment environment;
private readonly FlowNodeService flowNodeService;
/// <summary> /// <summary>
/// 保存项目 /// 保存项目
@@ -31,6 +33,10 @@ namespace Serein.Workbench.ViewModels
/// </summary> /// </summary>
public ICommand RemoteFlowCanvasCommand { get; private set; } public ICommand RemoteFlowCanvasCommand { get; private set; }
/// <summary>
/// 运行当前画布流程
/// </summary>
public ICommand StartFlowCommand { get; private set; }
/// <summary> /// <summary>
/// 运行当前画布流程 /// 运行当前画布流程
/// </summary> /// </summary>
@@ -55,10 +61,10 @@ namespace Serein.Workbench.ViewModels
public MainMenuBarViewModel(IFlowEnvironment environment) public MainMenuBarViewModel(IFlowEnvironment environment, FlowNodeService flowNodeService)
{ {
this.environment = environment; this.environment = environment;
this.flowNodeService = flowNodeService;
SaveProjectCommand = new RelayCommand(SaveProject); // 保存项目 SaveProjectCommand = new RelayCommand(SaveProject); // 保存项目
LoadLocalProjectCommand = new RelayCommand(LoadLocalProject); // 加载本地项目 LoadLocalProjectCommand = new RelayCommand(LoadLocalProject); // 加载本地项目
LoadRemoteProjectCommand = new RelayCommand(LoadRemoteProject); // 加载远程项目 LoadRemoteProjectCommand = new RelayCommand(LoadRemoteProject); // 加载远程项目
@@ -66,26 +72,30 @@ namespace Serein.Workbench.ViewModels
CreateFlowCanvasCommand = new RelayCommand(CreateFlowCanvas); // 增加画布 CreateFlowCanvasCommand = new RelayCommand(CreateFlowCanvas); // 增加画布
RemoteFlowCanvasCommand = new RelayCommand(RemoteFlowCanvas); // 移除画布 RemoteFlowCanvasCommand = new RelayCommand(RemoteFlowCanvas); // 移除画布
StartCurrentCanvasFlowCommand = new RelayCommand(StartCurrentCanvasFlow); // 运行当前流程 StartFlowCommand = new RelayCommand(StartFlow);
StartCurrentCanvasFlowCommand = new RelayCommand(StartCurrentCanvasFlow); // 运行当前所查看画布的流程
StopCurrentCanvasFlowCommand = new RelayCommand(StopCurrentCanvasFlow); // 停止当前流程 StopCurrentCanvasFlowCommand = new RelayCommand(StopCurrentCanvasFlow); // 停止当前流程
OpenEnvOutWindowCommand = new RelayCommand(OpenEnvOutWindow); // 打开运行输出窗口 OpenEnvOutWindowCommand = new RelayCommand(OpenEnvOutWindow); // 打开运行输出窗口
OpenDynamicCompilerCommand = new RelayCommand(OpenDynamicCompiler); // 打开动态编译仓库窗口 OpenDynamicCompilerCommand = new RelayCommand(OpenDynamicCompiler); // 打开动态编译仓库窗口
} }
private void SaveProject() { private void SaveProject() => environment.SaveProject(); // 保存项目
environment.SaveProject(); // 保存项目
}
private void LoadLocalProject() { private void LoadLocalProject() {
//environment.LoadProject(); // 加载项目 //environment.LoadProject(); // 加载项目
} }
private void LoadRemoteProject() { } private void LoadRemoteProject()
private void CreateFlowCanvas() { } {
private void RemoteFlowCanvas() { } }
private void StartCurrentCanvasFlow() { } private void CreateFlowCanvas() => flowNodeService.CreateFlowCanvas();
private void RemoteFlowCanvas() => flowNodeService.RemoveFlowCanvas();
private void StartFlow() => environment.StartFlowAsync([.. flowNodeService.FlowCanvass.Select(c => c.Guid)]);
private void StartCurrentCanvasFlow() => environment.StartFlowAsync([flowNodeService.CurrentSelectCanvas.Guid]);
private void StopCurrentCanvasFlow() { } private void StopCurrentCanvasFlow() { }
private void OpenDynamicCompiler() { } private void OpenDynamicCompiler() { }
private void OpenEnvOutWindow() { } private void OpenEnvOutWindow() => LogWindow.Instance?.Show();
} }
} }

View File

@@ -1,17 +1,25 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Workbench.Services;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input;
using System.Windows;
namespace Serein.Workbench.ViewModels namespace Serein.Workbench.ViewModels
{ {
public class MainViewModel : ObservableObject public class MainViewModel : ObservableObject
{ {
public MainViewModel() private readonly IKeyEventService keyEventService;
public MainViewModel(IKeyEventService keyEventService)
{ {
this.keyEventService = keyEventService;
} }
} }
} }

View File

@@ -1,4 +1,5 @@
using Serein.Library; using Serein.Library;
using Serein.Workbench.Customs;
using Serein.Workbench.ViewModels; using Serein.Workbench.ViewModels;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;

View File

@@ -23,7 +23,7 @@
<Canvas ClipToBounds="True" <Canvas ClipToBounds="True"
x:Name="FlowChartCanvas" x:Name="FlowChartCanvas"
Background="#E1FBEA" Background="#FFFFFF"
AllowDrop="True" AllowDrop="True"
Width="{Binding Model.Width, Mode=TwoWay}" Width="{Binding Model.Width, Mode=TwoWay}"
Height="{Binding Model.Height, Mode=TwoWay}" Height="{Binding Model.Height, Mode=TwoWay}"

View File

@@ -1,14 +1,21 @@
using Serein.Library; using Newtonsoft.Json.Linq;
using Newtonsoft.Json;
using Serein.Library;
using Serein.Library.Api; using Serein.Library.Api;
using Serein.NodeFlow.Env; using Serein.NodeFlow.Env;
using Serein.Workbench.Api; using Serein.Workbench.Api;
using Serein.Workbench.Customs;
using Serein.Workbench.Extension;
using Serein.Workbench.Models;
using Serein.Workbench.Node; using Serein.Workbench.Node;
using Serein.Workbench.Node.View; using Serein.Workbench.Node.View;
using Serein.Workbench.Services; using Serein.Workbench.Services;
using Serein.Workbench.Themes; using Serein.Workbench.Themes;
using Serein.Workbench.Tool;
using Serein.Workbench.ViewModels; using Serein.Workbench.ViewModels;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Tracing; using System.Diagnostics.Tracing;
using System.Drawing.Printing; using System.Drawing.Printing;
using System.Linq; using System.Linq;
@@ -19,13 +26,20 @@ using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
using System.Windows.Data; using System.Windows.Data;
using System.Windows.Documents; using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input; using System.Windows.Input;
using System.Windows.Media; using System.Windows.Media;
using System.Windows.Media.Imaging; using System.Windows.Media.Imaging;
using System.Windows.Media.Media3D; using System.Windows.Media.Media3D;
using System.Windows.Navigation; using System.Windows.Navigation;
using System.Windows.Shapes; using System.Windows.Shapes;
using static Serein.Workbench.MainWindow; using Binding = System.Windows.Data.Binding;
using DragDropEffects = System.Windows.DragDropEffects;
using DragEventArgs = System.Windows.DragEventArgs;
using MouseEventArgs = System.Windows.Input.MouseEventArgs;
using UserControl = System.Windows.Controls.UserControl;
using Clipboard = System.Windows.Clipboard;
using TextDataFormat = System.Windows.TextDataFormat;
namespace Serein.Workbench.Views namespace Serein.Workbench.Views
{ {
@@ -34,9 +48,11 @@ namespace Serein.Workbench.Views
/// </summary> /// </summary>
public partial class FlowCanvasView : UserControl, IFlowCanvas public partial class FlowCanvasView : UserControl, IFlowCanvas
{ {
private readonly IFlowEnvironment flowEnvironment; private readonly IFlowEnvironment flowEnvironment;
private readonly IKeyEventService keyEventService;
private readonly FlowNodeService flowNodeService; private readonly FlowNodeService flowNodeService;
/// <summary> /// <summary>
/// 存储所有的连接。考虑集成在运行环境中。 /// 存储所有的连接。考虑集成在运行环境中。
/// </summary> /// </summary>
@@ -44,7 +60,6 @@ namespace Serein.Workbench.Views
private FlowCanvasViewModel ViewModel => this.DataContext as FlowCanvasViewModel ?? throw new ArgumentNullException(); private FlowCanvasViewModel ViewModel => this.DataContext as FlowCanvasViewModel ?? throw new ArgumentNullException();
#region #region
private IFlowCanvas Api => this; private IFlowCanvas Api => this;
@@ -57,9 +72,10 @@ namespace Serein.Workbench.Views
} }
public string Name => ViewModel.Model.Name; public string Name => ViewModel.Model.Name;
FlowCanvasDetails IFlowCanvas.Model => ViewModel.Model;
void IFlowCanvas.Remove(NodeControlBase nodeControl) void IFlowCanvas.Remove(NodeControlBase nodeControl)
{ {
ViewModel.NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid);
FlowChartCanvas.Dispatcher.Invoke(() => FlowChartCanvas.Dispatcher.Invoke(() =>
{ {
FlowChartCanvas.Children.Remove(nodeControl); FlowChartCanvas.Children.Remove(nodeControl);
@@ -67,11 +83,15 @@ namespace Serein.Workbench.Views
} }
void IFlowCanvas.Add(NodeControlBase nodeControl) void IFlowCanvas.Add(NodeControlBase nodeControl)
{ {
ViewModel.NodeControls.TryAdd(nodeControl.ViewModel.NodeModel.Guid, nodeControl);
FlowChartCanvas.Dispatcher.Invoke(() => FlowChartCanvas.Dispatcher.Invoke(() =>
{ {
FlowChartCanvas.Children.Add(nodeControl); FlowChartCanvas.Children.Add(nodeControl);
}); });
ConfigureNodeEvents(nodeControl); // 配置相关事件
ConfigureContextMenu(nodeControl); // 添加右键菜单
} }
void IFlowCanvas.CreateInvokeConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, ConnectionInvokeType type) void IFlowCanvas.CreateInvokeConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, ConnectionInvokeType type)
@@ -224,6 +244,9 @@ namespace Serein.Workbench.Views
/// 标记是否正在拖动画布 /// 标记是否正在拖动画布
/// </summary> /// </summary>
private bool IsCanvasDragging; private bool IsCanvasDragging;
/// <summary>
/// 是否正在选取控件
/// </summary>
private bool IsSelectDragging; private bool IsSelectDragging;
/// <summary> /// <summary>
@@ -258,8 +281,7 @@ namespace Serein.Workbench.Views
private readonly TranslateTransform translateTransform; private readonly TranslateTransform translateTransform;
#endregion #endregion
#region
#region
public FlowCanvasView(FlowCanvasDetails model) public FlowCanvasView(FlowCanvasDetails model)
{ {
@@ -270,9 +292,9 @@ namespace Serein.Workbench.Views
flowEnvironment = App.GetService<IFlowEnvironment>(); flowEnvironment = App.GetService<IFlowEnvironment>();
flowNodeService = App.GetService<FlowNodeService>(); flowNodeService = App.GetService<FlowNodeService>();
keyEventService = App.GetService<IKeyEventService>();
flowNodeService.OnCreateNode += OnCreateNode; flowNodeService.OnCreateNode += OnCreateNode;
keyEventService.OnKeyDown += KeyEventService_OnKeyDown; ;
// 缩放平移容器 // 缩放平移容器
canvasTransformGroup = new TransformGroup(); canvasTransformGroup = new TransformGroup();
@@ -286,6 +308,10 @@ namespace Serein.Workbench.Views
} }
/// <summary>
/// 设置绑定
/// </summary>
/// <param name="canvasModel"></param>
private void SetBinding(FlowCanvasDetails canvasModel) private void SetBinding(FlowCanvasDetails canvasModel)
{ {
Binding bindingScaleX = new(nameof(canvasModel.ScaleX)) { Source = canvasModel, Mode = BindingMode.TwoWay }; Binding bindingScaleX = new(nameof(canvasModel.ScaleX)) { Source = canvasModel, Mode = BindingMode.TwoWay };
@@ -325,8 +351,7 @@ namespace Serein.Workbench.Views
{ {
// 并非添加在容器中,直接放置节点 // 并非添加在容器中,直接放置节点
Api.Add(nodeControl); // 添加到对应的画布上 Api.Add(nodeControl); // 添加到对应的画布上
ConfigureNodeEvents(nodeControl); // 添加了节点
ConfigureContextMenu(nodeControl); // 添加右键菜单
} }
} }
@@ -349,7 +374,7 @@ namespace Serein.Workbench.Views
// 准备放置条件表达式控件 // 准备放置条件表达式控件
if (nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.ExpCondition) if (nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.ExpCondition)
{ {
ConditionRegionControl? conditionRegion = GetParentOfType<ConditionRegionControl>(hitElement); ConditionRegionControl? conditionRegion = WpfFuncTool.GetParentOfType<ConditionRegionControl>(hitElement);
if (conditionRegion is not null) if (conditionRegion is not null)
{ {
targetNodeControl = conditionRegion; targetNodeControl = conditionRegion;
@@ -362,7 +387,7 @@ namespace Serein.Workbench.Views
else else
{ {
// 准备放置全局数据控件 // 准备放置全局数据控件
GlobalDataControl? globalDataControl = GetParentOfType<GlobalDataControl>(hitElement); GlobalDataControl? globalDataControl = WpfFuncTool.GetParentOfType<GlobalDataControl>(hitElement);
if (globalDataControl is not null) if (globalDataControl is not null)
{ {
targetNodeControl = globalDataControl; targetNodeControl = globalDataControl;
@@ -376,6 +401,107 @@ namespace Serein.Workbench.Views
#endregion #endregion
#region
/// <summary>
/// 监听按键事件
/// </summary>
/// <param name="key"></param>
private void KeyEventService_OnKeyDown(Key key)
{
if (!flowNodeService.CurrentSelectCanvas.Guid.Equals(Guid))
{
return;
}
if (key == Key.Escape)
{
IsControlDragging = false;
IsCanvasDragging = false;
SelectionRectangle.Visibility = Visibility.Collapsed;
CancelSelectNode();
EndConnection();
return;
}
// 复制节点
if (selectNodeControls.Count > 0 && key == Key.C && (keyEventService.GetKeyState(Key.LeftCtrl) || keyEventService.GetKeyState(Key.RightCtrl)))
{
var text = flowNodeService.CpoyNodeInfo([.. selectNodeControls.Select(c => c.ViewModel.NodeModel)]);
Clipboard.SetDataObject(text, true); // 复制,持久性设置
return;
}
// 粘贴节点
if (key == Key.V && (keyEventService.GetKeyState(Key.LeftCtrl) || keyEventService.GetKeyState(Key.RightCtrl)))
{
string clipboardText = Clipboard.GetText(TextDataFormat.Text); // 获取复制的文本
var jobject = JObject.Parse(clipboardText);
var nodesText = jobject["nodes"]?.ToString();
if (!string.IsNullOrWhiteSpace(nodesText))
{
if (Clipboard.ContainsText())
{
Point mousePosition = Mouse.GetPosition(FlowChartCanvas);
PositionOfUI positionOfUI = new PositionOfUI(mousePosition.X, mousePosition.Y); // 坐标数据
flowNodeService.PasteNodeInfo(Guid, nodesText, positionOfUI); // 粘贴节点信息
}
else if (Clipboard.ContainsImage())
{
// var image = Clipboard.GetImage();
}
else
{
SereinEnv.WriteLine(InfoType.INFO, "剪贴板中没有可识别的数据。");
}
}
return;
}
var cd = flowNodeService.ConnectingData;
if (cd.IsCreateing)
{
if (cd.Type == JunctionOfConnectionType.Invoke)
{
ConnectionInvokeType connectionInvokeType = key switch
{
Key.D1 => ConnectionInvokeType.Upstream,
Key.D2 => ConnectionInvokeType.IsSucceed,
Key.D3 => ConnectionInvokeType.IsFail,
Key.D4 => ConnectionInvokeType.IsError,
_ => ConnectionInvokeType.None,
};
if (connectionInvokeType != ConnectionInvokeType.None)
{
cd.ConnectionInvokeType = connectionInvokeType;
cd.MyLine.Line.UpdateLineColor(connectionInvokeType.ToLineColor());
}
}
else if (cd.Type == JunctionOfConnectionType.Arg)
{
ConnectionArgSourceType connectionArgSourceType = key switch
{
Key.D1 => ConnectionArgSourceType.GetOtherNodeData,
Key.D2 => ConnectionArgSourceType.GetOtherNodeDataOfInvoke,
_ => ConnectionArgSourceType.GetPreviousNodeData,
};
if (connectionArgSourceType != ConnectionArgSourceType.GetPreviousNodeData)
{
cd.ConnectionArgSourceType = connectionArgSourceType;
cd.MyLine.Line.UpdateLineColor(connectionArgSourceType.ToLineColor());
}
}
cd.CurrentJunction.InvalidateVisual(); // 刷新目标节点控制点样式
}
}
#endregion
#region
/// <summary> /// <summary>
/// 鼠标在画布移动。 /// 鼠标在画布移动。
@@ -385,11 +511,11 @@ namespace Serein.Workbench.Views
/// </summary> /// </summary>
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e) private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
{ {
var myData = GlobalJunctionData.MyGlobalConnectingData; var cd = flowNodeService.ConnectingData;
if (myData.IsCreateing && e.LeftButton == MouseButtonState.Pressed) if (cd.IsCreateing && e.LeftButton == MouseButtonState.Pressed)
{ {
if (myData.Type == JunctionOfConnectionType.Invoke) if (cd.Type == JunctionOfConnectionType.Invoke)
{ {
ViewModel.IsConnectionInvokeNode = true; // 正在连接节点的调用关系 ViewModel.IsConnectionInvokeNode = true; // 正在连接节点的调用关系
@@ -401,7 +527,7 @@ namespace Serein.Workbench.Views
var currentPoint = e.GetPosition(FlowChartCanvas); var currentPoint = e.GetPosition(FlowChartCanvas);
currentPoint.X -= 2; currentPoint.X -= 2;
currentPoint.Y -= 2; currentPoint.Y -= 2;
myData.UpdatePoint(currentPoint); cd.UpdatePoint(currentPoint);
return; return;
} }
@@ -456,10 +582,10 @@ namespace Serein.Workbench.Views
PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y); PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y);
if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas)) if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas))
{ {
if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeData nodeData) if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeModel nodeModel)
{ {
flowNodeService.CurrentNodeControlType = nodeData.NodeControlType; // 设置基础节点类型 flowNodeService.CurrentNodeControlType = nodeModel.NodeControlType; // 设置基础节点类型
flowNodeService.CurrentDragMdInfo = nodeData.MethodDetailsInfo; // 基础节点不需要参数信息 flowNodeService.CurrentDragMdInfo = nodeModel.MethodDetailsInfo; // 基础节点不需要参数信息
flowNodeService.CurrentMouseLocation = position; // 设置当前鼠标为止 flowNodeService.CurrentMouseLocation = position; // 设置当前鼠标为止
flowNodeService.CreateNode(); // 创建来自DLL加载的方法节点 flowNodeService.CreateNode(); // 创建来自DLL加载的方法节点
} }
@@ -524,7 +650,8 @@ namespace Serein.Workbench.Views
/// <param name="e"></param> /// <param name="e"></param>
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{ {
if (GlobalJunctionData.MyGlobalConnectingData.IsCreateing)
if (flowNodeService.ConnectingData.IsCreateing)
{ {
return; return;
} }
@@ -580,42 +707,42 @@ namespace Serein.Workbench.Views
} }
// 创建连线 // 创建连线
if (GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing) var cd = flowNodeService.ConnectingData;
if (cd.IsCreateing)
{ {
if (cd.IsCanConnected)
if (myData.IsCanConnected)
{ {
var canvas = this.FlowChartCanvas; var canvas = this.FlowChartCanvas;
var currentendPoint = e.GetPosition(canvas); // 当前鼠标落点 var currentendPoint = e.GetPosition(canvas); // 当前鼠标落点
var changingJunctionPosition = myData.CurrentJunction.TranslatePoint(new Point(0, 0), canvas); var changingJunctionPosition = cd.CurrentJunction.TranslatePoint(new Point(0, 0), canvas);
var changingJunctionRect = new Rect(changingJunctionPosition, new Size(myData.CurrentJunction.Width, myData.CurrentJunction.Height)); var changingJunctionRect = new Rect(changingJunctionPosition, new Size(cd.CurrentJunction.Width, cd.CurrentJunction.Height));
if (changingJunctionRect.Contains(currentendPoint)) // 可以创建连接 if (changingJunctionRect.Contains(currentendPoint)) // 可以创建连接
{ {
#region #region
if (myData.Type == JunctionOfConnectionType.Invoke) if (cd.Type == JunctionOfConnectionType.Invoke)
{ {
var canvasGuid = this.Guid; var canvasGuid = this.Guid;
await flowEnvironment.ConnectInvokeNodeAsync( await flowEnvironment.ConnectInvokeNodeAsync(
canvasGuid, canvasGuid,
myData.StartJunction.MyNode.Guid, cd.StartJunction.MyNode.Guid,
myData.CurrentJunction.MyNode.Guid, cd.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType, cd.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType, cd.CurrentJunction.JunctionType,
myData.ConnectionInvokeType); cd.ConnectionInvokeType);
} }
#endregion #endregion
#region #region
else if (myData.Type == JunctionOfConnectionType.Arg) else if (cd.Type == JunctionOfConnectionType.Arg)
{ {
var argIndex = 0; var argIndex = 0;
if (myData.StartJunction is ArgJunctionControl argJunction1) if (cd.StartJunction is ArgJunctionControl argJunction1)
{ {
argIndex = argJunction1.ArgIndex; argIndex = argJunction1.ArgIndex;
} }
else if (myData.CurrentJunction is ArgJunctionControl argJunction2) else if (cd.CurrentJunction is ArgJunctionControl argJunction2)
{ {
argIndex = argJunction2.ArgIndex; argIndex = argJunction2.ArgIndex;
} }
@@ -623,11 +750,11 @@ namespace Serein.Workbench.Views
await flowEnvironment.ConnectArgSourceNodeAsync( await flowEnvironment.ConnectArgSourceNodeAsync(
canvasGuid, canvasGuid,
myData.StartJunction.MyNode.Guid, cd.StartJunction.MyNode.Guid,
myData.CurrentJunction.MyNode.Guid, cd.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType, cd.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType, cd.CurrentJunction.JunctionType,
myData.ConnectionArgSourceType, cd.ConnectionArgSourceType,
argIndex); argIndex);
} }
#endregion #endregion
@@ -640,7 +767,14 @@ namespace Serein.Workbench.Views
} }
#region #region
/// <summary>
/// 开始拖动画布
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e) private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{ {
IsCanvasDragging = true; IsCanvasDragging = true;
@@ -649,11 +783,13 @@ namespace Serein.Workbench.Views
e.Handled = true; // 防止事件传播影响其他控件 e.Handled = true; // 防止事件传播影响其他控件
} }
/// <summary>
/// 停止拖动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void FlowChartCanvas_MouseUp(object sender, MouseButtonEventArgs e) private void FlowChartCanvas_MouseUp(object sender, MouseButtonEventArgs e)
{ {
if (IsCanvasDragging) if (IsCanvasDragging)
{ {
IsCanvasDragging = false; IsCanvasDragging = false;
@@ -661,7 +797,11 @@ namespace Serein.Workbench.Views
} }
} }
// 单纯缩放画布,不改变画布大小 /// <summary>
/// 单纯缩放画布,不改变画布大小
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void FlowChartCanvas_MouseWheel(object sender, MouseWheelEventArgs e) private void FlowChartCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
{ {
// if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) // if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
@@ -820,6 +960,9 @@ namespace Serein.Workbench.Views
#endregion #endregion
#endregion #endregion
#endregion
#region
/// <summary> /// <summary>
/// 完成选取操作 /// 完成选取操作
@@ -887,6 +1030,27 @@ namespace Serein.Workbench.Views
} }
} }
private void CancelSelectNode()
{
IsSelectControl = false;
foreach (var nodeControl in selectNodeControls)
{
//nodeControl.ViewModel.IsSelect = false;
nodeControl.BorderBrush = Brushes.Black;
nodeControl.BorderThickness = new Thickness(0);
var startNodeGuid = ViewModel.Model.StartNode;
var nodeGuid = nodeControl.ViewModel.NodeModel.Guid;
if (startNodeGuid.Equals(nodeGuid))
{
nodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"));
nodeControl.BorderThickness = new Thickness(2);
}
}
selectNodeControls.Clear();
}
/// <summary> /// <summary>
/// 结束连接操作,清理状态并移除虚线。 /// 结束连接操作,清理状态并移除虚线。
/// </summary> /// </summary>
@@ -895,21 +1059,9 @@ namespace Serein.Workbench.Views
Mouse.OverrideCursor = null; // 恢复视觉效果 Mouse.OverrideCursor = null; // 恢复视觉效果
ViewModel.IsConnectionArgSourceNode = false; ViewModel.IsConnectionArgSourceNode = false;
ViewModel.IsConnectionInvokeNode = false; ViewModel.IsConnectionInvokeNode = false;
GlobalJunctionData.OK(); flowNodeService.ConnectingData.Reset();
} }
/// <summary>
/// 创建菜单子项
/// </summary>
/// <param name="header"></param>
/// <param name="handler"></param>
/// <returns></returns>
public static MenuItem CreateMenuItem(string header, RoutedEventHandler handler)
{
var menuItem = new MenuItem { Header = header };
menuItem.Click += handler;
return menuItem;
}
/// <summary> /// <summary>
/// 选择范围配置 /// 选择范围配置
@@ -918,7 +1070,7 @@ namespace Serein.Workbench.Views
private ContextMenu ConfiguerSelectionRectangle() private ContextMenu ConfiguerSelectionRectangle()
{ {
var contextMenu = new ContextMenu(); var contextMenu = new ContextMenu();
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除", (s, e) =>
{ {
if (selectNodeControls.Count > 0) if (selectNodeControls.Count > 0)
{ {
@@ -938,6 +1090,10 @@ namespace Serein.Workbench.Views
// nodeControl.ContextMenu = contextMenu; // nodeControl.ContextMenu = contextMenu;
} }
#endregion
#region #region
/// <summary> /// <summary>
/// 配置节点事件(移动,点击相关) /// 配置节点事件(移动,点击相关)
@@ -950,13 +1106,7 @@ namespace Serein.Workbench.Views
nodeControl.MouseMove += Block_MouseMove; nodeControl.MouseMove += Block_MouseMove;
nodeControl.MouseLeftButtonUp += Block_MouseLeftButtonUp; nodeControl.MouseLeftButtonUp += Block_MouseLeftButtonUp;
}
private void EmptyNodeEvents(NodeControlBase nodeControl)
{
nodeControl.MouseLeftButtonDown -= Block_MouseLeftButtonDown;
nodeControl.MouseMove -= Block_MouseMove;
nodeControl.MouseLeftButtonUp -= Block_MouseLeftButtonUp;
} }
@@ -972,8 +1122,8 @@ namespace Serein.Workbench.Views
IsControlDragging = true; IsControlDragging = true;
startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置 startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置
((UIElement)sender).CaptureMouse(); // 捕获鼠标 ((UIElement)sender).CaptureMouse(); // 捕获鼠标
e.Handled = true; // 防止事件传播影响其他控件
} }
e.Handled = true; // 防止事件传播影响其他控件
} }
/// <summary> /// <summary>
@@ -981,12 +1131,13 @@ namespace Serein.Workbench.Views
/// </summary> /// </summary>
private void Block_MouseMove(object sender, MouseEventArgs e) private void Block_MouseMove(object sender, MouseEventArgs e)
{ {
if (IsCanvasDragging) if (IsCanvasDragging)
return; return;
if (IsSelectControl) if (IsSelectControl)
return; return;
if (IsControlDragging) // 如果正在拖动控件 if (IsControlDragging && !flowNodeService.ConnectingData.IsCreateing) // 如果正在拖动控件
{ {
Point currentPosition = e.GetPosition(FlowChartCanvas); // 获取当前鼠标位置 Point currentPosition = e.GetPosition(FlowChartCanvas); // 获取当前鼠标位置
@@ -1005,21 +1156,26 @@ namespace Serein.Workbench.Views
var newLeft = oldLeft + deltaX; var newLeft = oldLeft + deltaX;
var newTop = oldTop + deltaY; var newTop = oldTop + deltaY;
this.flowEnvironment.MoveNode(Guid, nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
// 计算控件实际移动的距离 // 计算控件实际移动的距离
var actualDeltaX = newLeft - oldLeft; var actualDeltaX = newLeft - oldLeft;
var actualDeltaY = newTop - oldTop; var actualDeltaY = newTop - oldTop;
// 移动其它选中的控件 List<(string Guid, double NewLeft, double NewTop, double MaxWidth, double MaxHeight)> moveSizes =
foreach (var nodeControl in selectNodeControls) selectNodeControls.Select(control =>
(control.ViewModel.NodeModel.Guid,
Canvas.GetLeft(control) + actualDeltaX,
Canvas.GetTop(control) + actualDeltaY,
control.FlowCanvas.Model.Width - control.ActualWidth - 10,
control.FlowCanvas.Model.Height - control.ActualHeight - 10)).ToList();
var isNeedCancel = moveSizes.Exists(item => item.NewLeft < 5 || item.NewTop < 5|| item.NewLeft > item.MaxWidth || item.NewTop > item.MaxHeight);
if (isNeedCancel)
{ {
if (nodeControl != nodeControlMain) // 跳过已经移动的控件 return;
{ }
var otherNewLeft = Canvas.GetLeft(nodeControl) + actualDeltaX; foreach (var item in moveSizes)
var otherNewTop = Canvas.GetTop(nodeControl) + actualDeltaY; {
this.flowEnvironment.MoveNode(Guid, nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点 this.flowEnvironment.MoveNode(this.Guid, item.Guid, item.NewLeft, item.NewTop); // 移动节点
}
} }
// 更新节点之间线的连接位置 // 更新节点之间线的连接位置
@@ -1038,6 +1194,14 @@ namespace Serein.Workbench.Views
double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量 double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量
double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距 double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距
double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距 double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距
// 如果被移动的控件接触到画布边缘,则限制移动范围
var canvasModel = nodeControl.FlowCanvas.Model;
var canvasWidth = canvasModel.Width - nodeControl.ActualWidth - 10;
var canvasHeight= canvasModel.Height - nodeControl.ActualHeight - 10;
newLeft = newLeft < 5 ? 5 : newLeft > canvasWidth ? canvasWidth : newLeft;
newTop = newTop < 5 ? 5 : newTop > canvasHeight ? canvasHeight : newTop;
this.flowEnvironment.MoveNode(Guid, nodeControl.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点 this.flowEnvironment.MoveNode(Guid, nodeControl.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
nodeControl.UpdateLocationConnections(); nodeControl.UpdateLocationConnections();
} }
@@ -1095,7 +1259,7 @@ namespace Serein.Workbench.Views
if (nodeControl.ViewModel?.NodeModel.ControlType == NodeControlType.Flipflop) if (nodeControl.ViewModel?.NodeModel.ControlType == NodeControlType.Flipflop)
{ {
contextMenu.Items.Add(CreateMenuItem("启动触发器", (s, e) => contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("启动触发器", (s, e) =>
{ {
if (s is MenuItem menuItem) if (s is MenuItem menuItem)
{ {
@@ -1119,7 +1283,7 @@ namespace Serein.Workbench.Views
if (nodeControl.ViewModel?.NodeModel?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void)) if (nodeControl.ViewModel?.NodeModel?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void))
{ {
contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) => contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("查看返回类型", (s, e) =>
{ {
DisplayReturnTypeTreeViewer(returnType); DisplayReturnTypeTreeViewer(returnType);
})); }));
@@ -1127,8 +1291,8 @@ namespace Serein.Workbench.Views
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => flowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid))); contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("设为起点", (s, e) => flowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("删除", async (s, e) => contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除", async (s, e) =>
{ {
var result = await flowEnvironment.RemoveNodeAsync(canvasGuid, nodeGuid); var result = await flowEnvironment.RemoveNodeAsync(canvasGuid, nodeGuid);
})); }));
@@ -1136,28 +1300,28 @@ namespace Serein.Workbench.Views
#region - #region -
var AvoidMenu = new MenuItem(); var AvoidMenu = new MenuItem();
AvoidMenu.Items.Add(CreateMenuItem("群组对齐", (s, e) => AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("群组对齐", (s, e) =>
{ {
AlignControlsWithGrouping(selectNodeControls, AlignMode.Grouping); AlignControlsWithGrouping(selectNodeControls, AlignMode.Grouping);
})); }));
AvoidMenu.Items.Add(CreateMenuItem("规划对齐", (s, e) => AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("规划对齐", (s, e) =>
{ {
AlignControlsWithGrouping(selectNodeControls, AlignMode.Planning); AlignControlsWithGrouping(selectNodeControls, AlignMode.Planning);
})); }));
AvoidMenu.Items.Add(CreateMenuItem("水平中心对齐", (s, e) => AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("水平中心对齐", (s, e) =>
{ {
AlignControlsWithGrouping(selectNodeControls, AlignMode.HorizontalCenter); AlignControlsWithGrouping(selectNodeControls, AlignMode.HorizontalCenter);
})); }));
AvoidMenu.Items.Add(CreateMenuItem("垂直中心对齐 ", (s, e) => AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("垂直中心对齐 ", (s, e) =>
{ {
AlignControlsWithGrouping(selectNodeControls, AlignMode.VerticalCenter); AlignControlsWithGrouping(selectNodeControls, AlignMode.VerticalCenter);
})); }));
AvoidMenu.Items.Add(CreateMenuItem("垂直对齐时水平斜分布", (s, e) => AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("垂直对齐时水平斜分布", (s, e) =>
{ {
AlignControlsWithGrouping(selectNodeControls, AlignMode.Vertical); AlignControlsWithGrouping(selectNodeControls, AlignMode.Vertical);
})); }));
AvoidMenu.Items.Add(CreateMenuItem("水平对齐时垂直斜分布", (s, e) => AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("水平对齐时垂直斜分布", (s, e) =>
{ {
AlignControlsWithGrouping(selectNodeControls, AlignMode.Horizontal); AlignControlsWithGrouping(selectNodeControls, AlignMode.Horizontal);
})); }));

View File

@@ -56,11 +56,11 @@ AllowDrop="True"-->
</TabControl> </TabControl>
<!-- Tab control buttons --> <!-- Tab control buttons -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right"> <!--<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right">
<Button Content="添加" Command="{Binding AddTabCommand}" Margin="5" Width="80"/> <Button Content="添加" Command="{Binding AddTabCommand}" Margin="5" Width="80"/>
<Button Content="移除" Command="{Binding RemoveTabCommand}" Margin="5" Width="80"/> <Button Content="移除" Command="{Binding RemoveTabCommand}" Margin="5" Width="80"/>
<!--<Button Content="Rename Tab" Command="{Binding RenameTabCommand}" />--> --><!--<Button Content="Rename Tab" Command="{Binding RenameTabCommand}" />--><!--
</StackPanel> </StackPanel>-->
</Grid> </Grid>
</UserControl> </UserControl>

View File

@@ -126,12 +126,11 @@ namespace Serein.Workbench.Views
private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e)
{ {
if (sender is TabControl tabControl if (sender is TabControl tabControl
&& tabControl.SelectedIndex > 0 && tabControl.SelectedIndex > -1
&& DataContext is FlowEditViewModel viewModel && DataContext is FlowEditViewModel viewModel
&& viewModel.CanvasTabs[tabControl.SelectedIndex] is FlowEditorTabModel tab) && viewModel.CanvasTabs[tabControl.SelectedIndex] is FlowEditorTabModel tab)
{ {
viewModel.EndEditingTab(lastTab); // 取消编辑
viewModel.EndEditingTab(lastTab); // 确认新名称
lastTab = tab; lastTab = tab;
return; return;
} }

View File

@@ -9,11 +9,24 @@
xmlns:vm="clr-namespace:Serein.Workbench.ViewModels" xmlns:vm="clr-namespace:Serein.Workbench.ViewModels"
mc:Ignorable="d" mc:Ignorable="d"
d:DataContext="{d:DesignInstance vm:FlowLibrarysViewModel}" d:DataContext="{d:DesignInstance vm:FlowLibrarysViewModel}"
d:DesignHeight="450" d:DesignWidth="800"
d:DesignHeight="450" d:DesignWidth="800"> AllowDrop="True"
Drop="FlowLibrarysView_Drop"
DragOver="FlowLibrarysView_DragOver" >
<UserControl.Resources> <UserControl.Resources>
<converter:CountToVisibilityConverter x:Key="CountToVisibilityConverter"/> <converter:CountToVisibilityConverter x:Key="CountToVisibilityConverter"/>
<!--<DataTemplate x:Key="NodeListTemplate">
<Grid Margin="2">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding NodeType}"/>
<TextBlock Text="{Binding AnotherName}" Margin="4,0,0,0"/>
<TextBlock Text="{Binding MethodName}" Margin="6,0,0,0"/>
</StackPanel>
</Grid>
</DataTemplate>-->
</UserControl.Resources> </UserControl.Resources>
<ScrollViewer> <ScrollViewer>
@@ -31,58 +44,27 @@
<TextBlock Text="{Binding LibraryName}" ></TextBlock> <TextBlock Text="{Binding LibraryName}" ></TextBlock>
<TextBlock Text="{Binding FilePath}" Margin="6,0,0,0"></TextBlock> <TextBlock Text="{Binding FilePath}" Margin="6,0,0,0"></TextBlock>
</StackPanel> </StackPanel>
<custom:FlowMethodInfoListBox Grid.Row="1" ItemsSource="{Binding ActionNodes}" Background="#D0F1F9"/>
<!--<custom:FlowMethodInfoListBox Grid.Row="1" Nodes="{Binding ActionNodes}" BackgroundColor="#D0F1F9"/> <custom:FlowMethodInfoListBox Grid.Row="2" ItemsSource="{Binding FlipflopNodes}" Background="#FACFC1"/>
<custom:FlowMethodInfoListBox Grid.Row="2" Nodes="{Binding FlipflopNodes}" BackgroundColor="#FACFC1"/> <custom:FlowMethodInfoListBox Grid.Row="3" ItemsSource="{Binding UINodes}" Background="#FFFBD7"/>
<custom:FlowMethodInfoListBox Grid.Row="3" Nodes="{Binding UINodes}" BackgroundColor="#FFFBD7"/>-->
<!--<ListBox Grid.Row="1" Margin="6,2,2,2" ItemsSource="{Binding ActionNodes}"
<ListBox Grid.Row="1" Margin="6,2,2,2" ItemsSource="{Binding ActionNodes}"
Visibility="{Binding ActionNodes, Converter={StaticResource CountToVisibilityConverter}}" Visibility="{Binding ActionNodes, Converter={StaticResource CountToVisibilityConverter}}"
Background="#D0F1F9"> Background="#D0F1F9"
<ListBox.ItemTemplate> ItemTemplate="{StaticResource NodeListTemplate}">
<DataTemplate>
<Grid Margin="2" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding NodeType}"></TextBlock>
<TextBlock Text="{Binding AnotherName}" Margin="4,0,0,0"></TextBlock>
<TextBlock Text="{Binding MethodName}" Margin="6,0,0,0"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox> </ListBox>
<ListBox Grid.Row="2" Margin="6,2,2,2" ItemsSource="{Binding FlipflopNodes}" <ListBox Grid.Row="2" Margin="6,2,2,2" ItemsSource="{Binding FlipflopNodes}"
Visibility="{Binding FlipflopNodes, Converter={StaticResource CountToVisibilityConverter}}" Visibility="{Binding FlipflopNodes, Converter={StaticResource CountToVisibilityConverter}}"
Background="#FACFC1"> Background="#FACFC1"
<ListBox.ItemTemplate> ItemTemplate="{StaticResource NodeListTemplate}">
<DataTemplate>
<Grid Margin="2" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding NodeType}"></TextBlock>
<TextBlock Text="{Binding AnotherName}" Margin="4,0,0,0"></TextBlock>
<TextBlock Text="{Binding MethodName}" Margin="6,0,0,0"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox> </ListBox>
<ListBox Grid.Row="3" Margin="6,2,2,2" ItemsSource="{Binding UINodes}" <ListBox Grid.Row="3" Margin="6,2,2,2" ItemsSource="{Binding UINodes}"
Visibility="{Binding UINodes, Converter={StaticResource CountToVisibilityConverter}}" Visibility="{Binding UINodes, Converter={StaticResource CountToVisibilityConverter}}"
Background="#FFFBD7"> Background="#FFFBD7"
<ListBox.ItemTemplate> ItemTemplate="{StaticResource NodeListTemplate}">
<DataTemplate> </ListBox>-->
<Grid Margin="2" >
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding NodeType}"></TextBlock>
<TextBlock Text="{Binding AnotherName}" Margin="4,0,0,0"></TextBlock>
<TextBlock Text="{Binding MethodName}" Margin="6,0,0,0"></TextBlock>
</StackPanel>
</Grid>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
</Grid> </Grid>
</DataTemplate> </DataTemplate>
@@ -90,3 +72,9 @@
</ItemsControl> </ItemsControl>
</ScrollViewer> </ScrollViewer>
</UserControl> </UserControl>
<!--<custom:FlowMethodInfoListBox Grid.Row="1" Nodes="{Binding ActionNodes}" BackgroundColor="#D0F1F9"/>
<custom:FlowMethodInfoListBox Grid.Row="2" Nodes="{Binding FlipflopNodes}" BackgroundColor="#FACFC1"/>
<custom:FlowMethodInfoListBox Grid.Row="3" Nodes="{Binding UINodes}" BackgroundColor="#FFFBD7"/> -->

View File

@@ -1,4 +1,5 @@
using Serein.Workbench.ViewModels; using Serein.Workbench.Node.ViewModel;
using Serein.Workbench.ViewModels;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -21,10 +22,32 @@ namespace Serein.Workbench.Views
/// </summary> /// </summary>
public partial class FlowLibrarysView : UserControl public partial class FlowLibrarysView : UserControl
{ {
private FlowLibrarysViewModel ViewModel => DataContext as FlowLibrarysViewModel ?? throw new ArgumentNullException();
public FlowLibrarysView() public FlowLibrarysView()
{ {
this.DataContext = App.GetService<Locator>().FlowLibrarysViewModel; this.DataContext = App.GetService<Locator>().FlowLibrarysViewModel;
InitializeComponent(); InitializeComponent();
} }
private void FlowLibrarysView_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string file in files)
{
if (file.EndsWith(".dll"))
{
ViewModel.LoadFileLibrary(file);
}
}
}
}
private void FlowLibrarysView_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Copy;
e.Handled = true;
}
} }
} }

View File

@@ -6,7 +6,8 @@
xmlns:local="clr-namespace:Serein.Workbench.Views" xmlns:local="clr-namespace:Serein.Workbench.Views"
mc:Ignorable="d" mc:Ignorable="d"
Loaded="Window_Loaded" Loaded="Window_Loaded"
Title="FlowWorkbenchView" Height="450" Width="800"> Title="FlowWorkbenchView" Height="450" Width="800"
Closing="Window_Closing">
<Grid> <Grid>
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto"/> <RowDefinition Height="auto"/>
@@ -23,7 +24,7 @@
<local:MainMenuBarView Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="5"/> <local:MainMenuBarView Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="5"/>
<!--左侧功能区--> <!--左侧功能区-->
<Grid Grid.Row="1" Grid.Column="0" > <Grid Grid.Row="1" Grid.Column="0">
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition> <RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition> <RowDefinition Height="*"></RowDefinition>
@@ -31,10 +32,13 @@
<!--<RowDefinition Height="3*"></RowDefinition>--> <!--<RowDefinition Height="3*"></RowDefinition>-->
</Grid.RowDefinitions> </Grid.RowDefinitions>
<local:BaseNodesView Grid.Row="0" Grid.ColumnSpan="1" Margin="0,0,0,15"/> <local:BaseNodesView Grid.Row="0" Grid.ColumnSpan="1" Margin="0,0,0,15"/>
<local:FlowLibrarysView Grid.Row="1" Grid.ColumnSpan="1" Margin="0,0,0,15"/> <local:FlowLibrarysView Grid.Row="1" Grid.ColumnSpan="1" Margin="0,0,0,15" />
</Grid> </Grid>
<!--功能区和编辑区的分割线-->
<GridSplitter Grid.Row="1" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="#CCD5F0" />
<!--流程编辑区--> <!--流程编辑区-->
<local:FlowEditView Grid.Row="1" Grid.Column="2"/> <local:FlowEditView Grid.Row="1" Grid.Column="2"/>
<!--编辑区和视图区的分割线-->
<GridSplitter Grid.Row="1" Grid.Column="3" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="#CCD5F0" />
</Grid> </Grid>
</Window> </Window>

View File

@@ -20,6 +20,7 @@ namespace Serein.Workbench.Views
/// </summary> /// </summary>
public partial class FlowWorkbenchView : Window public partial class FlowWorkbenchView : Window
{ {
private FlowWorkbenchViewModel ViewModel => ViewModel as FlowWorkbenchViewModel;
public FlowWorkbenchView() public FlowWorkbenchView()
{ {
this.DataContext = App.GetService<Locator>().FlowWorkbenchViewModel; this.DataContext = App.GetService<Locator>().FlowWorkbenchViewModel;
@@ -40,5 +41,12 @@ namespace Serein.Workbench.Views
this.Width = System.Windows.SystemParameters.PrimaryScreenWidth; this.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
this.Height = System.Windows.SystemParameters.PrimaryScreenHeight;*/ this.Height = System.Windows.SystemParameters.PrimaryScreenHeight;*/
} }
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
// 确保所有窗口关闭
LogWindow.Instance.Close();
System.Windows.Application.Current.Shutdown();
}
} }
} }

View File

@@ -16,19 +16,22 @@
<MenuItem Header="加载本地项目" ></MenuItem> <MenuItem Header="加载本地项目" ></MenuItem>
<MenuItem Header="加载远程项目"></MenuItem> <MenuItem Header="加载远程项目"></MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="编辑">
<MenuItem Header="增加画布"></MenuItem> <MenuItem Header="画布">
<MenuItem Header="删除当前画布"></MenuItem> <MenuItem Header="增加画布" Command="{Binding CreateFlowCanvasCommand}"></MenuItem>
<MenuItem Header="删除当前画布" Command="{Binding RemoteFlowCanvasCommand}"></MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="调试"> <MenuItem Header="运行">
<MenuItem Header="运行(仅当前画布)" Command="{Binding StartCurrentCanvasFlowCommand}"></MenuItem>
<MenuItem Header="运行(从起始节点)"></MenuItem> <MenuItem Header="运行(从起始节点)"></MenuItem>
<MenuItem Header="运行(从选定节点)" ></MenuItem> <MenuItem Header="运行(从选定节点)" ></MenuItem>
<MenuItem Header="运行" Command="{Binding StartFlowCommand}"></MenuItem>
<MenuItem Header="结束流程" ></MenuItem> <MenuItem Header="结束流程" ></MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="视图"> <MenuItem Header="视图">
<MenuItem Header="输出窗口" ></MenuItem> <MenuItem Header="输出窗口" Command="{Binding OpenEnvOutWindowCommand}"></MenuItem>
<MenuItem Header="重置画布"></MenuItem> <MenuItem Header="重置画布"></MenuItem>
<MenuItem Header="定位节点" ></MenuItem> <MenuItem Header="定位节点" ></MenuItem>
</MenuItem> </MenuItem>