From 7848af0363d0733fb2f9ddc579c064ca5288f3a6 Mon Sep 17 00:00:00 2001 From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com> Date: Tue, 27 May 2025 18:32:40 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E4=BA=86=E5=A4=9A=E7=94=BB?= =?UTF-8?q?=E5=B8=83=E4=B8=8B=EF=BC=8C=E8=8A=82=E7=82=B9=E7=9A=84=E5=A4=8D?= =?UTF-8?q?=E5=88=B6=E7=B2=98=E8=B4=B4=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Library/Api/IFlowEnvironment.cs | 9 +- Library/Entity/MoveNodeData.cs | 9 +- Library/Extension/FlowModelExtension.cs | 7 +- Library/FlowNode/FlowCanvasDetails.cs | 3 +- Library/FlowNode/MethodDetailsInfo.cs | 2 +- Library/FlowNode/SereinProjectData.cs | 1 + Library/Serein.Library.csproj | 1 + NodeFlow/Env/FlowEnvironment.cs | 149 +++++--- NodeFlow/Env/FlowEnvironmentDecorator.cs | 12 +- NodeFlow/Env/MsgControllerOfServer.cs | 6 +- NodeFlow/Env/RemoteFlowEnvironment.cs | 10 +- NodeFlow/FlowWorkManagement.cs | 21 +- NodeFlow/FlowWorkOptions.cs | 2 +- NodeFlow/Tool/NativeDllHelper.cs | 5 + Workbench/Api/IFlowCanvas.cs | 5 + Workbench/App.xaml | 2 - Workbench/App.xaml.cs | 34 +- Workbench/Customs/FlowMethodInfoListBox.xaml | 14 +- .../Customs/FlowMethodInfoListBox.xaml.cs | 120 ++++--- Workbench/LogWindow.xaml.cs | 4 + Workbench/MainWindow.xaml.cs | 31 +- Workbench/Models/FlowLibraryInfo.cs | 26 +- Workbench/Models/MoveNodeModel.cs | 18 + .../Node/Junction/JunctionControlBase.cs | 47 ++- Workbench/Node/Junction/JunctionData.cs | 4 +- Workbench/Node/View/ConnectionControl.cs | 5 +- Workbench/Node/View/DllControlControl.xaml.cs | 36 +- Workbench/Serein.WorkBench.csproj | 3 + Workbench/Services/FlowEEForwardingService.cs | 11 +- Workbench/Services/FlowNodeService.cs | 202 ++++++++++- Workbench/Services/KeyEventService.cs | 59 +-- Workbench/Services/NodeControlService.cs | 55 --- Workbench/Services/WorkbenchEventService.cs | 105 +++++- .../Themes/NodeTreeItemViewControl.xaml.cs | 7 +- Workbench/Themes/ObjectViewerControl.xaml.cs | 3 +- Workbench/Themes/TypeViewerWindow.xaml.cs | 7 +- Workbench/Tool/WpfFuncTool.cs | 48 +++ Workbench/ViewModels/FlowCanvasViewModel.cs | 4 +- Workbench/ViewModels/FlowEditViewModel.cs | 13 +- Workbench/ViewModels/FlowLibrarysViewModel.cs | 27 +- .../ViewModels/FlowWorkbenchViewModel.cs | 27 +- Workbench/ViewModels/MainMenuBarViewModel.cs | 32 +- Workbench/ViewModels/MainViewModel.cs | 10 +- Workbench/Views/BaseNodesView.xaml.cs | 1 + Workbench/Views/FlowCanvasView.xaml | 2 +- Workbench/Views/FlowCanvasView.xaml.cs | 338 +++++++++++++----- Workbench/Views/FlowEditView.xaml | 6 +- Workbench/Views/FlowEditView.xaml.cs | 5 +- Workbench/Views/FlowLibrarysView.xaml | 78 ++-- Workbench/Views/FlowLibrarysView.xaml.cs | 25 +- Workbench/Views/FlowWorkbenchView.xaml | 12 +- Workbench/Views/FlowWorkbenchView.xaml.cs | 8 + Workbench/Views/MainMenuBarView.xaml | 15 +- 53 files changed, 1187 insertions(+), 499 deletions(-) create mode 100644 Workbench/Models/MoveNodeModel.cs delete mode 100644 Workbench/Services/NodeControlService.cs create mode 100644 Workbench/Tool/WpfFuncTool.cs diff --git a/Library/Api/IFlowEnvironment.cs b/Library/Api/IFlowEnvironment.cs index 75e6bb6..6debb1e 100644 --- a/Library/Api/IFlowEnvironment.cs +++ b/Library/Api/IFlowEnvironment.cs @@ -1097,16 +1097,19 @@ namespace Serein.Library.Api void SetUIContextOperation(UIContextOperation uiContextOperation); /// - /// 开始运行 + /// 开始运行流程 /// - Task StartFlowAsync(); + /// 需要运行的流程Guid + /// + Task StartFlowAsync(string[] canvasGuids); + /// /// 从选定的节点开始运行 /// /// /// - Task StartAsyncInSelectNode(string startNodeGuid); + Task StartFlowFromSelectNodeAsync(string startNodeGuid); /// /// 结束运行 diff --git a/Library/Entity/MoveNodeData.cs b/Library/Entity/MoveNodeData.cs index bfac713..d437aa3 100644 --- a/Library/Entity/MoveNodeData.cs +++ b/Library/Entity/MoveNodeData.cs @@ -7,12 +7,5 @@ using System.Threading.Tasks; namespace Serein.Library { - /// - /// 拖拽创建节点使用的数据 - /// - public class MoveNodeData - { - public NodeControlType NodeControlType { get; set; } - public MethodDetailsInfo MethodDetailsInfo { get; set; } - } + } diff --git a/Library/Extension/FlowModelExtension.cs b/Library/Extension/FlowModelExtension.cs index df53097..6e051d6 100644 --- a/Library/Extension/FlowModelExtension.cs +++ b/Library/Extension/FlowModelExtension.cs @@ -101,10 +101,10 @@ namespace Serein.Library // 生成参数列表 ParameterData[] parameterData = nodeModel.SaveParameterInfo(); - NodeInfo nodeInfo = new NodeInfo + var nodeInfo = new NodeInfo { - Guid = nodeModel.Guid, CanvasGuid = nodeModel.CanvasGuid, + Guid = nodeModel.Guid, AssemblyName = nodeModel.MethodDetails.AssemblyName, MethodName = nodeModel.MethodDetails?.MethodName, Label = nodeModel.MethodDetails?.MethodAnotherName, @@ -130,10 +130,12 @@ namespace Serein.Library /// /// 从节点信息加载节点 /// + /// /// /// public static void LoadInfo(this NodeModelBase nodeModel, NodeInfo nodeInfo) { + nodeModel.CanvasGuid = nodeInfo.CanvasGuid; nodeModel.Guid = nodeInfo.Guid; nodeModel.Position = nodeInfo.Position ?? new PositionOfUI(0, 0);// 加载位置信息 var md = nodeModel.MethodDetails; // 当前节点的方法说明 @@ -191,6 +193,7 @@ namespace Serein.Library /// /// 开始执行 /// + /// /// /// 流程运行 /// diff --git a/Library/FlowNode/FlowCanvasDetails.cs b/Library/FlowNode/FlowCanvasDetails.cs index 4f56a64..0fa1ef7 100644 --- a/Library/FlowNode/FlowCanvasDetails.cs +++ b/Library/FlowNode/FlowCanvasDetails.cs @@ -23,6 +23,7 @@ namespace Serein.Library Env = env; } + public IFlowEnvironment Env { get; } /// @@ -79,8 +80,6 @@ namespace Serein.Library /// private string _startNode; - - } diff --git a/Library/FlowNode/MethodDetailsInfo.cs b/Library/FlowNode/MethodDetailsInfo.cs index 64bb13f..e8856fb 100644 --- a/Library/FlowNode/MethodDetailsInfo.cs +++ b/Library/FlowNode/MethodDetailsInfo.cs @@ -27,7 +27,7 @@ namespace Serein.Library public string NodeType { get; set; } /// - /// 方法说明 + /// 方法别名 /// public string MethodAnotherName { get; set; } diff --git a/Library/FlowNode/SereinProjectData.cs b/Library/FlowNode/SereinProjectData.cs index 8ccc9fa..3cd8754 100644 --- a/Library/FlowNode/SereinProjectData.cs +++ b/Library/FlowNode/SereinProjectData.cs @@ -336,6 +336,7 @@ namespace Serein.Library _x = x; _y = y; } + /// /// 指示控件在画布的横向向方向上的位置 /// diff --git a/Library/Serein.Library.csproj b/Library/Serein.Library.csproj index b0f5275..574d076 100644 --- a/Library/Serein.Library.csproj +++ b/Library/Serein.Library.csproj @@ -14,6 +14,7 @@ MIT True latest + no true true diff --git a/NodeFlow/Env/FlowEnvironment.cs b/NodeFlow/Env/FlowEnvironment.cs index 79265f3..1c84fb0 100644 --- a/NodeFlow/Env/FlowEnvironment.cs +++ b/NodeFlow/Env/FlowEnvironment.cs @@ -368,27 +368,87 @@ namespace Serein.NodeFlow.Env /// 异步运行 /// /// - public async Task StartFlowAsync() + public async Task StartFlowAsync(string[] canvasGuids) { + #region 校验参数 + HashSet guids = new HashSet(); + 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 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.Register(); // 注册脚本接口 var flowTaskOptions = new FlowWorkOptions { - Environment = this, - FlowContextPool = new ObjectPool(() => new DynamicContext(this)), - //Nodes = NodeModels.Values.ToList(), - AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(), - InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init), + Environment = this, // 流程 + Flows = flowTasks, + FlowContextPool = new ObjectPool(() => new DynamicContext(this)), // 上下文对象池 + AutoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType(), // 需要自动实例化的类型 + InitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init), LoadMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading), ExitMds = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit), - }; + + + flowTaskManagement = new FlowWorkManagement(flowTaskOptions); var cts = new CancellationTokenSource(); try { - var t =await flowTaskManagement.RunAsync(cts.Token); + var t = await flowTaskManagement.RunAsync(cts.Token); } catch (Exception ex) { @@ -405,12 +465,13 @@ namespace Serein.NodeFlow.Env } + /// /// 从选定节点开始运行 /// /// /// - public async Task StartAsyncInSelectNode(string startNodeGuid) + public async Task StartFlowFromSelectNodeAsync(string startNodeGuid) { if (flowTaskManagement is null) @@ -425,12 +486,6 @@ namespace Serein.NodeFlow.Env { 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); return true; } @@ -559,9 +614,22 @@ namespace Serein.NodeFlow.Env LoadLibrary(dllFilePath); // 加载项目文件时加载对应的程序集 } + + _ = Task.Run( async () => - { + { + // 加载画布 + foreach (var canvasInfo in projectData.Canvass) + { + LoadCanvas(canvasInfo); + } await LoadNodeInfosAsync(projectData.Nodes.ToList()); // 加载节点信息 + + // 加载画布 + foreach (var canvasInfo in projectData.Canvass) + { + await SetStartNodeAsync(canvasInfo.Guid, canvasInfo.StartNode); // 设置起始节点 + } //await SetStartNodeAsync("", projectData.StartNode); // 设置起始节点 }); @@ -779,20 +847,31 @@ namespace Serein.NodeFlow.Env /// public async Task CreateCanvasAsync(string canvasName, int width, int height) { - var model = new FlowCanvasDetails(this) + var info = new FlowCanvasDetailsInfo() { Guid = Guid.NewGuid().ToString(), Height = height, Width = width, + ViewX = 0, + ViewY = 0, + ScaleY = 1, + ScaleX = 1, 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); - await UIContextOperation.InvokeAsync(() => + UIContextOperation.InvokeAsync(() => { OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model)); }); - var info = model.ToInfo(); - return info; + return model; } /// @@ -957,7 +1036,7 @@ namespace Serein.NodeFlow.Env #region 确定节点之间的参数调用关系 foreach (var toNode in NodeModels.Values) { - var canvasGuid = toNode.Guid; + var canvasGuid = toNode.CanvasGuid; if (toNode.MethodDetails.ParameterDetailss == null) { continue; @@ -1727,7 +1806,7 @@ namespace Serein.NodeFlow.Env private bool TryAddNode(NodeModelBase nodeModel) { nodeModel.Guid ??= Guid.NewGuid().ToString(); - NodeModels[nodeModel.Guid] = nodeModel; + NodeModels.TryAdd(nodeModel.Guid, nodeModel); // 如果是触发器,则需要添加到专属集合中 if (nodeModel is SingleFlipflopNode flipflopNode) @@ -1944,32 +2023,22 @@ namespace Serein.NodeFlow.Env /// /// 更改起点节点 /// - /// - /// + /// 节点所在的画布 + /// 起始节点 private void SetStartNode(FlowCanvasDetails cavnasModel, NodeModelBase newStartNode) { var oldNodeGuid = cavnasModel.StartNode; + /*if(TryGetNodeModel(oldNodeGuid, out var newStartNodeModel)) + { + newStartNode.IsStart = false; + }*/ cavnasModel.StartNode = newStartNode.Guid; + //newStartNode.IsStart = true; + UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(cavnasModel.Guid, oldNodeGuid, cavnasModel.StartNode))); - //if (OperatingSystem.IsWindows()) - //{ - // } } - ///// - ///// 输出内容 - ///// - ///// - //private void Output(string msg) - //{ - // if (OperatingSystem.IsWindows()) - // { - // UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg)); - // } - - //} - /// /// 向容器登记缓存的持久化实例 /// diff --git a/NodeFlow/Env/FlowEnvironmentDecorator.cs b/NodeFlow/Env/FlowEnvironmentDecorator.cs index 5456d7c..4d6493f 100644 --- a/NodeFlow/Env/FlowEnvironmentDecorator.cs +++ b/NodeFlow/Env/FlowEnvironmentDecorator.cs @@ -263,7 +263,7 @@ namespace Serein.NodeFlow.Env /// 目标节点控制点 /// 决定了方法执行后的后继行为 public async Task ConnectInvokeNodeAsync(string canvasGuid, - string fromNodeGuid, + string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, @@ -518,14 +518,16 @@ namespace Serein.NodeFlow.Env return await currentFlowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid); } - public async Task StartFlowAsync() + public async Task StartFlowAsync(string[] canvasGuids) { - return await currentFlowEnvironment.StartFlowAsync(); + return await currentFlowEnvironment.StartFlowAsync(canvasGuids); } - public async Task StartAsyncInSelectNode(string startNodeGuid) + + + public async Task StartFlowFromSelectNodeAsync(string startNodeGuid) { - return await currentFlowEnvironment.StartAsyncInSelectNode(startNodeGuid); + return await currentFlowEnvironment.StartFlowFromSelectNodeAsync(startNodeGuid); } diff --git a/NodeFlow/Env/MsgControllerOfServer.cs b/NodeFlow/Env/MsgControllerOfServer.cs index 5902b98..005fce0 100644 --- a/NodeFlow/Env/MsgControllerOfServer.cs +++ b/NodeFlow/Env/MsgControllerOfServer.cs @@ -173,10 +173,10 @@ namespace Serein.NodeFlow.Env /// /// [AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlow)] - private async Task StartAsync() + private async Task StartAsync(string[] canvasGuid) { var uiContextOperation = environment.IOC.Get(); - var state = await environment.StartFlowAsync(); + var state = await environment.StartFlowAsync(canvasGuid); return new { state = state, @@ -191,7 +191,7 @@ namespace Serein.NodeFlow.Env [AutoSocketHandle(ThemeValue = EnvMsgTheme.StartFlowInSelectNode)] private async Task StartAsyncInSelectNode(string nodeGuid) { - var state = await environment.StartAsyncInSelectNode(nodeGuid); + var state = await environment.StartFlowFromSelectNodeAsync(nodeGuid); return new { state = state, diff --git a/NodeFlow/Env/RemoteFlowEnvironment.cs b/NodeFlow/Env/RemoteFlowEnvironment.cs index c4cd54b..1e3abee 100644 --- a/NodeFlow/Env/RemoteFlowEnvironment.cs +++ b/NodeFlow/Env/RemoteFlowEnvironment.cs @@ -420,18 +420,22 @@ namespace Serein.NodeFlow.Env /// 启动远程环境的流程 /// /// - public async Task StartFlowAsync() + public async Task StartFlowAsync(string[] canvasGuids) { // 远程环境下不需要UI上下文 - var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.StartFlow); + var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.StartFlow, new + { + canvasGuids + }); return result; } + /// /// 从选定的节点开始运行 /// /// /// - public async Task StartAsyncInSelectNode(string startNodeGuid) + public async Task StartFlowFromSelectNodeAsync(string startNodeGuid) { var result = await msgClient.SendAndWaitDataAsync(EnvMsgTheme.StartFlowInSelectNode, new { diff --git a/NodeFlow/FlowWorkManagement.cs b/NodeFlow/FlowWorkManagement.cs index 9b4a4d1..53d2f11 100644 --- a/NodeFlow/FlowWorkManagement.cs +++ b/NodeFlow/FlowWorkManagement.cs @@ -43,7 +43,7 @@ namespace Serein.NodeFlow } /// - /// 初始化啊 + /// 初始化 /// /// public async Task RunAsync(CancellationToken token) @@ -85,22 +85,19 @@ namespace Serein.NodeFlow var flowNodes = flow.GetNodes(); // 找到流程的起始节点,开始运行 - NodeModelBase? startNode = flowNodes.FirstOrDefault(node => node.IsStart); - if (startNode is null) - { - return false; - } + NodeModelBase startNode = flow.GetStartNode(); // 是否后台运行当前画布流程 if (flow.IsTaskAsync) { - _ = Task.Run(async () => await CallStartNode(startNode)); + _ = Task.Run(async () => await CallStartNode(startNode), token); // 后台调用流程中的触发器 + } else { await CallStartNode(startNode); + } - var flipflopTasks = CallFlipflopNode(flow); // 获取所有触发器异步任务 - _ = Task.Run(async () => await flipflopTasks); // 后台调用流程中的触发器 + _ = Task.Run(async () => await CallFlipflopNode(flow), token); // 后台调用流程中的触发器 } // 等待流程运行完成 @@ -226,7 +223,7 @@ namespace Serein.NodeFlow return isSuccessful; } - private Task CallFlipflopNode(FlowTask flow) + private async Task CallFlipflopNode(FlowTask flow) { var env = WorkOptions.Environment; var flipflopNodes = flow.GetNodes().Where(item => item is SingleFlipflopNode node @@ -242,9 +239,8 @@ namespace Serein.NodeFlow { await RunGlobalFlipflopAsync(env, node); // 启动流程时启动全局触发器 }); - Task.WhenAll(tasks); + await Task.WhenAll(tasks); } - return Task.CompletedTask; } /// @@ -257,6 +253,7 @@ namespace Serein.NodeFlow var pool = WorkOptions.FlowContextPool; var token = WorkOptions.CancellationTokenSource.Token; var context = pool.Allocate(); + context.Reset(); await startNode.StartFlowAsync(context, token); context.Exit(); pool.Free(context); diff --git a/NodeFlow/FlowWorkOptions.cs b/NodeFlow/FlowWorkOptions.cs index bce319a..755be81 100644 --- a/NodeFlow/FlowWorkOptions.cs +++ b/NodeFlow/FlowWorkOptions.cs @@ -46,7 +46,7 @@ namespace Serein.NodeFlow /// /// 上下文线程池 /// - public Serein.Library.Utils.ObjectPool FlowContextPool { get; set; } + public Serein.Library.Utils.ObjectPool FlowContextPool { get; set; } /// /// 每个画布需要启用的节点 diff --git a/NodeFlow/Tool/NativeDllHelper.cs b/NodeFlow/Tool/NativeDllHelper.cs index 2894319..facbace 100644 --- a/NodeFlow/Tool/NativeDllHelper.cs +++ b/NodeFlow/Tool/NativeDllHelper.cs @@ -69,6 +69,11 @@ namespace Serein.NodeFlow.Tool { if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + if (!Directory.Exists(path)) + { + SereinEnv.WriteLine(InfoType.ERROR, $"尝试加载Dll时失败,路径不存在。{path}"); + return; + } foreach (var file in Directory.GetFiles(path, "*.dll")) { LoadWindowsLibrarie(file); diff --git a/Workbench/Api/IFlowCanvas.cs b/Workbench/Api/IFlowCanvas.cs index bd965cb..21b8b23 100644 --- a/Workbench/Api/IFlowCanvas.cs +++ b/Workbench/Api/IFlowCanvas.cs @@ -23,6 +23,11 @@ namespace Serein.Workbench.Api /// string Name { get; } + /// + /// 数据 + /// + FlowCanvasDetails Model { get; } + /// /// 移除节点 /// diff --git a/Workbench/App.xaml b/Workbench/App.xaml index 99644e4..59dcb89 100644 --- a/Workbench/App.xaml +++ b/Workbench/App.xaml @@ -5,8 +5,6 @@ xmlns:view="clr-namespace:Serein.Workbench.Views" StartupUri="Views/FlowWorkbenchView.xaml" Startup="Application_Startup"> - - diff --git a/Workbench/App.xaml.cs b/Workbench/App.xaml.cs index d13581f..b6b9831 100644 --- a/Workbench/App.xaml.cs +++ b/Workbench/App.xaml.cs @@ -10,6 +10,7 @@ using Serein.Workbench.ViewModels; using System.Diagnostics; using System.IO; using System.Windows; +using System.Windows.Input; using System.Windows.Threading; namespace Serein.Workbench @@ -38,10 +39,9 @@ namespace Serein.Workbench public static void AddWorkbenchServices(this IServiceCollection collection) { collection.AddSingleton(); // 流程事件管理 + collection.AddSingleton();// 按键事件管理 collection.AddSingleton(); // 流程事件管理 collection.AddSingleton(); // 节点操作管理 - // collection.AddSingleton(); // 按键事件管理 - //collection.AddSingleton(); // 流程节点控件管理 } @@ -87,27 +87,30 @@ namespace Serein.Workbench { return ServiceProvider?.GetService() ?? throw new NullReferenceException(); } - + public App() { - var collection = new ServiceCollection(); collection.AddWorkbenchServices(); collection.AddFlowServices(); collection.AddViewModelServices(); var services = collection.BuildServiceProvider(); // 绑定并返回获取实例的服务接口 App.ServiceProvider = services; -#if DEBUG + + + + _ = this.LoadLocalProjectAsync(); -#endif + } + private async Task LoadLocalProjectAsync() { await Task.Delay(500); #if DEBUG - if (1 == 10) + if (1 ==1) { // 这里是测试代码,可以删除 string filePath; @@ -115,12 +118,17 @@ namespace Serein.Workbench filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\Release\banyunqi\project.dnf"; filePath = @"F:\临时\project\project.dnf"; filePath = @"F:\TempFile\flow\qrcode\project.dnf"; - //filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\test.dnf"; - string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 - App.FlowProjectData = JsonConvert.DeserializeObject(content); - App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;// - var dir = Path.GetDirectoryName(filePath); - App.GetService().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); + filePath = @"F:\TempFile\flow\temp\project.dnf"; + if (File.Exists(filePath)) + { + string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容 + App.FlowProjectData = JsonConvert.DeserializeObject(content); + App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;// + var dir = Path.GetDirectoryName(filePath); + App.GetService().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); + } + + } #endif } diff --git a/Workbench/Customs/FlowMethodInfoListBox.xaml b/Workbench/Customs/FlowMethodInfoListBox.xaml index 7edc49c..68e67d0 100644 --- a/Workbench/Customs/FlowMethodInfoListBox.xaml +++ b/Workbench/Customs/FlowMethodInfoListBox.xaml @@ -11,16 +11,16 @@ - + - + - - - + + + diff --git a/Workbench/Customs/FlowMethodInfoListBox.xaml.cs b/Workbench/Customs/FlowMethodInfoListBox.xaml.cs index d5a3606..40d89b1 100644 --- a/Workbench/Customs/FlowMethodInfoListBox.xaml.cs +++ b/Workbench/Customs/FlowMethodInfoListBox.xaml.cs @@ -1,4 +1,5 @@ using Serein.Library; +using Serein.Library.Utils; using Serein.Workbench.Models; using System; using System.Collections; @@ -20,10 +21,19 @@ using System.Windows.Shapes; namespace Serein.Workbench.Customs { - - public class Test: DependencyObject + /// + /// 拖拽创建节点类型 + /// + public static class MouseNodeType { - + /// + /// 创建来自DLL的节点 + /// + public static string CreateDllNodeInCanvas { get; } = nameof(CreateDllNodeInCanvas); + /// + /// 创建基础节点 + /// + public static string CreateBaseNodeInCanvas { get; } = nameof(CreateBaseNodeInCanvas); } /// @@ -35,66 +45,90 @@ namespace Serein.Workbench.Customs public FlowMethodInfoListBox() { - this.DataContext = this; InitializeComponent(); } - public IEnumerable Nodes + public static readonly DependencyProperty ItemsSourceProperty = + DependencyProperty.Register(nameof(ItemsSource), typeof(IEnumerable), typeof(FlowMethodInfoListBox), new PropertyMetadata(null)); + + public IEnumerable ItemsSource { - get { return (IEnumerable)GetValue(NodesProperty); } - set { SetValue(NodesProperty, value); } + get => (IEnumerable)GetValue(ItemsSourceProperty); + set => SetValue(ItemsSourceProperty, value); } - //public ItemCollection Items - //{ - // get - // { - // return (ItemCollection)GetValue(ItemsProperty); - // } - // set - // { - // SetValue(ItemsProperty, value); - // } - //} + public static readonly DependencyProperty BackgroundProperty = + DependencyProperty.Register(nameof(Background), typeof(Brush), typeof(FlowMethodInfoListBox), new PropertyMetadata(Brushes.Transparent)); - - public static readonly DependencyProperty NodesProperty = DependencyProperty.Register("NodesProperty", typeof(IEnumerable), 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), typeof(FlowMethodInfoListBox), new PropertyMetadata(null)); - - public Brush BackgroundColor + public Brush Background { - get { return (Brush)GetValue(BackgroundColorProperty); } - set { SetValue(BackgroundColorProperty, value); } + get => (Brush)GetValue(BackgroundProperty); + set => SetValue(BackgroundProperty, value); } - public static readonly DependencyProperty BackgroundColorProperty = - DependencyProperty.Register(nameof(BackgroundColor), typeof(Brush), typeof(FlowMethodInfoListBox), new PropertyMetadata(Brushes.White)); - + /// + /// 存储拖拽开始时的鼠标位置 + /// + 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(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); + } + } + } } } diff --git a/Workbench/LogWindow.xaml.cs b/Workbench/LogWindow.xaml.cs index 495a979..b0e20ec 100644 --- a/Workbench/LogWindow.xaml.cs +++ b/Workbench/LogWindow.xaml.cs @@ -17,6 +17,10 @@ namespace Serein.Workbench /// public partial class LogWindow : Window { + private static LogWindow instance = new LogWindow(); + public static LogWindow Instance => instance; + + private StringBuilder logBuffer = new StringBuilder(); private int logUpdateInterval = 200; // 批量更新的时间间隔(毫秒) private Timer logUpdateTimer; diff --git a/Workbench/MainWindow.xaml.cs b/Workbench/MainWindow.xaml.cs index 765ac80..fa4a368 100644 --- a/Workbench/MainWindow.xaml.cs +++ b/Workbench/MainWindow.xaml.cs @@ -9,6 +9,7 @@ using Serein.NodeFlow.Env; using Serein.NodeFlow.Tool; using Serein.Workbench.Api; using Serein.Workbench.Extension; +using Serein.Workbench.Models; using Serein.Workbench.Node; using Serein.Workbench.Node.View; using Serein.Workbench.Node.ViewModel; @@ -27,20 +28,7 @@ using DataObject = System.Windows.DataObject; namespace Serein.Workbench { - /// - /// 拖拽创建节点类型 - /// - public static class MouseNodeType - { - /// - /// 创建来自DLL的节点 - /// - public static string CreateDllNodeInCanvas { get; } = nameof(CreateDllNodeInCanvas); - /// - /// 创建基础节点 - /// - public static string CreateBaseNodeInCanvas { get; } = nameof(CreateBaseNodeInCanvas); - } + @@ -1257,6 +1245,7 @@ namespace Serein.Workbench #endregion #region 拖拽DLL文件到左侧功能区,加载相关节点清单 + /// /// 当拖动文件到窗口时触发,加载DLL文件 /// @@ -1308,6 +1297,7 @@ namespace Serein.Workbench /// private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e) { + dynamic GlobalJunctionData = ""; var myData = GlobalJunctionData.MyGlobalConnectingData; if (myData.IsCreateing && e.LeftButton == MouseButtonState.Pressed) { @@ -1406,12 +1396,12 @@ namespace Serein.Workbench PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y); 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 () => { - 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; // 恢复视觉效果 ViewModel.IsConnectionArgSourceNode = false; ViewModel.IsConnectionInvokeNode = false; - GlobalJunctionData.OK(); + dynamic GlobalJunctionData = ""; + } #region 拖动画布实现缩放平移效果 @@ -1908,6 +1899,8 @@ namespace Serein.Workbench /// private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { + dynamic GlobalJunctionData = ""; + if (GlobalJunctionData.MyGlobalConnectingData.IsCreateing) { return; @@ -1963,6 +1956,7 @@ namespace Serein.Workbench FlowChartCanvas.ReleaseMouseCapture(); } + dynamic GlobalJunctionData = ""; // 创建连线 if (GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing) { @@ -2818,8 +2812,9 @@ public class FlowLibrary CancelSelectNode(); EndConnection(); } + dynamic GlobalJunctionData = ""; - if(GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing) + if (GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing) { if(myData.Type == JunctionOfConnectionType.Invoke) { diff --git a/Workbench/Models/FlowLibraryInfo.cs b/Workbench/Models/FlowLibraryInfo.cs index 21c6008..ff81b0b 100644 --- a/Workbench/Models/FlowLibraryInfo.cs +++ b/Workbench/Models/FlowLibraryInfo.cs @@ -9,21 +9,10 @@ using System.Threading.Tasks; 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; - } + /// + /// 依赖信息 + /// internal partial class FlowLibraryInfo : ObservableObject { [ObservableProperty] @@ -33,12 +22,11 @@ namespace Serein.Workbench.Models private string _libraryName; [ObservableProperty] - private ObservableCollection _methodInfo; + private ObservableCollection _methodInfo; - - public List ActionNodes { get => MethodInfo.Where(x => x.NodeType == NodeType.Action.ToString()).ToList(); set { } } - public List FlipflopNodes { get => MethodInfo.Where(x => x.NodeType == NodeType.Flipflop.ToString()).ToList(); set { } } - public List UINodes { get => MethodInfo.Where(x => x.NodeType == NodeType.UI.ToString()).ToList(); set { } } + public List ActionNodes { get => MethodInfo.Where(x => x.NodeType == NodeType.Action.ToString()).ToList(); set { } } + public List FlipflopNodes { get => MethodInfo.Where(x => x.NodeType == NodeType.Flipflop.ToString()).ToList(); set { } } + public List UINodes { get => MethodInfo.Where(x => x.NodeType == NodeType.UI.ToString()).ToList(); set { } } } } diff --git a/Workbench/Models/MoveNodeModel.cs b/Workbench/Models/MoveNodeModel.cs new file mode 100644 index 0000000..7b19cec --- /dev/null +++ b/Workbench/Models/MoveNodeModel.cs @@ -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 +{ + /// + /// 拖拽创建节点使用的数据 + /// + internal class MoveNodeModel + { + public NodeControlType NodeControlType { get; set; } + public MethodDetailsInfo MethodDetailsInfo { get; set; } + } +} diff --git a/Workbench/Node/Junction/JunctionControlBase.cs b/Workbench/Node/Junction/JunctionControlBase.cs index 5ce870c..d975989 100644 --- a/Workbench/Node/Junction/JunctionControlBase.cs +++ b/Workbench/Node/Junction/JunctionControlBase.cs @@ -12,9 +12,15 @@ using System.Windows.Shapes; using System.Windows.Media.Media3D; using System.Windows.Documents; using System.Threading; +using Serein.Workbench.Services; +using Serein.Workbench.Tool; namespace Serein.Workbench.Node.View { + + /// + /// 控制带的拓展方法 + /// internal static class MyUIFunc { public static Pen CreateAndFreezePen() @@ -31,10 +37,11 @@ namespace Serein.Workbench.Node.View } } + /// + /// 入参控件 + /// public class ParamsArgControl: Shape { - - public ParamsArgControl() { this.MouseDown += ParamsArg_OnMouseDown; // 增加或删除 @@ -159,8 +166,10 @@ namespace Serein.Workbench.Node.View public abstract class JunctionControlBase : Shape { + private readonly FlowNodeService flowNodeService; protected JunctionControlBase() { + flowNodeService = App.GetService(); this.Width = 25; this.Height = 20; this.MouseDown += JunctionControlBase_MouseDown; @@ -229,7 +238,7 @@ namespace Serein.Workbench.Node.View { if(_isMouseOver != value) { - GlobalJunctionData.MyGlobalConnectingData.CurrentJunction = this; + flowNodeService.ConnectingData.CurrentJunction = this; _isMouseOver = value; InvalidateVisual(); } @@ -252,22 +261,22 @@ namespace Serein.Workbench.Node.View /// protected Brush GetBackgrounp() { - var myData = GlobalJunctionData.MyGlobalConnectingData; - if(!myData.IsCreateing) + var cd = flowNodeService.ConnectingData; + if(!cd.IsCreateing) { return Brushes.Transparent; } 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 { - return myData.ConnectionArgSourceType.ToLineColor(); + return cd.ConnectionArgSourceType.ToLineColor(); } } else @@ -320,15 +329,15 @@ namespace Serein.Workbench.Node.View { if (e.LeftButton == MouseButtonState.Pressed) { - var canvas = MainWindow.GetParentOfType(this); + var canvas = WpfFuncTool.GetParentOfType(this); if (canvas != null) { - var myData = GlobalJunctionData.MyGlobalConnectingData; - myData.Reset(); - myData.IsCreateing = true; // 表示开始连接 - myData.StartJunction = this; - myData.CurrentJunction = this; - myData.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas); + var cd = flowNodeService.ConnectingData; + cd.Reset(); + cd.IsCreateing = true; // 表示开始连接 + cd.StartJunction = this; + cd.CurrentJunction = this; + cd.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas); var junctionOfConnectionType = this.JunctionType.ToConnectyionType(); ConnectionLineShape bezierLine; // 类别 @@ -346,13 +355,13 @@ namespace Serein.Workbench.Node.View return; } bezierLine = new ConnectionLineShape(LineType.Bezier, - myData.StartPoint, - myData.StartPoint, + cd.StartPoint, + cd.StartPoint, brushColor, isTop: true); // 绘制临时的线 Mouse.OverrideCursor = Cursors.Cross; // 设置鼠标为正在创建连线 - myData.MyLine = new MyLine(canvas, bezierLine); + cd.MyLine = new MyLine(canvas, bezierLine); } } e.Handled = true; diff --git a/Workbench/Node/Junction/JunctionData.cs b/Workbench/Node/Junction/JunctionData.cs index ceb3048..7acb33b 100644 --- a/Workbench/Node/Junction/JunctionData.cs +++ b/Workbench/Node/Junction/JunctionData.cs @@ -123,7 +123,7 @@ namespace Serein.Workbench.Node.View } /// - /// 重置 + /// 重置连线状态 /// 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 object _lockObj = new object(); diff --git a/Workbench/Node/View/ConnectionControl.cs b/Workbench/Node/View/ConnectionControl.cs index 2d130e2..07080b9 100644 --- a/Workbench/Node/View/ConnectionControl.cs +++ b/Workbench/Node/View/ConnectionControl.cs @@ -1,6 +1,7 @@ using Serein.Library; using Serein.Library.Api; using Serein.Workbench.Extension; +using Serein.Workbench.Tool; using System; using System.Net; using System.Windows; @@ -224,8 +225,8 @@ namespace Serein.Workbench.Node.View private void ConfigureLineContextMenu() { var contextMenu = new ContextMenu(); - contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => Remote())); - contextMenu.Items.Add(MainWindow.CreateMenuItem("于父节点调用顺序中置顶", (s, e) => Topping())); + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除连线", (s, e) => Remote())); + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("于父节点调用顺序中置顶", (s, e) => Topping())); BezierLine.ContextMenu = contextMenu; } diff --git a/Workbench/Node/View/DllControlControl.xaml.cs b/Workbench/Node/View/DllControlControl.xaml.cs index ea11559..da807f9 100644 --- a/Workbench/Node/View/DllControlControl.xaml.cs +++ b/Workbench/Node/View/DllControlControl.xaml.cs @@ -140,26 +140,26 @@ namespace Serein.Workbench.Node.View return; } - MoveNodeData moveNodeData = new MoveNodeData - { + //MoveNodeData moveNodeData = new MoveNodeData + //{ - NodeControlType = nodeType switch - { - NodeType.Action => NodeControlType.Action, - NodeType.Flipflop => NodeControlType.Flipflop, - NodeType.UI => NodeControlType.UI, - _ => NodeControlType.None, - }, - MethodDetailsInfo = mdInfo, - }; - if(moveNodeData.NodeControlType == NodeControlType.None) - { - return; - } + // NodeControlType = nodeType switch + // { + // NodeType.Action => NodeControlType.Action, + // NodeType.Flipflop => NodeControlType.Flipflop, + // NodeType.UI => NodeControlType.UI, + // _ => NodeControlType.None, + // }, + // MethodDetailsInfo = mdInfo, + //}; + //if(moveNodeData.NodeControlType == NodeControlType.None) + //{ + // return; + //} - // 创建一个 DataObject 用于拖拽操作,并设置拖拽效果 - DataObject dragData = new DataObject(MouseNodeType.CreateDllNodeInCanvas, moveNodeData); - DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move); + //// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果 + //DataObject dragData = new DataObject(MouseNodeType.CreateDllNodeInCanvas, moveNodeData); + //DragDrop.DoDragDrop(typeText, dragData, DragDropEffects.Move); } } } diff --git a/Workbench/Serein.WorkBench.csproj b/Workbench/Serein.WorkBench.csproj index 16613c4..1ce1737 100644 --- a/Workbench/Serein.WorkBench.csproj +++ b/Workbench/Serein.WorkBench.csproj @@ -24,6 +24,8 @@ + + @@ -38,6 +40,7 @@ + diff --git a/Workbench/Services/FlowEEForwardingService.cs b/Workbench/Services/FlowEEForwardingService.cs index efcebc2..13d6118 100644 --- a/Workbench/Services/FlowEEForwardingService.cs +++ b/Workbench/Services/FlowEEForwardingService.cs @@ -26,17 +26,21 @@ namespace Serein.Workbench.Services /// private readonly IFlowEnvironment flowEnvironment; private readonly IFlowEnvironmentEvent flowEnvironmentEvent; + private readonly UIContextOperation uIContextOperation; /// /// 转发流程运行环境各个事件的实现类 /// /// /// + /// public FlowEEForwardingService(IFlowEnvironment flowEnvironment, - IFlowEnvironmentEvent flowEnvironmentEvent) + IFlowEnvironmentEvent flowEnvironmentEvent, + UIContextOperation uIContextOperation) { this.flowEnvironment = flowEnvironment; this.flowEnvironmentEvent = flowEnvironmentEvent; + this.uIContextOperation = uIContextOperation; InitFlowEnvironmentEvent(); } @@ -185,7 +189,10 @@ namespace Serein.Workbench.Services /// private void FlowEnvironment_OnEnvOutEvent(InfoType type, string value) { - //LogOutWindow.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}"); + uIContextOperation.Invoke(() => + { + OnEnvOut?.Invoke(type, value); + }); } /// diff --git a/Workbench/Services/FlowNodeService.cs b/Workbench/Services/FlowNodeService.cs index cdcb120..5d837e3 100644 --- a/Workbench/Services/FlowNodeService.cs +++ b/Workbench/Services/FlowNodeService.cs @@ -1,4 +1,6 @@ -using Serein.Library; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Serein.Library; using Serein.Library.Api; using Serein.Workbench.Api; using Serein.Workbench.Node; @@ -6,7 +8,10 @@ using Serein.Workbench.Node.View; using Serein.Workbench.Node.ViewModel; using Serein.Workbench.ViewModels; using Serein.Workbench.Views; +using System.Text; +using System.Windows; using System.Windows.Controls; +using System.Windows.Media; namespace Serein.Workbench.Services { @@ -34,7 +39,6 @@ namespace Serein.Workbench.Services #endregion - #region 创建节点相关的属性 /// /// 当前查看的画布 @@ -51,7 +55,6 @@ namespace Serein.Workbench.Services /// public NodeControlType CurrentNodeControlType { get; set; } = NodeControlType.None; - /// /// 当前鼠标位置 /// @@ -61,6 +64,12 @@ namespace Serein.Workbench.Services /// 当前选中的节点 /// public NodeControlBase? CurrentSelectNodeControl { get; set; } + + /// + /// 连接数据 + /// + public ConnectingData ConnectingData { get; } = new ConnectingData(); + #endregion /// @@ -72,6 +81,11 @@ namespace Serein.Workbench.Services /// public NodeControlBase? ConnectionEndNode { get; set; } + /// + /// 当前所有画布 + /// + public FlowCanvasView[] FlowCanvass => Canvass.Select(c => c.Value).ToArray(); + /// /// 记录流程画布 /// @@ -134,7 +148,27 @@ namespace Serein.Workbench.Services flowEEForwardingService.OnNodeTakeOut += FlowEEForwardingService_OnNodeTakeOut; ; // 节点从容器中取出 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) @@ -387,7 +421,167 @@ namespace Serein.Workbench.Services return Canvass.TryGetValue(nodeGuid, out flowCanvas); } + #region 节点的复制与粘贴 + /// + /// 从节点信息转换为Json文本数据 + /// + public string CpoyNodeInfo(List 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; + } + } + + + /// + /// 从Json中加载节点 + /// + /// 需要加载在哪个画布上 + /// 文本内容 + /// 需要加载的位置 + public void PasteNodeInfo(string canvasGuid, string jsonText, PositionOfUI positionOfUI) + { + try + { + List? nodes = JsonConvert.DeserializeObject>(jsonText); + if (nodes is not null && nodes.Count != 0) + { + + } + if (nodes is null || nodes.Count < 0) + { + return; + } + + #region 节点去重 + Dictionary guids = new Dictionary(); // 记录 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>(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 向运行环境发出请求 diff --git a/Workbench/Services/KeyEventService.cs b/Workbench/Services/KeyEventService.cs index a21718c..88c6ec2 100644 --- a/Workbench/Services/KeyEventService.cs +++ b/Workbench/Services/KeyEventService.cs @@ -6,19 +6,20 @@ using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows.Input; +using static System.Windows.Forms.AxHost; namespace Serein.Workbench.Services { - delegate void KeyDownEventHandler(Key key); - delegate void KeyUpEventHandler(Key key); + public delegate void KeyDownEventHandler(Key key); + public delegate void KeyUpEventHandler(Key key); /// - /// 全局事件服务 + /// 全局按键事件服务 /// - internal interface IKeyEventService + public interface IKeyEventService { - event KeyDownEventHandler KeyDown; - event KeyUpEventHandler KeyUp; + event KeyDownEventHandler OnKeyDown; + event KeyUpEventHandler OnKeyUp; /// /// 获取某个按键状态 @@ -26,27 +27,34 @@ namespace Serein.Workbench.Services /// /// bool GetKeyState(Key key); + /// - /// 设置某个按键的状态 + /// 按下了某个键 /// /// - /// - void SetKeyState(Key key, bool statestate); + void KeyDown(Key key); + + /// + /// 抬起了某个键 + /// + /// + void KeyUp(Key key); + } /// /// 管理按键状态 /// - internal class KeyEventService : IKeyEventService + public class KeyEventService : IKeyEventService { /// /// 按键按下 /// - public event KeyDownEventHandler KeyDown; + public event KeyDownEventHandler OnKeyDown; /// /// 按键松开 /// - public event KeyUpEventHandler KeyUp; + public event KeyUpEventHandler OnKeyUp; public KeyEventService() { @@ -62,18 +70,23 @@ namespace Serein.Workbench.Services { return KeysState[(int)key]; } - public void SetKeyState(Key key, bool state) + + + + + public void KeyDown(Key key) { - if (state) - { - KeyDown?.Invoke(key); - } - else - { - KeyUp?.Invoke(key); - } - //Debug.WriteLine($"按键事件:{key} - {state}"); - KeysState[(int)key] = state; + KeysState[(int)key] = true; + OnKeyDown?.Invoke(key); + Debug.WriteLine($"按键按下事件:{key}"); + } + + public void KeyUp(Key key) + { + KeysState[(int)key] = false; + OnKeyUp?.Invoke(key); + Debug.WriteLine($"按键抬起事件:{key}"); + } } } diff --git a/Workbench/Services/NodeControlService.cs b/Workbench/Services/NodeControlService.cs deleted file mode 100644 index 7654e57..0000000 --- a/Workbench/Services/NodeControlService.cs +++ /dev/null @@ -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 -{ - /// - /// 节点操作相关服务 - /// - 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(); - 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(content); - var projectDfilePath = System.IO.Path.GetDirectoryName(filePath)!; - flowEnvironment.LoadProject(new FlowEnvInfo { Project = projectData }, projectDfilePath); - }, CancellationToken.None);*/ - } - - - - - - } - -} diff --git a/Workbench/Services/WorkbenchEventService.cs b/Workbench/Services/WorkbenchEventService.cs index 4469620..73422db 100644 --- a/Workbench/Services/WorkbenchEventService.cs +++ b/Workbench/Services/WorkbenchEventService.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.IO; using System.Linq; +using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; using System.Text; using System.Threading.Tasks; @@ -71,9 +72,15 @@ namespace Serein.Workbench.Services private void InitEvents() { flowEEForwardingService.OnProjectSaving += SaveProjectToLocalFile; + flowEEForwardingService.OnEnvOut += FlowEEForwardingService_OnEnvOut; } - + private void FlowEEForwardingService_OnEnvOut(InfoType type, string value) + { + LogWindow.Instance.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}"); + } + + /// /// 预览了某个方法信息(待创建) @@ -103,6 +110,8 @@ namespace Serein.Workbench.Services private void SaveProjectToLocalFile(ProjectSavingEventArgs e) { var project = e.ProjectData; + + #region 获取保存路径 // 创建一个新的保存文件对话框 SaveFileDialog saveFileDialog = new() { @@ -128,8 +137,10 @@ namespace Serein.Workbench.Services SereinEnv.WriteLine(InfoType.ERROR, "保存项目DLL时返回了意外的文件保存路径"); return; } + #endregion + #region 将Dll输出到指定路径 Uri saveProjectFileUri = new Uri(savePath); SereinEnv.WriteLine(InfoType.INFO, "项目文件保存路径:" + savePath); for (int index = 0; index < project.Librarys.Length; index++) @@ -180,12 +191,104 @@ namespace Serein.Workbench.Services } } + #endregion + #region 输出项目保存文件 JObject projectJsonData = JObject.FromObject(project); File.WriteAllText(savePath, projectJsonData.ToString()); + #endregion + } + + + + } } + +#region 抽取重复文件(例如net运行时) +/* + + 1. 扫描目录并计算哈希 +string[] directories = new[] { "path1", "path2" }; +var fileHashMap = new Dictionary>(); // hash -> List + +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(); + + fileHashMap[hash].Add(file); + } +} + +2. 将重复文件压缩并保存 +string archiveDir = "compressed_output"; +Directory.CreateDirectory(archiveDir); + +var manifest = new List(); + +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 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>(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 + diff --git a/Workbench/Themes/NodeTreeItemViewControl.xaml.cs b/Workbench/Themes/NodeTreeItemViewControl.xaml.cs index ffd9711..09f4d06 100644 --- a/Workbench/Themes/NodeTreeItemViewControl.xaml.cs +++ b/Workbench/Themes/NodeTreeItemViewControl.xaml.cs @@ -1,6 +1,7 @@ using Serein.Library; using Serein.Library.Api; using Serein.Library.Utils; +using Serein.Workbench.Tool; using System.Windows; using System.Windows.Controls; @@ -137,11 +138,11 @@ namespace Serein.Workbench.Themes treeViewItem.Expanded += TreeViewItem_Expanded; var contextMenu = new ContextMenu(); - contextMenu.Items.Add(MainWindow.CreateMenuItem("从此节点执行", async (s, e) => + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("从此节点执行", async (s, e) => { try { - await flowEnvironment.StartAsyncInSelectNode(tmpNodeTreeModel.RootNode.Guid); + await flowEnvironment.StartFlowFromSelectNodeAsync(tmpNodeTreeModel.RootNode.Guid); } catch (Exception ex) { @@ -149,7 +150,7 @@ namespace Serein.Workbench.Themes 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.Margin = new Thickness(-20, 0, 0, 0); diff --git a/Workbench/Themes/ObjectViewerControl.xaml.cs b/Workbench/Themes/ObjectViewerControl.xaml.cs index d3d68ab..2edf486 100644 --- a/Workbench/Themes/ObjectViewerControl.xaml.cs +++ b/Workbench/Themes/ObjectViewerControl.xaml.cs @@ -1,6 +1,7 @@ using Newtonsoft.Json.Linq; using Serein.Library.Api; using Serein.Library.Utils.SereinExpression; +using Serein.Workbench.Tool; using System; using System.Collections; using System.Collections.Generic; @@ -294,7 +295,7 @@ namespace Serein.Workbench.Themes // 配置右键菜单 var contextMenu = new ContextMenu(); - contextMenu.Items.Add(MainWindow.CreateMenuItem($"表达式", (s, e) => + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem($"表达式", (s, e) => { ExpressionTextBox.Text = subPath; // 获取表达式 diff --git a/Workbench/Themes/TypeViewerWindow.xaml.cs b/Workbench/Themes/TypeViewerWindow.xaml.cs index 7c390bd..a84d13e 100644 --- a/Workbench/Themes/TypeViewerWindow.xaml.cs +++ b/Workbench/Themes/TypeViewerWindow.xaml.cs @@ -1,4 +1,5 @@ -using System; +using Serein.Workbench.Tool; +using System; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -165,7 +166,7 @@ namespace Serein.Workbench.Themes { isChange = true; contextMenu = new ContextMenu(); - contextMenu.Items.Add(MainWindow.CreateMenuItem($"取值表达式", (s, e) => + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem($"取值表达式", (s, e) => { string fullPath = GetNodeFullPath(memberNode); string copyValue = "@Get " + fullPath; @@ -181,7 +182,7 @@ namespace Serein.Workbench.Themes { isChange = true; contextMenu = new ContextMenu(); - contextMenu.Items.Add(MainWindow.CreateMenuItem($"取值表达式", (s, e) => + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem($"取值表达式", (s, e) => { string fullPath = GetNodeFullPath(memberNode); string copyValue = "@Get " + fullPath; diff --git a/Workbench/Tool/WpfFuncTool.cs b/Workbench/Tool/WpfFuncTool.cs new file mode 100644 index 0000000..7fd171e --- /dev/null +++ b/Workbench/Tool/WpfFuncTool.cs @@ -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 + { /// + /// 创建菜单子项 + /// + /// + /// + /// + public static MenuItem CreateMenuItem(string header, RoutedEventHandler handler) + { + var menuItem = new MenuItem { Header = header }; + menuItem.Click += handler; + return menuItem; + } + + + + /// + /// 穿透元素获取区域容器 + /// + /// + /// + /// + public static T? GetParentOfType(DependencyObject element) where T : DependencyObject + { + while (element != null) + { + if (element is T e) + { + return e; + } + element = VisualTreeHelper.GetParent(element); + } + return null; + } + + } +} diff --git a/Workbench/ViewModels/FlowCanvasViewModel.cs b/Workbench/ViewModels/FlowCanvasViewModel.cs index b8dc077..be49fe7 100644 --- a/Workbench/ViewModels/FlowCanvasViewModel.cs +++ b/Workbench/ViewModels/FlowCanvasViewModel.cs @@ -19,9 +19,9 @@ namespace Serein.Workbench.ViewModels { /// - /// 画布当前选中的节点 + /// 画布当前的节点 /// - public NodeControlBase CurrentSelectNode { get; set; } + public Dictionary NodeControls { get; set; } = []; /// /// 正在创建节点方法调用关系 diff --git a/Workbench/ViewModels/FlowEditViewModel.cs b/Workbench/ViewModels/FlowEditViewModel.cs index 029b23e..45a4d33 100644 --- a/Workbench/ViewModels/FlowEditViewModel.cs +++ b/Workbench/ViewModels/FlowEditViewModel.cs @@ -23,9 +23,6 @@ namespace Serein.Workbench.ViewModels public partial class FlowEditViewModel : ObservableObject { public ObservableCollection CanvasTabs { get; set; } = []; - public ICommand AddTabCommand { get; set; } - public ICommand RemoveTabCommand { get; set; } - public ICommand RenameTabCommand { get; set; } /// @@ -39,8 +36,7 @@ namespace Serein.Workbench.ViewModels public FlowEditViewModel(FlowNodeService flowNodeService) { this.flowNodeService = flowNodeService; - AddTabCommand = new RelayCommand(AddTab); - RemoveTabCommand = new RelayCommand(RemoveTab); + flowNodeService.OnCreateFlowCanvasView += OnCreateFlowCanvasView; // 创建了画布 flowNodeService.OnRemoveFlowCanvasView += OnRemoveFlowCanvasView; // 移除了画布 @@ -81,13 +77,6 @@ namespace Serein.Workbench.ViewModels #endregion - private void AddTab() => flowNodeService.CreateFlowCanvas(); - private void RemoveTab() - { - if (CanvasTabs.Count > 0 && SelectedTab != null) flowNodeService.RemoveFlowCanvas(); - } - - /// diff --git a/Workbench/ViewModels/FlowLibrarysViewModel.cs b/Workbench/ViewModels/FlowLibrarysViewModel.cs index d36d093..52a7bdf 100644 --- a/Workbench/ViewModels/FlowLibrarysViewModel.cs +++ b/Workbench/ViewModels/FlowLibrarysViewModel.cs @@ -1,5 +1,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using Serein.Library; +using Serein.Library.Api; +using Serein.NodeFlow.Env; using Serein.Workbench.Api; using Serein.Workbench.Models; using Serein.Workbench.Services; @@ -15,16 +17,33 @@ namespace Serein.Workbench.ViewModels internal partial class FlowLibrarysViewModel : ObservableObject { private readonly IFlowEEForwardingService flowEEForwardingService; - + private readonly IFlowEnvironment flowEnvironment; [ObservableProperty] private ObservableCollection flowLibraryInfos; - public FlowLibrarysViewModel(IFlowEEForwardingService flowEEForwardingService) + public FlowLibrarysViewModel(IFlowEEForwardingService flowEEForwardingService,IFlowEnvironment flowEnvironment) { this.flowEEForwardingService = flowEEForwardingService; + this.flowEnvironment = flowEnvironment; FlowLibraryInfos = new ObservableCollection(); flowEEForwardingService.OnDllLoad += FlowEEForwardingService_OnDllLoad; } + /// + /// 加载文件依赖 + /// + /// + 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) { @@ -32,10 +51,10 @@ namespace Serein.Workbench.ViewModels List mds = eventArgs.MethodDetailss; NodeLibraryInfo libraryInfo = eventArgs.NodeLibraryInfo; - var methodInfo = new ObservableCollection(); + var methodInfo = new ObservableCollection(); foreach (var md in mds) { - methodInfo.Add(new FlowLibraryMethodDetailsInfo(md)); + methodInfo.Add(md); } var flInfo = new FlowLibraryInfo { diff --git a/Workbench/ViewModels/FlowWorkbenchViewModel.cs b/Workbench/ViewModels/FlowWorkbenchViewModel.cs index 47fb4dd..b7a352c 100644 --- a/Workbench/ViewModels/FlowWorkbenchViewModel.cs +++ b/Workbench/ViewModels/FlowWorkbenchViewModel.cs @@ -1,4 +1,6 @@ using CommunityToolkit.Mvvm.ComponentModel; +using Serein.Library.Api; +using Serein.NodeFlow.Env; using Serein.Workbench.Api; using Serein.Workbench.Models; using Serein.Workbench.Services; @@ -8,19 +10,36 @@ using System.Collections.ObjectModel; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; namespace Serein.Workbench.ViewModels { internal partial class FlowWorkbenchViewModel : ObservableObject { - private readonly IFlowEEForwardingService flowEEForwardingService; + private readonly IFlowEnvironment flowEnvironment; 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; - //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); + } + + } } diff --git a/Workbench/ViewModels/MainMenuBarViewModel.cs b/Workbench/ViewModels/MainMenuBarViewModel.cs index 6bf28ff..fa08066 100644 --- a/Workbench/ViewModels/MainMenuBarViewModel.cs +++ b/Workbench/ViewModels/MainMenuBarViewModel.cs @@ -1,6 +1,7 @@ using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using Serein.Library.Api; +using Serein.Workbench.Services; using System.Windows.Input; namespace Serein.Workbench.ViewModels @@ -8,6 +9,7 @@ namespace Serein.Workbench.ViewModels public class MainMenuBarViewModel : ObservableObject { private readonly IFlowEnvironment environment; + private readonly FlowNodeService flowNodeService; /// /// 保存项目 @@ -31,6 +33,10 @@ namespace Serein.Workbench.ViewModels /// public ICommand RemoteFlowCanvasCommand { get; private set; } + /// + /// 运行当前画布流程 + /// + public ICommand StartFlowCommand { get; private set; } /// /// 运行当前画布流程 /// @@ -55,10 +61,10 @@ namespace Serein.Workbench.ViewModels - public MainMenuBarViewModel(IFlowEnvironment environment) + public MainMenuBarViewModel(IFlowEnvironment environment, FlowNodeService flowNodeService) { this.environment = environment; - + this.flowNodeService = flowNodeService; SaveProjectCommand = new RelayCommand(SaveProject); // 保存项目 LoadLocalProjectCommand = new RelayCommand(LoadLocalProject); // 加载本地项目 LoadRemoteProjectCommand = new RelayCommand(LoadRemoteProject); // 加载远程项目 @@ -66,26 +72,30 @@ namespace Serein.Workbench.ViewModels CreateFlowCanvasCommand = new RelayCommand(CreateFlowCanvas); // 增加画布 RemoteFlowCanvasCommand = new RelayCommand(RemoteFlowCanvas); // 移除画布 - StartCurrentCanvasFlowCommand = new RelayCommand(StartCurrentCanvasFlow); // 运行当前流程 + StartFlowCommand = new RelayCommand(StartFlow); + StartCurrentCanvasFlowCommand = new RelayCommand(StartCurrentCanvasFlow); // 运行当前所查看画布的流程 StopCurrentCanvasFlowCommand = new RelayCommand(StopCurrentCanvasFlow); // 停止当前流程 OpenEnvOutWindowCommand = new RelayCommand(OpenEnvOutWindow); // 打开运行输出窗口 OpenDynamicCompilerCommand = new RelayCommand(OpenDynamicCompiler); // 打开动态编译仓库窗口 } - private void SaveProject() { - environment.SaveProject(); // 保存项目 - } + private void SaveProject() => environment.SaveProject(); // 保存项目 private void LoadLocalProject() { //environment.LoadProject(); // 加载项目 } - private void LoadRemoteProject() { } - private void CreateFlowCanvas() { } - private void RemoteFlowCanvas() { } - private void StartCurrentCanvasFlow() { } + private void LoadRemoteProject() + { + } + 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 OpenDynamicCompiler() { } - private void OpenEnvOutWindow() { } + private void OpenEnvOutWindow() => LogWindow.Instance?.Show(); } } diff --git a/Workbench/ViewModels/MainViewModel.cs b/Workbench/ViewModels/MainViewModel.cs index 29c31bf..2391529 100644 --- a/Workbench/ViewModels/MainViewModel.cs +++ b/Workbench/ViewModels/MainViewModel.cs @@ -1,17 +1,25 @@ using CommunityToolkit.Mvvm.ComponentModel; +using Serein.Workbench.Services; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using System.Windows.Input; +using System.Windows; namespace Serein.Workbench.ViewModels { public class MainViewModel : ObservableObject { - public MainViewModel() + private readonly IKeyEventService keyEventService; + + public MainViewModel(IKeyEventService keyEventService) { + this.keyEventService = keyEventService; } + + } } diff --git a/Workbench/Views/BaseNodesView.xaml.cs b/Workbench/Views/BaseNodesView.xaml.cs index 4225698..a4423c0 100644 --- a/Workbench/Views/BaseNodesView.xaml.cs +++ b/Workbench/Views/BaseNodesView.xaml.cs @@ -1,4 +1,5 @@ using Serein.Library; +using Serein.Workbench.Customs; using Serein.Workbench.ViewModels; using System; using System.Collections.Generic; diff --git a/Workbench/Views/FlowCanvasView.xaml b/Workbench/Views/FlowCanvasView.xaml index f92bd40..5c24964 100644 --- a/Workbench/Views/FlowCanvasView.xaml +++ b/Workbench/Views/FlowCanvasView.xaml @@ -23,7 +23,7 @@ public partial class FlowCanvasView : UserControl, IFlowCanvas { - + private readonly IFlowEnvironment flowEnvironment; + private readonly IKeyEventService keyEventService; private readonly FlowNodeService flowNodeService; + /// /// 存储所有的连接。考虑集成在运行环境中。 /// @@ -44,7 +60,6 @@ namespace Serein.Workbench.Views private FlowCanvasViewModel ViewModel => this.DataContext as FlowCanvasViewModel ?? throw new ArgumentNullException(); - #region 画布接口实现 private IFlowCanvas Api => this; @@ -57,9 +72,10 @@ namespace Serein.Workbench.Views } public string Name => ViewModel.Model.Name; - + FlowCanvasDetails IFlowCanvas.Model => ViewModel.Model; void IFlowCanvas.Remove(NodeControlBase nodeControl) { + ViewModel.NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid); FlowChartCanvas.Dispatcher.Invoke(() => { FlowChartCanvas.Children.Remove(nodeControl); @@ -67,11 +83,15 @@ namespace Serein.Workbench.Views } void IFlowCanvas.Add(NodeControlBase nodeControl) { + ViewModel.NodeControls.TryAdd(nodeControl.ViewModel.NodeModel.Guid, nodeControl); FlowChartCanvas.Dispatcher.Invoke(() => { FlowChartCanvas.Children.Add(nodeControl); }); + ConfigureNodeEvents(nodeControl); // 配置相关事件 + ConfigureContextMenu(nodeControl); // 添加右键菜单 + } void IFlowCanvas.CreateInvokeConnection(NodeControlBase fromNodeControl, NodeControlBase toNodeControl, ConnectionInvokeType type) @@ -224,6 +244,9 @@ namespace Serein.Workbench.Views /// 标记是否正在拖动画布 /// private bool IsCanvasDragging; + /// + /// 是否正在选取控件 + /// private bool IsSelectDragging; /// @@ -258,8 +281,7 @@ namespace Serein.Workbench.Views private readonly TranslateTransform translateTransform; #endregion - - #region 初始化 + #region 初始化以及相关事件 public FlowCanvasView(FlowCanvasDetails model) { @@ -270,9 +292,9 @@ namespace Serein.Workbench.Views flowEnvironment = App.GetService(); flowNodeService = App.GetService(); - + keyEventService = App.GetService(); flowNodeService.OnCreateNode += OnCreateNode; - + keyEventService.OnKeyDown += KeyEventService_OnKeyDown; ; // 缩放平移容器 canvasTransformGroup = new TransformGroup(); @@ -286,6 +308,10 @@ namespace Serein.Workbench.Views } + /// + /// 设置绑定 + /// + /// private void SetBinding(FlowCanvasDetails canvasModel) { Binding bindingScaleX = new(nameof(canvasModel.ScaleX)) { Source = canvasModel, Mode = BindingMode.TwoWay }; @@ -325,8 +351,7 @@ namespace Serein.Workbench.Views { // 并非添加在容器中,直接放置节点 Api.Add(nodeControl); // 添加到对应的画布上 - ConfigureNodeEvents(nodeControl); // 添加了节点 - ConfigureContextMenu(nodeControl); // 添加右键菜单 + } } @@ -349,7 +374,7 @@ namespace Serein.Workbench.Views // 准备放置条件表达式控件 if (nodeControl.ViewModel.NodeModel.ControlType == NodeControlType.ExpCondition) { - ConditionRegionControl? conditionRegion = GetParentOfType(hitElement); + ConditionRegionControl? conditionRegion = WpfFuncTool.GetParentOfType(hitElement); if (conditionRegion is not null) { targetNodeControl = conditionRegion; @@ -362,7 +387,7 @@ namespace Serein.Workbench.Views else { // 准备放置全局数据控件 - GlobalDataControl? globalDataControl = GetParentOfType(hitElement); + GlobalDataControl? globalDataControl = WpfFuncTool.GetParentOfType(hitElement); if (globalDataControl is not null) { targetNodeControl = globalDataControl; @@ -376,6 +401,107 @@ namespace Serein.Workbench.Views #endregion + #region 画布键盘操作 + /// + /// 监听按键事件 + /// + /// + 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 画布鼠标操作 + /// /// 鼠标在画布移动。 @@ -385,11 +511,11 @@ namespace Serein.Workbench.Views /// private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e) { - var myData = GlobalJunctionData.MyGlobalConnectingData; - if (myData.IsCreateing && e.LeftButton == MouseButtonState.Pressed) + var cd = flowNodeService.ConnectingData; + if (cd.IsCreateing && e.LeftButton == MouseButtonState.Pressed) { - if (myData.Type == JunctionOfConnectionType.Invoke) + if (cd.Type == JunctionOfConnectionType.Invoke) { ViewModel.IsConnectionInvokeNode = true; // 正在连接节点的调用关系 @@ -401,7 +527,7 @@ namespace Serein.Workbench.Views var currentPoint = e.GetPosition(FlowChartCanvas); currentPoint.X -= 2; currentPoint.Y -= 2; - myData.UpdatePoint(currentPoint); + cd.UpdatePoint(currentPoint); return; } @@ -456,10 +582,10 @@ namespace Serein.Workbench.Views PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y); 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.CurrentDragMdInfo = nodeData.MethodDetailsInfo; // 基础节点不需要参数信息 + flowNodeService.CurrentNodeControlType = nodeModel.NodeControlType; // 设置基础节点类型 + flowNodeService.CurrentDragMdInfo = nodeModel.MethodDetailsInfo; // 基础节点不需要参数信息 flowNodeService.CurrentMouseLocation = position; // 设置当前鼠标为止 flowNodeService.CreateNode(); // 创建来自DLL加载的方法节点 } @@ -524,7 +650,8 @@ namespace Serein.Workbench.Views /// private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) { - if (GlobalJunctionData.MyGlobalConnectingData.IsCreateing) + + if (flowNodeService.ConnectingData.IsCreateing) { 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 (myData.IsCanConnected) + if (cd.IsCanConnected) { var canvas = this.FlowChartCanvas; var currentendPoint = e.GetPosition(canvas); // 当前鼠标落点 - var changingJunctionPosition = myData.CurrentJunction.TranslatePoint(new Point(0, 0), canvas); - var changingJunctionRect = new Rect(changingJunctionPosition, new Size(myData.CurrentJunction.Width, myData.CurrentJunction.Height)); + var changingJunctionPosition = cd.CurrentJunction.TranslatePoint(new Point(0, 0), canvas); + var changingJunctionRect = new Rect(changingJunctionPosition, new Size(cd.CurrentJunction.Width, cd.CurrentJunction.Height)); if (changingJunctionRect.Contains(currentendPoint)) // 可以创建连接 { #region 方法调用关系创建 - if (myData.Type == JunctionOfConnectionType.Invoke) + if (cd.Type == JunctionOfConnectionType.Invoke) { var canvasGuid = this.Guid; await flowEnvironment.ConnectInvokeNodeAsync( canvasGuid, - myData.StartJunction.MyNode.Guid, - myData.CurrentJunction.MyNode.Guid, - myData.StartJunction.JunctionType, - myData.CurrentJunction.JunctionType, - myData.ConnectionInvokeType); + cd.StartJunction.MyNode.Guid, + cd.CurrentJunction.MyNode.Guid, + cd.StartJunction.JunctionType, + cd.CurrentJunction.JunctionType, + cd.ConnectionInvokeType); } #endregion #region 参数来源关系创建 - else if (myData.Type == JunctionOfConnectionType.Arg) + else if (cd.Type == JunctionOfConnectionType.Arg) { var argIndex = 0; - if (myData.StartJunction is ArgJunctionControl argJunction1) + if (cd.StartJunction is ArgJunctionControl argJunction1) { argIndex = argJunction1.ArgIndex; } - else if (myData.CurrentJunction is ArgJunctionControl argJunction2) + else if (cd.CurrentJunction is ArgJunctionControl argJunction2) { argIndex = argJunction2.ArgIndex; } @@ -623,11 +750,11 @@ namespace Serein.Workbench.Views await flowEnvironment.ConnectArgSourceNodeAsync( canvasGuid, - myData.StartJunction.MyNode.Guid, - myData.CurrentJunction.MyNode.Guid, - myData.StartJunction.JunctionType, - myData.CurrentJunction.JunctionType, - myData.ConnectionArgSourceType, + cd.StartJunction.MyNode.Guid, + cd.CurrentJunction.MyNode.Guid, + cd.StartJunction.JunctionType, + cd.CurrentJunction.JunctionType, + cd.ConnectionArgSourceType, argIndex); } #endregion @@ -640,7 +767,14 @@ namespace Serein.Workbench.Views } + #region 拖动画布实现缩放平移效果 + + /// + /// 开始拖动画布 + /// + /// + /// private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e) { IsCanvasDragging = true; @@ -649,11 +783,13 @@ namespace Serein.Workbench.Views e.Handled = true; // 防止事件传播影响其他控件 } + /// + /// 停止拖动 + /// + /// + /// private void FlowChartCanvas_MouseUp(object sender, MouseButtonEventArgs e) { - - - if (IsCanvasDragging) { IsCanvasDragging = false; @@ -661,7 +797,11 @@ namespace Serein.Workbench.Views } } - // 单纯缩放画布,不改变画布大小 + /// + /// 单纯缩放画布,不改变画布大小 + /// + /// + /// private void FlowChartCanvas_MouseWheel(object sender, MouseWheelEventArgs e) { // if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl)) @@ -820,6 +960,9 @@ namespace Serein.Workbench.Views #endregion #endregion + #endregion + + #region 私有方法 /// /// 完成选取操作 @@ -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(); + } + /// /// 结束连接操作,清理状态并移除虚线。 /// @@ -895,21 +1059,9 @@ namespace Serein.Workbench.Views Mouse.OverrideCursor = null; // 恢复视觉效果 ViewModel.IsConnectionArgSourceNode = false; ViewModel.IsConnectionInvokeNode = false; - GlobalJunctionData.OK(); + flowNodeService.ConnectingData.Reset(); } - /// - /// 创建菜单子项 - /// - /// - /// - /// - public static MenuItem CreateMenuItem(string header, RoutedEventHandler handler) - { - var menuItem = new MenuItem { Header = header }; - menuItem.Click += handler; - return menuItem; - } /// /// 选择范围配置 @@ -918,7 +1070,7 @@ namespace Serein.Workbench.Views private ContextMenu ConfiguerSelectionRectangle() { var contextMenu = new ContextMenu(); - contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除", (s, e) => { if (selectNodeControls.Count > 0) { @@ -938,6 +1090,10 @@ namespace Serein.Workbench.Views // nodeControl.ContextMenu = contextMenu; } + + + #endregion + #region 节点控件相关事件 /// /// 配置节点事件(移动,点击相关) @@ -950,13 +1106,7 @@ namespace Serein.Workbench.Views nodeControl.MouseMove += Block_MouseMove; 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; startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置 ((UIElement)sender).CaptureMouse(); // 捕获鼠标 - e.Handled = true; // 防止事件传播影响其他控件 } + e.Handled = true; // 防止事件传播影响其他控件 } /// @@ -981,12 +1131,13 @@ namespace Serein.Workbench.Views /// private void Block_MouseMove(object sender, MouseEventArgs e) { + if (IsCanvasDragging) return; if (IsSelectControl) return; - if (IsControlDragging) // 如果正在拖动控件 + if (IsControlDragging && !flowNodeService.ConnectingData.IsCreateing) // 如果正在拖动控件 { Point currentPosition = e.GetPosition(FlowChartCanvas); // 获取当前鼠标位置 @@ -1005,21 +1156,26 @@ namespace Serein.Workbench.Views var newLeft = oldLeft + deltaX; var newTop = oldTop + deltaY; - this.flowEnvironment.MoveNode(Guid, nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点 - // 计算控件实际移动的距离 var actualDeltaX = newLeft - oldLeft; var actualDeltaY = newTop - oldTop; - // 移动其它选中的控件 - foreach (var nodeControl in selectNodeControls) + List<(string Guid, double NewLeft, double NewTop, double MaxWidth, double MaxHeight)> moveSizes = + 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) // 跳过已经移动的控件 - { - var otherNewLeft = Canvas.GetLeft(nodeControl) + actualDeltaX; - var otherNewTop = Canvas.GetTop(nodeControl) + actualDeltaY; - this.flowEnvironment.MoveNode(Guid, nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点 - } + return; + } + foreach (var item in moveSizes) + { + 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 newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距 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); // 移动节点 nodeControl.UpdateLocationConnections(); } @@ -1095,7 +1259,7 @@ namespace Serein.Workbench.Views 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) { @@ -1119,7 +1283,7 @@ namespace Serein.Workbench.Views 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); })); @@ -1127,8 +1291,8 @@ namespace Serein.Workbench.Views - contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => flowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid))); - contextMenu.Items.Add(CreateMenuItem("删除", async (s, e) => + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("设为起点", (s, e) => flowEnvironment.SetStartNodeAsync(canvasGuid, nodeGuid))); + contextMenu.Items.Add(WpfFuncTool.CreateMenuItem("删除", async (s, e) => { var result = await flowEnvironment.RemoveNodeAsync(canvasGuid, nodeGuid); })); @@ -1136,28 +1300,28 @@ namespace Serein.Workbench.Views #region 右键菜单功能 - 控件对齐 var AvoidMenu = new MenuItem(); - AvoidMenu.Items.Add(CreateMenuItem("群组对齐", (s, e) => + AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("群组对齐", (s, e) => { AlignControlsWithGrouping(selectNodeControls, AlignMode.Grouping); })); - AvoidMenu.Items.Add(CreateMenuItem("规划对齐", (s, e) => + AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("规划对齐", (s, e) => { AlignControlsWithGrouping(selectNodeControls, AlignMode.Planning); })); - AvoidMenu.Items.Add(CreateMenuItem("水平中心对齐", (s, e) => + AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("水平中心对齐", (s, e) => { AlignControlsWithGrouping(selectNodeControls, AlignMode.HorizontalCenter); })); - AvoidMenu.Items.Add(CreateMenuItem("垂直中心对齐 ", (s, e) => + AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("垂直中心对齐 ", (s, e) => { AlignControlsWithGrouping(selectNodeControls, AlignMode.VerticalCenter); })); - AvoidMenu.Items.Add(CreateMenuItem("垂直对齐时水平斜分布", (s, e) => + AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("垂直对齐时水平斜分布", (s, e) => { AlignControlsWithGrouping(selectNodeControls, AlignMode.Vertical); })); - AvoidMenu.Items.Add(CreateMenuItem("水平对齐时垂直斜分布", (s, e) => + AvoidMenu.Items.Add(WpfFuncTool.CreateMenuItem("水平对齐时垂直斜分布", (s, e) => { AlignControlsWithGrouping(selectNodeControls, AlignMode.Horizontal); })); diff --git a/Workbench/Views/FlowEditView.xaml b/Workbench/Views/FlowEditView.xaml index cdfb90e..d6538d0 100644 --- a/Workbench/Views/FlowEditView.xaml +++ b/Workbench/Views/FlowEditView.xaml @@ -56,11 +56,11 @@ AllowDrop="True"--> - + - + --> diff --git a/Workbench/Views/FlowEditView.xaml.cs b/Workbench/Views/FlowEditView.xaml.cs index b08663e..3f7c17f 100644 --- a/Workbench/Views/FlowEditView.xaml.cs +++ b/Workbench/Views/FlowEditView.xaml.cs @@ -126,12 +126,11 @@ namespace Serein.Workbench.Views private void TabControl_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (sender is TabControl tabControl - && tabControl.SelectedIndex > 0 + && tabControl.SelectedIndex > -1 && DataContext is FlowEditViewModel viewModel && viewModel.CanvasTabs[tabControl.SelectedIndex] is FlowEditorTabModel tab) { - - viewModel.EndEditingTab(lastTab); // 确认新名称 + viewModel.EndEditingTab(lastTab); // 取消编辑 lastTab = tab; return; } diff --git a/Workbench/Views/FlowLibrarysView.xaml b/Workbench/Views/FlowLibrarysView.xaml index 8c86de0..b4c89f2 100644 --- a/Workbench/Views/FlowLibrarysView.xaml +++ b/Workbench/Views/FlowLibrarysView.xaml @@ -9,11 +9,24 @@ xmlns:vm="clr-namespace:Serein.Workbench.ViewModels" mc:Ignorable="d" 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" > + + + @@ -31,58 +44,27 @@ - - - - + + + + @@ -90,3 +72,9 @@ + + + + \ No newline at end of file diff --git a/Workbench/Views/FlowLibrarysView.xaml.cs b/Workbench/Views/FlowLibrarysView.xaml.cs index cd9f80a..a21141a 100644 --- a/Workbench/Views/FlowLibrarysView.xaml.cs +++ b/Workbench/Views/FlowLibrarysView.xaml.cs @@ -1,4 +1,5 @@ -using Serein.Workbench.ViewModels; +using Serein.Workbench.Node.ViewModel; +using Serein.Workbench.ViewModels; using System; using System.Collections.Generic; using System.Linq; @@ -21,10 +22,32 @@ namespace Serein.Workbench.Views /// public partial class FlowLibrarysView : UserControl { + private FlowLibrarysViewModel ViewModel => DataContext as FlowLibrarysViewModel ?? throw new ArgumentNullException(); public FlowLibrarysView() { this.DataContext = App.GetService().FlowLibrarysViewModel; 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; + } } } diff --git a/Workbench/Views/FlowWorkbenchView.xaml b/Workbench/Views/FlowWorkbenchView.xaml index 051570f..75fde79 100644 --- a/Workbench/Views/FlowWorkbenchView.xaml +++ b/Workbench/Views/FlowWorkbenchView.xaml @@ -6,7 +6,8 @@ xmlns:local="clr-namespace:Serein.Workbench.Views" mc:Ignorable="d" Loaded="Window_Loaded" - Title="FlowWorkbenchView" Height="450" Width="800"> + Title="FlowWorkbenchView" Height="450" Width="800" + Closing="Window_Closing"> @@ -23,7 +24,7 @@ - + @@ -31,10 +32,13 @@ - + - + + + + diff --git a/Workbench/Views/FlowWorkbenchView.xaml.cs b/Workbench/Views/FlowWorkbenchView.xaml.cs index b667cb5..c6b6125 100644 --- a/Workbench/Views/FlowWorkbenchView.xaml.cs +++ b/Workbench/Views/FlowWorkbenchView.xaml.cs @@ -20,6 +20,7 @@ namespace Serein.Workbench.Views /// public partial class FlowWorkbenchView : Window { + private FlowWorkbenchViewModel ViewModel => ViewModel as FlowWorkbenchViewModel; public FlowWorkbenchView() { this.DataContext = App.GetService().FlowWorkbenchViewModel; @@ -40,5 +41,12 @@ namespace Serein.Workbench.Views this.Width = System.Windows.SystemParameters.PrimaryScreenWidth; this.Height = System.Windows.SystemParameters.PrimaryScreenHeight;*/ } + + private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) + { + // 确保所有窗口关闭 + LogWindow.Instance.Close(); + System.Windows.Application.Current.Shutdown(); + } } } diff --git a/Workbench/Views/MainMenuBarView.xaml b/Workbench/Views/MainMenuBarView.xaml index 219e5da..1b6012e 100644 --- a/Workbench/Views/MainMenuBarView.xaml +++ b/Workbench/Views/MainMenuBarView.xaml @@ -16,19 +16,22 @@ - - - + + + + - - + + + + - +