From 2168c5ec6652534ecd085078df8fff875ac7e89f Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Tue, 18 Mar 2025 21:01:15 +0800
Subject: [PATCH] =?UTF-8?q?=E9=87=8D=E6=9E=84=E4=B8=ADday1=EF=BC=8C?=
=?UTF-8?q?=E5=86=99=E4=BA=86=E5=BE=88=E5=A4=9A=EF=BC=8C=E4=B8=8D=E7=9F=A5?=
=?UTF-8?q?=E9=81=93=E6=80=8E=E4=B9=88=E8=AF=B4=E6=B8=85=E6=A5=9A?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../Services/NodeOperationService.cs | 4 +-
Workbench/Api/IFlowEEForwardingService.cs | 17 +
Workbench/Api/INodeContainerControl.cs | 32 +
Workbench/Api/INodeControl.cs | 23 +
Workbench/Api/INodeJunction.cs | 51 ++
Workbench/App.xaml | 3 +-
Workbench/App.xaml.cs | 99 ++-
.../Converters/CountToVisibilityConverter.cs | 33 +
Workbench/Customs/FlowMethodInfoListBox.xaml | 29 +
.../Customs/FlowMethodInfoListBox.xaml.cs | 100 +++
Workbench/Models/FlowLibraryInfo.cs | 44 ++
Workbench/Serein.WorkBench.csproj | 7 +-
Workbench/Services/FlowEEForwardingService.cs | 344 +++++++++
Workbench/Services/KeyEventService.cs | 80 ++
Workbench/Services/NodeOperationService.cs | 406 +++++++++++
Workbench/Services/ProjectService.cs | 13 +
Workbench/Services/WorkbenchEventService.cs | 90 +++
Workbench/ViewModels/BaseNodesViewModel.cs | 13 +
Workbench/ViewModels/FlowCanvasViewModel.cs | 19 +
Workbench/ViewModels/FlowEditViewModel.cs | 16 +
Workbench/ViewModels/FlowLibrarysViewModel.cs | 54 ++
.../ViewModels/FlowWorkbenchViewModel.cs | 25 +
Workbench/ViewModels/Locator.cs | 40 +
Workbench/ViewModels/MainMenuBarViewModel.cs | 12 +
Workbench/ViewModels/MainViewModel.cs | 17 +
Workbench/Views/BaseNodesView.xaml | 23 +
Workbench/Views/BaseNodesView.xaml.cs | 57 ++
Workbench/Views/FlowCanvasView.xaml | 121 +++
Workbench/Views/FlowCanvasView.xaml.cs | 686 ++++++++++++++++++
Workbench/Views/FlowEditView.xaml | 13 +
Workbench/Views/FlowEditView.xaml.cs | 31 +
Workbench/Views/FlowLibrarysView.xaml | 92 +++
Workbench/Views/FlowLibrarysView.xaml.cs | 30 +
Workbench/Views/FlowWorkbenchView.xaml | 40 +
Workbench/Views/FlowWorkbenchView.xaml.cs | 45 ++
Workbench/Views/MainMenuBarView.xaml | 37 +
Workbench/Views/MainMenuBarView.xaml.cs | 30 +
Workbench/Views/MainView.xaml | 13 +
Workbench/Views/MainView.xaml.cs | 30 +
39 files changed, 2809 insertions(+), 10 deletions(-)
create mode 100644 Workbench/Api/IFlowEEForwardingService.cs
create mode 100644 Workbench/Api/INodeContainerControl.cs
create mode 100644 Workbench/Api/INodeControl.cs
create mode 100644 Workbench/Api/INodeJunction.cs
create mode 100644 Workbench/Converters/CountToVisibilityConverter.cs
create mode 100644 Workbench/Customs/FlowMethodInfoListBox.xaml
create mode 100644 Workbench/Customs/FlowMethodInfoListBox.xaml.cs
create mode 100644 Workbench/Models/FlowLibraryInfo.cs
create mode 100644 Workbench/Services/FlowEEForwardingService.cs
create mode 100644 Workbench/Services/KeyEventService.cs
create mode 100644 Workbench/Services/NodeOperationService.cs
create mode 100644 Workbench/Services/ProjectService.cs
create mode 100644 Workbench/Services/WorkbenchEventService.cs
create mode 100644 Workbench/ViewModels/BaseNodesViewModel.cs
create mode 100644 Workbench/ViewModels/FlowCanvasViewModel.cs
create mode 100644 Workbench/ViewModels/FlowEditViewModel.cs
create mode 100644 Workbench/ViewModels/FlowLibrarysViewModel.cs
create mode 100644 Workbench/ViewModels/FlowWorkbenchViewModel.cs
create mode 100644 Workbench/ViewModels/Locator.cs
create mode 100644 Workbench/ViewModels/MainMenuBarViewModel.cs
create mode 100644 Workbench/ViewModels/MainViewModel.cs
create mode 100644 Workbench/Views/BaseNodesView.xaml
create mode 100644 Workbench/Views/BaseNodesView.xaml.cs
create mode 100644 Workbench/Views/FlowCanvasView.xaml
create mode 100644 Workbench/Views/FlowCanvasView.xaml.cs
create mode 100644 Workbench/Views/FlowEditView.xaml
create mode 100644 Workbench/Views/FlowEditView.xaml.cs
create mode 100644 Workbench/Views/FlowLibrarysView.xaml
create mode 100644 Workbench/Views/FlowLibrarysView.xaml.cs
create mode 100644 Workbench/Views/FlowWorkbenchView.xaml
create mode 100644 Workbench/Views/FlowWorkbenchView.xaml.cs
create mode 100644 Workbench/Views/MainMenuBarView.xaml
create mode 100644 Workbench/Views/MainMenuBarView.xaml.cs
create mode 100644 Workbench/Views/MainView.xaml
create mode 100644 Workbench/Views/MainView.xaml.cs
diff --git a/Serein.Workbench.Avalonia/Services/NodeOperationService.cs b/Serein.Workbench.Avalonia/Services/NodeOperationService.cs
index cc13f59..bb7690b 100644
--- a/Serein.Workbench.Avalonia/Services/NodeOperationService.cs
+++ b/Serein.Workbench.Avalonia/Services/NodeOperationService.cs
@@ -115,7 +115,7 @@ namespace Serein.Workbench.Avalonia.Services
this.feefService = feefService;
feefService.OnNodeCreate += FeefService_OnNodeCreate; // 订阅运行环境创建节点事件
feefService.OnNodeConnectChange += FeefService_OnNodeConnectChange; // 订阅运行环境连接了节点事件
- NodeMVVMManagement.RegisterUI(NodeControlType.Action, typeof(ActionNodeView), typeof(ActionNodeViewModel)); // 注册动作节点
+ flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.Action, typeof(ActionNodeView), typeof(ActionNodeViewModel)); // 注册动作节点
// 手动加载项目
_ = Task.Run(async delegate
{
@@ -184,7 +184,7 @@ namespace Serein.Workbench.Avalonia.Services
SereinEnv.WriteLine(InfoType.WARN, $"OnNodeCreate 事件意外触发,节点Guid重复 - {nodeModel.Guid}");
return;
}
- if (!NodeMVVMManagement.TryGetType(nodeModel.ControlType, out var nodeMVVM))
+ if (!flowEnvironment.NodeMVVMManagement.TryGetType(nodeModel.ControlType, out var nodeMVVM))
{
SereinEnv.WriteLine(InfoType.INFO, $"无法创建{nodeModel.ControlType}节点,节点类型尚未注册。");
return;
diff --git a/Workbench/Api/IFlowEEForwardingService.cs b/Workbench/Api/IFlowEEForwardingService.cs
new file mode 100644
index 0000000..cb0d623
--- /dev/null
+++ b/Workbench/Api/IFlowEEForwardingService.cs
@@ -0,0 +1,17 @@
+using Serein.Library.Api;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Api
+{
+ ///
+ /// 流程事件管理,转发流程运行环境中触发的事件到工作台各个订阅者
+ ///
+ internal interface IFlowEEForwardingService : IFlowEnvironmentEvent
+ {
+
+ }
+}
diff --git a/Workbench/Api/INodeContainerControl.cs b/Workbench/Api/INodeContainerControl.cs
new file mode 100644
index 0000000..4fcc403
--- /dev/null
+++ b/Workbench/Api/INodeContainerControl.cs
@@ -0,0 +1,32 @@
+using Serein.Workbench.Node.View;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Api
+{
+ ///
+ /// 约束具有容器功能的节点控件应该有什么方法
+ ///
+ public interface INodeContainerControl
+ {
+ ///
+ /// 放置一个节点
+ ///
+ ///
+ bool PlaceNode(NodeControlBase nodeControl);
+
+ ///
+ /// 取出一个节点
+ ///
+ ///
+ bool TakeOutNode(NodeControlBase nodeControl);
+
+ ///
+ /// 取出所有节点(用于删除容器)
+ ///
+ void TakeOutAll();
+ }
+}
diff --git a/Workbench/Api/INodeControl.cs b/Workbench/Api/INodeControl.cs
new file mode 100644
index 0000000..06770d2
--- /dev/null
+++ b/Workbench/Api/INodeControl.cs
@@ -0,0 +1,23 @@
+using Serein.Library;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Avalonia.Api
+{
+ //internal interface INodeControl
+ //{
+ // ///
+ // /// 对应的节点实体
+ // ///
+ // NodeModelBase NodeModelBase { get; }
+
+ // ///
+ // /// 初始化使用的方法,设置节点实体
+ // ///
+ // ///
+ // void SetNodeModel(NodeModelBase nodeModel);
+ //}
+}
diff --git a/Workbench/Api/INodeJunction.cs b/Workbench/Api/INodeJunction.cs
new file mode 100644
index 0000000..51a5a48
--- /dev/null
+++ b/Workbench/Api/INodeJunction.cs
@@ -0,0 +1,51 @@
+using Serein.Library;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Avalonia.Api
+{
+
+
+
+ ///
+ /// 约束一个节点应该有哪些控制点
+ ///
+ /*public interface INodeJunction
+ {
+ ///
+ /// 方法执行入口控制点
+ ///
+ NodeJunctionView ExecuteJunction { get; }
+ ///
+ /// 执行完成后下一个要执行的方法控制点
+ ///
+ NodeJunctionView NextStepJunction { get; }
+
+ ///
+ /// 参数节点控制点
+ ///
+ NodeJunctionView[] ArgDataJunction { get; }
+ ///
+ /// 返回值控制点
+ ///
+ NodeJunctionView ReturnDataJunction { get; }
+
+ ///
+ /// 获取目标参数控制点,用于防止wpf释放资源导致找不到目标节点,返回-1,-1的坐标
+ ///
+ ///
+ ///
+ NodeJunctionView GetJunctionOfArgData(int index)
+ {
+ var arr = ArgDataJunction;
+ if (index >= arr.Length)
+ {
+ return null;
+ }
+ return arr[index];
+ }
+ }*/
+}
diff --git a/Workbench/App.xaml b/Workbench/App.xaml
index baa41de..c5398c3 100644
--- a/Workbench/App.xaml
+++ b/Workbench/App.xaml
@@ -2,7 +2,8 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Serein.Workbench"
- StartupUri="MainWindow.xaml"
+ 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 deeb37b..99fb86a 100644
--- a/Workbench/App.xaml.cs
+++ b/Workbench/App.xaml.cs
@@ -1,18 +1,109 @@
-using Newtonsoft.Json;
+using Microsoft.Extensions.DependencyInjection;
+using Newtonsoft.Json;
using Serein.Library;
+using Serein.Library.Api;
using Serein.Library.Utils;
+using Serein.NodeFlow.Env;
+using Serein.Workbench.Api;
+using Serein.Workbench.Services;
+using Serein.Workbench.ViewModels;
using System.Diagnostics;
using System.IO;
using System.Windows;
+using System.Windows.Threading;
namespace Serein.Workbench
{
+ public static class ServiceCollectionExtensions
+ {
+ ///
+ /// 注册ViewModel
+ ///
+ ///
+ public static void AddViewModelServices(this IServiceCollection collection)
+ {
+ collection.AddSingleton(); // 主窗体
+
+ collection.AddSingleton();
+ collection.AddSingleton();
+ collection.AddSingleton();
+ collection.AddSingleton();
+ collection.AddSingleton();
+ collection.AddSingleton();
+
+ collection.AddTransient(); // 依赖信息
+ }
+
+ public static void AddWorkbenchServices(this IServiceCollection collection)
+ {
+ collection.AddSingleton(); // 流程事件管理
+ collection.AddSingleton(); // 流程事件管理
+ collection.AddSingleton(); // 节点操作管理
+ // collection.AddSingleton(); // 按键事件管理
+ //collection.AddSingleton(); // 流程节点控件管理
+ }
+
+
+ ///
+ /// 注册流程接口相关实例
+ ///
+ ///
+ public static void AddFlowServices(this IServiceCollection collection)
+ {
+ #region 创建实例
+ Func getSyncContext = null;
+ Dispatcher.CurrentDispatcher.Invoke(() =>
+ {
+ var uiContext = SynchronizationContext.Current; // 在UI线程上获取UI线程上下文信息
+ if (uiContext is not null)
+ {
+ getSyncContext = () => uiContext;
+ }
+ });
+
+ UIContextOperation? uIContextOperation = null;
+ uIContextOperation = new UIContextOperation(getSyncContext); // 封装一个调用UI线程的工具类
+ var flowEnvironmentDecorator = new FlowEnvironmentDecorator();
+ flowEnvironmentDecorator.SetUIContextOperation(uIContextOperation);
+ collection.AddSingleton(uIContextOperation); // 注册UI线程操作上下文
+ collection.AddSingleton(flowEnvironmentDecorator); // 注册运行环境
+ collection.AddSingleton(flowEnvironmentDecorator); // 注册运行环境事件
+
+ #endregion
+
+ }
+ }
+
+
+
///
/// Interaction logic for App.xaml
///
public partial class App : Application
{
+ private static IServiceProvider? ServiceProvider;
+ public static T GetService() where T : class
+ {
+ 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;
+ _ = Task.Run(async () =>
+ {
+ await Task.Delay(500);
+ await this.LoadLocalProjectAsync();
+ });
+ }
+
+
private async Task LoadLocalProjectAsync()
{
@@ -30,10 +121,14 @@ namespace Serein.Workbench
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
}
+
+
public static SereinProjectData? FlowProjectData { get; set; }
public static string FileDataPath { get; set; } = "";
@@ -66,7 +161,7 @@ namespace Serein.Workbench
}
}
- await this.LoadLocalProjectAsync();
+
}
diff --git a/Workbench/Converters/CountToVisibilityConverter.cs b/Workbench/Converters/CountToVisibilityConverter.cs
new file mode 100644
index 0000000..c264193
--- /dev/null
+++ b/Workbench/Converters/CountToVisibilityConverter.cs
@@ -0,0 +1,33 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Data;
+using System.Windows;
+
+namespace Serein.Workbench.Converters
+{
+ public class CountToVisibilityConverter : IValueConverter
+ {
+ public bool Inverse { get; set; } = false; // 可选:反转逻辑
+
+ public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
+ {
+ if (value is IEnumerable collection)
+ {
+ int count = 0;
+ foreach (var item in collection) count++;
+ bool visible = count > 0;
+ if (Inverse) visible = !visible;
+ return visible ? Visibility.Visible : Visibility.Collapsed;
+ }
+ return Visibility.Collapsed;
+ }
+
+ public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotImplementedException();
+ }
+
+}
diff --git a/Workbench/Customs/FlowMethodInfoListBox.xaml b/Workbench/Customs/FlowMethodInfoListBox.xaml
new file mode 100644
index 0000000..7edc49c
--- /dev/null
+++ b/Workbench/Customs/FlowMethodInfoListBox.xaml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Workbench/Customs/FlowMethodInfoListBox.xaml.cs b/Workbench/Customs/FlowMethodInfoListBox.xaml.cs
new file mode 100644
index 0000000..d5a3606
--- /dev/null
+++ b/Workbench/Customs/FlowMethodInfoListBox.xaml.cs
@@ -0,0 +1,100 @@
+using Serein.Library;
+using Serein.Workbench.Models;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Serein.Workbench.Customs
+{
+
+
+ public class Test: DependencyObject
+ {
+
+ }
+
+ ///
+ /// FlowMethodInfoListBox.xaml 的交互逻辑
+ ///
+ public partial class FlowMethodInfoListBox : UserControl
+ {
+
+
+ public FlowMethodInfoListBox()
+ {
+ this.DataContext = this;
+ InitializeComponent();
+ }
+
+
+ public IEnumerable Nodes
+ {
+ get { return (IEnumerable)GetValue(NodesProperty); }
+ set { SetValue(NodesProperty, value); }
+ }
+
+ //public ItemCollection Items
+ //{
+ // get
+ // {
+ // return (ItemCollection)GetValue(ItemsProperty);
+ // }
+ // set
+ // {
+ // SetValue(ItemsProperty, value);
+ // }
+ //}
+
+
+ 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
+ {
+ get { return (Brush)GetValue(BackgroundColorProperty); }
+ set { SetValue(BackgroundColorProperty, value); }
+ }
+
+ public static readonly DependencyProperty BackgroundColorProperty =
+ DependencyProperty.Register(nameof(BackgroundColor), typeof(Brush), typeof(FlowMethodInfoListBox), new PropertyMetadata(Brushes.White));
+
+
+
+
+
+
+
+
+
+
+
+ }
+}
diff --git a/Workbench/Models/FlowLibraryInfo.cs b/Workbench/Models/FlowLibraryInfo.cs
new file mode 100644
index 0000000..21c6008
--- /dev/null
+++ b/Workbench/Models/FlowLibraryInfo.cs
@@ -0,0 +1,44 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using Serein.Library;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+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]
+ private string _filePath;
+
+ [ObservableProperty]
+ private string _libraryName;
+
+ [ObservableProperty]
+ 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 { } }
+
+ }
+}
diff --git a/Workbench/Serein.WorkBench.csproj b/Workbench/Serein.WorkBench.csproj
index 0e1152d..cd26351 100644
--- a/Workbench/Serein.WorkBench.csproj
+++ b/Workbench/Serein.WorkBench.csproj
@@ -60,6 +60,8 @@
+
+
@@ -83,9 +85,4 @@
-
-
-
-
-
diff --git a/Workbench/Services/FlowEEForwardingService.cs b/Workbench/Services/FlowEEForwardingService.cs
new file mode 100644
index 0000000..280c218
--- /dev/null
+++ b/Workbench/Services/FlowEEForwardingService.cs
@@ -0,0 +1,344 @@
+
+using Newtonsoft.Json.Linq;
+using Serein.Library.Api;
+using Serein.NodeFlow;
+using System;
+using System.Collections.Generic;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Serein.Library;
+using Serein.Library.Utils;
+using Serein.Workbench.Avalonia.Api;
+using Serein.Workbench.Api;
+
+namespace Serein.Workbench.Services
+{
+
+
+
+ internal class FlowEEForwardingService : IFlowEEForwardingService
+ {
+ ///
+ /// 流程运行环境
+ ///
+ private readonly IFlowEnvironment flowEnvironment;
+ private readonly IFlowEnvironmentEvent flowEnvironmentEvent;
+
+ ///
+ /// 转发流程运行环境各个事件的实现类
+ ///
+ ///
+ ///
+ public FlowEEForwardingService(IFlowEnvironment flowEnvironment,
+ IFlowEnvironmentEvent flowEnvironmentEvent)
+ {
+ this.flowEnvironment = flowEnvironment;
+ this.flowEnvironmentEvent = flowEnvironmentEvent;
+ InitFlowEnvironmentEvent();
+ }
+
+ #region 工作台事件转发
+ ///
+ /// 加载了依赖文件事件
+ ///
+ public event LoadDllHandler? OnDllLoad;
+ ///
+ /// 项目加载完成事件
+ ///
+ public event ProjectLoadedHandler? OnProjectLoaded;
+ ///
+ /// 项目保存中事件
+ ///
+ public event ProjectSavingHandler? OnProjectSaving;
+ ///
+ /// 节点连接改变事件
+ ///
+ public event NodeConnectChangeHandler? OnNodeConnectChange;
+ ///
+ /// 节点创建事件
+ ///
+ public event NodeCreateHandler? OnNodeCreate;
+ ///
+ /// 节点移除事件
+ ///
+ public event NodeRemoveHandler? OnNodeRemove;
+ ///
+ /// 节点放置容器事件
+ ///
+ public event NodePlaceHandler? OnNodePlace;
+ ///
+ /// 节点取出事件
+ ///
+ public event NodeTakeOutHandler? OnNodeTakeOut;
+ ///
+ /// 流程起始节点改变事件
+ ///
+ public event StartNodeChangeHandler? OnStartNodeChange;
+ ///
+ /// 流程运行完毕事件
+ ///
+ public event FlowRunCompleteHandler? OnFlowRunComplete;
+ ///
+ /// 被监视的对象数据改变事件
+ ///
+ public event MonitorObjectChangeHandler? OnMonitorObjectChange;
+ ///
+ /// 节点中断状态改变事件
+ ///
+ public event NodeInterruptStateChangeHandler? OnNodeInterruptStateChange;
+ ///
+ /// 表达式中断触发事件
+ ///
+ public event ExpInterruptTriggerHandler? OnInterruptTrigger;
+ ///
+ /// 容器对象改变事件
+ ///
+ public event IOCMembersChangedHandler? OnIOCMembersChanged;
+ ///
+ /// 节点定位事件
+ ///
+ public event NodeLocatedHandler? OnNodeLocated;
+ ///
+ /// 节点移动事件
+ ///
+ public event NodeMovedHandler? OnNodeMoved;
+ ///
+ /// 运行环境输出事件
+ ///
+ public event EnvOutHandler? OnEnvOut;
+
+ #endregion
+
+ #region 流程运行环境事件
+
+ private void InitFlowEnvironmentEvent()
+ {
+ flowEnvironmentEvent.OnDllLoad += FlowEnvironment_DllLoadEvent;
+ flowEnvironmentEvent.OnProjectSaving += EnvDecorator_OnProjectSaving;
+ flowEnvironmentEvent.OnProjectLoaded += FlowEnvironment_OnProjectLoaded;
+ flowEnvironmentEvent.OnStartNodeChange += FlowEnvironment_StartNodeChangeEvent;
+ flowEnvironmentEvent.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt;
+ flowEnvironmentEvent.OnNodeCreate += FlowEnvironment_NodeCreateEvent;
+ flowEnvironmentEvent.OnNodeRemove += FlowEnvironment_NodeRemoveEvent;
+ flowEnvironmentEvent.OnNodePlace += EnvDecorator_OnNodePlaceEvent;
+ flowEnvironmentEvent.OnNodeTakeOut += EnvDecorator_OnNodeTakeOutEvent;
+ flowEnvironmentEvent.OnFlowRunComplete += FlowEnvironment_OnFlowRunCompleteEvent;
+
+ flowEnvironmentEvent.OnMonitorObjectChange += FlowEnvironment_OnMonitorObjectChangeEvent;
+ flowEnvironmentEvent.OnNodeInterruptStateChange += FlowEnvironment_OnNodeInterruptStateChangeEvent;
+ flowEnvironmentEvent.OnInterruptTrigger += FlowEnvironment_OnInterruptTriggerEvent;
+
+ flowEnvironmentEvent.OnIOCMembersChanged += FlowEnvironment_OnIOCMembersChangedEvent;
+
+ flowEnvironmentEvent.OnNodeLocated += FlowEnvironment_OnNodeLocateEvent;
+ flowEnvironmentEvent.OnNodeMoved += FlowEnvironment_OnNodeMovedEvent;
+
+ flowEnvironmentEvent.OnEnvOut += FlowEnvironment_OnEnvOutEvent;
+ }
+
+ private void ResetFlowEnvironmentEvent()
+ {
+ flowEnvironmentEvent.OnDllLoad -= FlowEnvironment_DllLoadEvent;
+ flowEnvironmentEvent.OnProjectSaving -= EnvDecorator_OnProjectSaving;
+ flowEnvironmentEvent.OnProjectLoaded -= FlowEnvironment_OnProjectLoaded;
+ flowEnvironmentEvent.OnStartNodeChange -= FlowEnvironment_StartNodeChangeEvent;
+ flowEnvironmentEvent.OnNodeConnectChange -= FlowEnvironment_NodeConnectChangeEvemt;
+ flowEnvironmentEvent.OnNodeCreate -= FlowEnvironment_NodeCreateEvent;
+ flowEnvironmentEvent.OnNodeRemove -= FlowEnvironment_NodeRemoveEvent;
+ flowEnvironmentEvent.OnNodePlace -= EnvDecorator_OnNodePlaceEvent;
+ flowEnvironmentEvent.OnNodeTakeOut -= EnvDecorator_OnNodeTakeOutEvent;
+ flowEnvironmentEvent.OnFlowRunComplete -= FlowEnvironment_OnFlowRunCompleteEvent;
+
+
+ flowEnvironmentEvent.OnMonitorObjectChange -= FlowEnvironment_OnMonitorObjectChangeEvent;
+ flowEnvironmentEvent.OnNodeInterruptStateChange -= FlowEnvironment_OnNodeInterruptStateChangeEvent;
+ flowEnvironmentEvent.OnInterruptTrigger -= FlowEnvironment_OnInterruptTriggerEvent;
+
+ flowEnvironmentEvent.OnIOCMembersChanged -= FlowEnvironment_OnIOCMembersChangedEvent;
+ flowEnvironmentEvent.OnNodeLocated -= FlowEnvironment_OnNodeLocateEvent;
+ flowEnvironmentEvent.OnNodeMoved -= FlowEnvironment_OnNodeMovedEvent;
+
+ flowEnvironmentEvent.OnEnvOut -= FlowEnvironment_OnEnvOutEvent;
+
+ }
+
+ #region 运行环境事件
+
+ ///
+ /// 环境内容输出
+ ///
+ ///
+ ///
+ private void FlowEnvironment_OnEnvOutEvent(InfoType type, string value)
+ {
+ //LogOutWindow.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}");
+ }
+
+ ///
+ /// 需要保存项目
+ ///
+ ///
+ ///
+ private void EnvDecorator_OnProjectSaving(ProjectSavingEventArgs eventArgs)
+ {
+ OnProjectSaving?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 加载完成
+ ///
+ ///
+ private void FlowEnvironment_OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
+ {
+ OnProjectLoaded?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 运行完成
+ ///
+ ///
+ ///
+ private void FlowEnvironment_OnFlowRunCompleteEvent(FlowEventArgs eventArgs)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, "-------运行完成---------\r\n");
+ OnFlowRunComplete?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 加载了DLL文件,dll内容
+ ///
+ private void FlowEnvironment_DllLoadEvent(LoadDllEventArgs eventArgs)
+ {
+ OnDllLoad?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 节点连接关系变更
+ ///
+ ///
+ private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs)
+ {
+ OnNodeConnectChange?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 节点移除事件
+ ///
+ ///
+ private void FlowEnvironment_NodeRemoveEvent(NodeRemoveEventArgs eventArgs)
+ {
+ OnNodeRemove?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 添加节点事件
+ ///
+ /// 添加节点事件参数
+ ///
+ private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs)
+ {
+ OnNodeCreate?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 放置一个节点
+ ///
+ ///
+ ///
+ private void EnvDecorator_OnNodePlaceEvent(NodePlaceEventArgs eventArgs)
+ {
+ OnNodePlace?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 取出一个节点
+ ///
+ ///
+ private void EnvDecorator_OnNodeTakeOutEvent(NodeTakeOutEventArgs eventArgs)
+ {
+ OnNodeTakeOut?.Invoke(eventArgs);
+
+ }
+
+ ///
+ /// 设置了流程起始控件
+ ///
+ ///
+ ///
+ private void FlowEnvironment_StartNodeChangeEvent(StartNodeChangeEventArgs eventArgs)
+ {
+
+ OnStartNodeChange?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 被监视的对象发生改变
+ ///
+ ///
+ private void FlowEnvironment_OnMonitorObjectChangeEvent(MonitorObjectEventArgs eventArgs)
+ {
+ OnMonitorObjectChange?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 节点中断状态改变。
+ ///
+ ///
+ private void FlowEnvironment_OnNodeInterruptStateChangeEvent(NodeInterruptStateChangeEventArgs eventArgs)
+ {
+ OnNodeInterruptStateChange?.Invoke(eventArgs);
+ }
+
+ ///
+ /// 节点触发了中断
+ ///
+ ///
+ ///
+ private void FlowEnvironment_OnInterruptTriggerEvent(InterruptTriggerEventArgs eventArgs)
+ {
+ OnInterruptTrigger?.Invoke(eventArgs);
+ }
+
+ ///
+ /// IOC变更
+ ///
+ ///
+ ///
+ private void FlowEnvironment_OnIOCMembersChangedEvent(IOCMembersChangedEventArgs eventArgs)
+ {
+ OnIOCMembersChanged?.Invoke(eventArgs);
+
+ }
+
+ ///
+ /// 节点需要定位
+ ///
+ ///
+ ///
+ private void FlowEnvironment_OnNodeLocateEvent(NodeLocatedEventArgs eventArgs)
+ {
+ OnNodeLocated?.Invoke(eventArgs);
+ }
+
+
+ ///
+ /// 节点移动
+ ///
+ ///
+ private void FlowEnvironment_OnNodeMovedEvent(NodeMovedEventArgs eventArgs)
+ {
+ OnNodeMoved?.Invoke(eventArgs);
+ }
+
+
+ #endregion
+
+
+ #endregion
+
+
+ }
+}
diff --git a/Workbench/Services/KeyEventService.cs b/Workbench/Services/KeyEventService.cs
new file mode 100644
index 0000000..ac6069f
--- /dev/null
+++ b/Workbench/Services/KeyEventService.cs
@@ -0,0 +1,80 @@
+/*
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Services
+{
+ delegate void KeyDownEventHandler(Key key);
+ delegate void KeyUpEventHandler(Key key);
+
+ ///
+ /// 全局事件服务
+ ///
+ internal interface IKeyEventService
+ {
+ event KeyDownEventHandler KeyDown;
+ event KeyUpEventHandler KeyUp;
+
+ ///
+ /// 获取某个按键状态
+ ///
+ ///
+ ///
+ bool GetKeyState(Key key);
+ ///
+ /// 设置某个按键的状态
+ ///
+ ///
+ ///
+ void SetKeyState(Key key, bool statestate);
+ }
+
+ ///
+ /// 管理按键状态
+ ///
+ internal class KeyEventService : IKeyEventService
+ {
+
+ ///
+ /// 按键按下
+ ///
+ public event KeyDownEventHandler KeyDown;
+ ///
+ /// 按键松开
+ ///
+ public event KeyUpEventHandler KeyUp;
+
+ public KeyEventService()
+ {
+ var arr = Enum.GetValues();
+ KeysState = new bool[arr.Length];
+
+ // 绑定快捷键
+ //HotKeyManager.SetHotKey(saveMenuItem, new KeyGesture(Key.S, KeyModifiers.Control));
+ }
+
+ private readonly bool[] KeysState;
+ public bool GetKeyState(Key key)
+ {
+ return KeysState[(int)key];
+ }
+ public void SetKeyState(Key key, bool state)
+ {
+ if (state)
+ {
+ KeyDown?.Invoke(key);
+ }
+ else
+ {
+ KeyUp?.Invoke(key);
+ }
+ //Debug.WriteLine($"按键事件:{key} - {state}");
+ KeysState[(int)key] = state;
+ }
+ }
+}
+*/
\ No newline at end of file
diff --git a/Workbench/Services/NodeOperationService.cs b/Workbench/Services/NodeOperationService.cs
new file mode 100644
index 0000000..2a9117f
--- /dev/null
+++ b/Workbench/Services/NodeOperationService.cs
@@ -0,0 +1,406 @@
+
+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.Api
+{
+
+ ///
+ /// 提供节点操作的接口
+ ///
+ internal interface INodeOperationService
+ {
+ ///
+ /// 连接数据
+ ///
+ // ConnectingManage ConnectingManage { get; }
+
+ ///
+ /// 主画布
+ ///
+ Canvas MainCanvas { get; set; }
+
+ ///
+ /// 节点创建事件
+ ///
+
+ event NodeViewCreateHandle OnNodeViewCreate;
+
+ ///
+ /// 创建节点控件
+ ///
+ /// 控件类型
+ /// 创建坐标
+ /// 节点方法信息
+ public void CreateNodeView(MethodDetailsInfo methodDetailsInfo, PositionOfUI position);
+
+ ///
+ /// 尝试从连接控制点创建连接
+ ///
+ ///
+ //void TryCreateConnectionOnJunction(NodeJunctionView startJunction);
+
+ }
+
+
+
+
+ #region 事件与事件参数
+ ///
+ /// 创建节点控件事件
+ ///
+ ///
+
+ internal delegate bool NodeViewCreateHandle(NodeViewCreateEventArgs eventArgs);
+
+ ///
+ /// 创建节点控件事件参数
+ ///
+
+
+
+ internal class NodeViewCreateEventArgs : EventArgs
+ {
+ internal NodeViewCreateEventArgs(NodeControlBase nodeControl, PositionOfUI position)
+ {
+ this.NodeControl = nodeControl;
+ this.Position = position;
+ }
+ public NodeControlBase NodeControl { get; private set; }
+ public PositionOfUI Position { get; private set; }
+ }
+
+
+ #endregion
+
+
+
+
+
+}
+
+namespace Serein.Workbench.Services
+{
+ ///
+ /// 节点操作相关服务
+ ///
+ internal class NodeOperationService : INodeOperationService
+ {
+
+ public NodeOperationService(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);
+ }
+
+
+ #region 接口属性
+ //public ConnectingManage ConnectingManage { get; private set; } = new ConnectingManage();
+ public Canvas MainCanvas { get; set; }
+
+ #endregion
+
+ #region 私有变量
+
+ ///
+ /// 存储所有与节点有关的控件
+ ///
+ private Dictionary NodeControls { get; } = [];
+
+ ///
+ /// 存储所有连接
+ ///
+ //private List Connections { get; } = [];
+
+
+
+ ///
+ /// 流程运行环境
+ ///
+ private readonly IFlowEnvironment flowEnvironment;
+
+ ///
+ /// 流程运行环境事件转发
+ ///
+ private readonly IFlowEEForwardingService feefService;
+ #endregion
+
+ #region 节点操作事件
+
+ ///
+ /// 创建了节点控件
+ ///
+ public event NodeViewCreateHandle OnNodeViewCreate;
+
+ #endregion
+
+ #region 转发事件的处理
+
+ ///
+ /// 从工作台事件转发器监听节点创建事件
+ ///
+ ///
+ private void FeefService_OnNodeCreate(NodeCreateEventArgs eventArgs)
+ {
+ var nodeModel = eventArgs.NodeModel;
+ if (NodeControls.ContainsKey(nodeModel.Guid))
+ {
+ SereinEnv.WriteLine(InfoType.WARN, $"OnNodeCreate 事件意外触发,节点Guid重复 - {nodeModel.Guid}");
+ return;
+ }
+ if (!flowEnvironment.NodeMVVMManagement.TryGetType(nodeModel.ControlType, out var nodeMVVM))
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"无法创建{nodeModel.ControlType}节点,节点类型尚未注册。");
+ return;
+ }
+ if (nodeMVVM.ControlType == null
+ || nodeMVVM.ViewModelType == null)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"无法创建{nodeModel.ControlType}节点,UI类型尚未注册(请通过 NodeMVVMManagement.RegisterUI() 方法进行注册)。");
+ return;
+ }
+
+ var isSuccessful = TryCreateNodeView(nodeMVVM.ControlType, // 控件UI类型
+ nodeMVVM.ViewModelType, // 控件VIewModel类型
+ nodeModel, // 控件数据实体
+ out var nodeControl); // 成功创建后传出的节点控件实体
+ if (!isSuccessful || nodeControl is null)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, $"无法创建{nodeModel.ControlType}节点,节点创建失败。");
+ return;
+ }
+
+
+ var e = new NodeViewCreateEventArgs(nodeControl, eventArgs.Position);
+ if (OnNodeViewCreate?.Invoke(e) == true)
+ {
+ // 成功创建
+ NodeControls.TryAdd(nodeModel.Guid, nodeControl); // 缓存起来,通知其它地方拿取这个控件
+ }
+
+ }
+
+
+ ///
+ /// 运行环境连接了节点事件
+ ///
+ ///
+ ///
+ private void FeefService_OnNodeConnectChange(NodeConnectChangeEventArgs eventArgs)
+ {
+ string fromNodeGuid = eventArgs.FromNodeGuid;
+ string toNodeGuid = eventArgs.ToNodeGuid;
+ if (!TryGetControl(fromNodeGuid, out var fromNodeControl)
+ || !TryGetControl(toNodeGuid, out var toNodeControl))
+ {
+ return;
+ }
+
+
+ if (eventArgs.JunctionOfConnectionType == JunctionOfConnectionType.Invoke)
+ {
+ ConnectionInvokeType connectionType = eventArgs.ConnectionInvokeType;
+ #region 创建/删除节点之间的调用关系
+ #region 创建连接
+ if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接
+ {
+ if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
+ {
+ SereinEnv.WriteLine(InfoType.INFO, "非预期的连接");
+ return;
+ }
+ var startJunction = IFormJunction.NextStepJunction;
+ var endJunction = IToJunction.ExecuteJunction;
+
+ // NodeConnectionLineControl nodeConnectionLineControl = new NodeConnectionLineControl(MainCanvas, startJunction, endJunction);
+
+ //startJunction.TransformToVisual(MainCanvas);
+
+ //// 添加连接
+ //var shape = new ConnectionLineShape(
+ // FlowChartCanvas,
+ // connectionType,
+ // startJunction,
+ // endJunction
+ //);
+
+
+ //NodeConnectionLine nodeConnectionLine = new NodeConnectionLine(MainCanvas, shape);
+
+ //if (toNodeControl is FlipflopNodeControl flipflopControl
+ // && flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
+ //{
+ // NodeTreeViewer.RemoveGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
+ //}
+
+ //Connections.Add(nodeConnectionLineControl);
+ //fromNodeControl.AddConnection(nodeConnectionLineControl);
+ //toNodeControl.AddConnection(nodeConnectionLineControl);
+ }
+ #endregion
+
+
+ #region 移除连接
+ /* else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Remove) // 移除连接
+ {
+ // 需要移除连接
+ var removeConnections = Connections.Where(c =>
+ c.Start.MyNode.Guid.Equals(fromNodeGuid)
+ && c.End.MyNode.Guid.Equals(toNodeGuid)
+ && (c.Start.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Invoke
+ || c.End.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Invoke))
+ .ToList();
+
+
+ foreach (var connection in removeConnections)
+ {
+ Connections.Remove(connection);
+ fromNodeControl.RemoveConnection(connection); // 移除连接
+ toNodeControl.RemoveConnection(connection); // 移除连接
+ if (NodeControls.TryGetValue(connection.End.MyNode.Guid, out var control))
+ {
+ JudgmentFlipFlopNode(control); // 连接关系变更时判断
+ }
+ }
+ }*/
+ #endregion
+
+
+ #endregion
+ }
+
+ }
+ #endregion
+
+ #region 私有方法
+
+ ///
+ /// 创建节点控件
+ ///
+ /// 节点控件视图控件类型
+ /// 节点控件ViewModel类型
+ /// 节点Model实例
+ /// 返回的节点对象
+ /// 是否创建成功
+ /// 无法创建节点控件
+ private bool TryCreateNodeView(Type viewType, Type viewModelType, NodeModelBase nodeModel, out NodeControlBase? nodeView)
+ {
+ if (string.IsNullOrEmpty(nodeModel.Guid))
+ {
+ nodeModel.Guid = Guid.NewGuid().ToString();
+ }
+ var t_ViewModel = Activator.CreateInstance(viewModelType, nodeModel);
+ if (t_ViewModel is not NodeControlViewModelBase viewModelBase)
+ {
+ nodeView = null;
+ return false;
+ }
+ var controlObj = Activator.CreateInstance(viewType);
+ if (controlObj is NodeControlBase nodeControl)
+ {
+ nodeControl.DataContext = viewModelBase;
+ nodeView = nodeControl;
+ return true;
+ }
+ else
+ {
+ nodeView = null;
+ return false;
+ }
+
+ // 在其它地方验证过了,所以注释
+ //if ((viewType is null)
+ // || viewModelType is null
+ // || nodeModel is null)
+ //{
+ // nodeView = null;
+ // return false;
+ //}
+ //if (typeof(INodeControl).IsSubclassOf(viewType)
+ // || typeof(NodeViewModelBase).IsSubclassOf(viewModelType))
+ //{
+ // nodeView = null;
+ // return false;
+ //}
+ }
+
+ private bool TryGetControl(string nodeGuid, out NodeControlBase nodeControl)
+ {
+ if (string.IsNullOrEmpty(nodeGuid))
+ {
+ nodeControl = null;
+ return false;
+ }
+ if (!NodeControls.TryGetValue(nodeGuid, out nodeControl))
+ {
+ nodeControl = null;
+ return false;
+ }
+ if (nodeControl is null)
+ {
+ return false;
+ }
+ return true;
+ }
+
+ #endregion
+
+ #region 操作接口对外暴露的接口
+
+ ///
+ /// 创建节点控件
+ ///
+ /// 控件类型
+ /// 创建坐标
+ /// 节点方法信息(基础节点传null)
+ public void CreateNodeView(MethodDetailsInfo methodDetailsInfo, PositionOfUI position)
+ {
+ Task.Run(async () =>
+ {
+ if (EnumHelper.TryConvertEnum(methodDetailsInfo.NodeType, out var nodeType))
+ {
+ await flowEnvironment.CreateNodeAsync(nodeType, position, methodDetailsInfo);
+ }
+ });
+ }
+
+
+
+
+ #endregion
+ }
+
+}
diff --git a/Workbench/Services/ProjectService.cs b/Workbench/Services/ProjectService.cs
new file mode 100644
index 0000000..789551f
--- /dev/null
+++ b/Workbench/Services/ProjectService.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Services
+{
+ internal class ProjectService
+ {
+
+ }
+}
diff --git a/Workbench/Services/WorkbenchEventService.cs b/Workbench/Services/WorkbenchEventService.cs
new file mode 100644
index 0000000..78ede6b
--- /dev/null
+++ b/Workbench/Services/WorkbenchEventService.cs
@@ -0,0 +1,90 @@
+using Serein.Library;
+using Serein.Library.Api;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Security.Cryptography.X509Certificates;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.Services
+{
+
+ #region 工作台事件
+
+ public delegate void PreviewlMethodInfoHandler(PreviewlMethodInfoEventArgs eventArgs);
+
+ #endregion
+
+ #region 工作台事件参数
+ public class PreviewlMethodInfoEventArgs(MethodDetailsInfo mdInfo) : EventArgs
+ {
+ ///
+ /// 方法信息
+ ///
+ public MethodDetailsInfo MethodDetailsInfo { get; } = mdInfo;
+ }
+ #endregion
+
+
+ ///
+ /// 工作台事件管理
+ ///
+ internal interface IWorkbenchEventService
+ {
+ ///
+ /// 预览了某个方法信息(待创建)
+ ///
+ event PreviewlMethodInfoHandler OnPreviewlMethodInfo;
+
+ ///
+ /// 预览依赖方法信息
+ ///
+ void PreviewLibraryMethodInfo(MethodDetailsInfo mdInfo);
+ }
+
+ ///
+ /// 工作台事件的实现类
+ ///
+ internal class WorkbenchEventService : IWorkbenchEventService
+ {
+
+ private readonly IFlowEnvironment flowEnvironment;
+ ///
+ /// 管理工作台的事件
+ ///
+ ///
+ public WorkbenchEventService(IFlowEnvironment flowEnvironment)
+ {
+ this.flowEnvironment = flowEnvironment;
+
+ }
+
+ private void SubscribeEvents()
+ {
+
+ }
+
+ ///
+ /// 预览了某个方法信息(待创建)
+ ///
+ public event PreviewlMethodInfoHandler? OnPreviewlMethodInfo;
+ ///
+ /// 预览依赖方法信息
+ ///
+ public void PreviewLibraryMethodInfo(MethodDetailsInfo mdInfo)
+ {
+ OnPreviewlMethodInfo?.Invoke(new PreviewlMethodInfoEventArgs(mdInfo));
+ }
+
+ ///
+ /// 需要放置节点控件
+ ///
+ public void PlateNodeControl()
+ {
+
+ }
+ }
+
+}
+
diff --git a/Workbench/ViewModels/BaseNodesViewModel.cs b/Workbench/ViewModels/BaseNodesViewModel.cs
new file mode 100644
index 0000000..e2a0465
--- /dev/null
+++ b/Workbench/ViewModels/BaseNodesViewModel.cs
@@ -0,0 +1,13 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.ViewModels
+{
+ internal class BaseNodesViewModel : ObservableObject
+ {
+ }
+}
diff --git a/Workbench/ViewModels/FlowCanvasViewModel.cs b/Workbench/ViewModels/FlowCanvasViewModel.cs
new file mode 100644
index 0000000..11058ad
--- /dev/null
+++ b/Workbench/ViewModels/FlowCanvasViewModel.cs
@@ -0,0 +1,19 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.ViewModels
+{
+ public partial class FlowCanvasViewModel : ObservableObject
+ {
+
+ [ObservableProperty]
+ private bool _isConnectionInvokeNode;
+
+ [ObservableProperty]
+ private bool _isConnectionArgSourceNode;
+ }
+}
diff --git a/Workbench/ViewModels/FlowEditViewModel.cs b/Workbench/ViewModels/FlowEditViewModel.cs
new file mode 100644
index 0000000..40f0cb3
--- /dev/null
+++ b/Workbench/ViewModels/FlowEditViewModel.cs
@@ -0,0 +1,16 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.ViewModels
+{
+ ///
+ /// 流程编辑数据视图
+ ///
+ public partial class FlowEditViewModel : ObservableObject
+ {
+ }
+}
diff --git a/Workbench/ViewModels/FlowLibrarysViewModel.cs b/Workbench/ViewModels/FlowLibrarysViewModel.cs
new file mode 100644
index 0000000..cdd91e5
--- /dev/null
+++ b/Workbench/ViewModels/FlowLibrarysViewModel.cs
@@ -0,0 +1,54 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using Serein.Library;
+using Serein.Workbench.Api;
+using Serein.Workbench.Models;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.ViewModels
+{
+ internal partial class FlowLibrarysViewModel : ObservableObject
+ {
+ private readonly IFlowEEForwardingService flowEEForwardingService;
+
+ [ObservableProperty]
+ private ObservableCollection flowLibraryInfos;
+
+
+
+ public FlowLibrarysViewModel(IFlowEEForwardingService flowEEForwardingService)
+ {
+ this.flowEEForwardingService = flowEEForwardingService;
+ FlowLibraryInfos = new ObservableCollection();
+ flowEEForwardingService.OnDllLoad += FlowEEForwardingService_OnDllLoad;
+ }
+
+ private void FlowEEForwardingService_OnDllLoad(Library.Api.LoadDllEventArgs eventArgs)
+ {
+ if (!eventArgs.IsSucceed) return;
+ List mds = eventArgs.MethodDetailss;
+ NodeLibraryInfo libraryInfo = eventArgs.NodeLibraryInfo;
+
+ var methodInfo = new ObservableCollection();
+ foreach (var md in mds)
+ {
+ methodInfo.Add(new FlowLibraryMethodDetailsInfo(md));
+ }
+ var flInfo = new FlowLibraryInfo
+ {
+ LibraryName = libraryInfo.AssemblyName,
+ FilePath = libraryInfo.FilePath,
+ MethodInfo = methodInfo
+ };
+
+ FlowLibraryInfos.Add(flInfo);
+
+
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/Workbench/ViewModels/FlowWorkbenchViewModel.cs b/Workbench/ViewModels/FlowWorkbenchViewModel.cs
new file mode 100644
index 0000000..4f3680b
--- /dev/null
+++ b/Workbench/ViewModels/FlowWorkbenchViewModel.cs
@@ -0,0 +1,25 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using Serein.Workbench.Api;
+using Serein.Workbench.Models;
+using System;
+using System.Collections.Generic;
+using System.Collections.ObjectModel;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.ViewModels
+{
+ internal partial class FlowWorkbenchViewModel : ObservableObject
+ {
+ private readonly IFlowEEForwardingService flowEEForwardingService;
+
+
+
+ public FlowWorkbenchViewModel(IFlowEEForwardingService flowEEForwardingService)
+ {
+ this.flowEEForwardingService = flowEEForwardingService;
+ //flowEEForwardingService.OnDllLoad += FlowEEForwardingService_OnDllLoad;
+ }
+ }
+}
diff --git a/Workbench/ViewModels/Locator.cs b/Workbench/ViewModels/Locator.cs
new file mode 100644
index 0000000..eae5f75
--- /dev/null
+++ b/Workbench/ViewModels/Locator.cs
@@ -0,0 +1,40 @@
+using Microsoft.Extensions.DependencyInjection;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.ViewModels
+{
+ internal class Locator
+ {
+ private static IServiceProvider ServiceProvide { get; set; }
+ public Locator(IServiceProvider serviceProvider)
+ {
+ ServiceProvider = serviceProvider;
+ }
+
+ //private IServiceProvider GetService()
+ //{
+ // var service = new ServiceCollection();
+ // service.AddSingleton();
+ // service.AddSingleton();
+ // service.AddSingleton();
+ // service.AddSingleton();
+ // service.AddSingleton();
+ // service.AddTransient();
+ // return service.BuildServiceProvider();
+ //}
+
+ public MainViewModel MainViewModel => App.GetService() ?? throw new NotImplementedException();
+ public MainMenuBarViewModel MainMenuBarViewModel => App.GetService() ?? throw new NotImplementedException();
+ public FlowWorkbenchViewModel FlowWorkbenchViewModel => App.GetService() ?? throw new NotImplementedException();
+ public BaseNodesViewModel BaseNodesViewModel => App.GetService() ?? throw new NotImplementedException();
+ public FlowLibrarysViewModel FlowLibrarysViewModel => App.GetService() ?? throw new NotImplementedException();
+ public FlowEditViewModel FlowEditViewModel => App.GetService() ?? throw new NotImplementedException();
+ public FlowCanvasViewModel FlowCanvasViewModel => App.GetService() ?? throw new NotImplementedException();
+
+ public IServiceProvider ServiceProvider { get; }
+ }
+}
diff --git a/Workbench/ViewModels/MainMenuBarViewModel.cs b/Workbench/ViewModels/MainMenuBarViewModel.cs
new file mode 100644
index 0000000..0a3a726
--- /dev/null
+++ b/Workbench/ViewModels/MainMenuBarViewModel.cs
@@ -0,0 +1,12 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+
+namespace Serein.Workbench.ViewModels
+{
+ public class MainMenuBarViewModel : ObservableObject
+ {
+ public MainMenuBarViewModel()
+ {
+
+ }
+ }
+}
diff --git a/Workbench/ViewModels/MainViewModel.cs b/Workbench/ViewModels/MainViewModel.cs
new file mode 100644
index 0000000..29c31bf
--- /dev/null
+++ b/Workbench/ViewModels/MainViewModel.cs
@@ -0,0 +1,17 @@
+using CommunityToolkit.Mvvm.ComponentModel;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.Workbench.ViewModels
+{
+ public class MainViewModel : ObservableObject
+ {
+ public MainViewModel()
+ {
+
+ }
+ }
+}
diff --git a/Workbench/Views/BaseNodesView.xaml b/Workbench/Views/BaseNodesView.xaml
new file mode 100644
index 0000000..8c1e870
--- /dev/null
+++ b/Workbench/Views/BaseNodesView.xaml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Workbench/Views/BaseNodesView.xaml.cs b/Workbench/Views/BaseNodesView.xaml.cs
new file mode 100644
index 0000000..4225698
--- /dev/null
+++ b/Workbench/Views/BaseNodesView.xaml.cs
@@ -0,0 +1,57 @@
+using Serein.Library;
+using Serein.Workbench.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Serein.Workbench.Views
+{
+ ///
+ /// BaseNodesView.xaml 的交互逻辑
+ ///
+ public partial class BaseNodesView : UserControl
+ {
+ public BaseNodesView()
+ {
+ this.DataContext = App.GetService().BaseNodesViewModel;
+ InitializeComponent();
+ }
+
+ ///
+ /// 基础节点的拖拽放置创建
+ ///
+ ///
+ ///
+ private void BaseNodeControl_PreviewMouseMove(object sender, MouseEventArgs e)
+ {
+ if (sender is UserControl control)
+ {
+ if (e.LeftButton == MouseButtonState.Pressed)
+ {
+ // 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
+ var dragData = new DataObject(MouseNodeType.CreateBaseNodeInCanvas, control.GetType());
+ try
+ {
+ DragDrop.DoDragDrop(control, dragData, DragDropEffects.Move);
+ }
+ catch (Exception ex)
+ {
+ SereinEnv.WriteLine(ex);
+ }
+ }
+
+ }
+ }
+ }
+}
diff --git a/Workbench/Views/FlowCanvasView.xaml b/Workbench/Views/FlowCanvasView.xaml
new file mode 100644
index 0000000..cd975dc
--- /dev/null
+++ b/Workbench/Views/FlowCanvasView.xaml
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Workbench/Views/FlowCanvasView.xaml.cs b/Workbench/Views/FlowCanvasView.xaml.cs
new file mode 100644
index 0000000..73d8a09
--- /dev/null
+++ b/Workbench/Views/FlowCanvasView.xaml.cs
@@ -0,0 +1,686 @@
+using Serein.Library;
+using Serein.Library.Api;
+using Serein.Workbench.Node.View;
+using Serein.Workbench.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Controls.Primitives;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Media.Media3D;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Serein.Workbench.Views
+{
+ ///
+ /// FlowCanvasView.xaml 的交互逻辑
+ ///
+ public partial class FlowCanvasView : UserControl
+ {
+ private FlowCanvasViewModel ViewModel;
+ ///
+ /// 存储所有的连接。考虑集成在运行环境中。
+ ///
+ private List Connections { get; } = [];
+
+ #region 与画布相关的字段
+
+ ///
+ /// 标记是否正在尝试选取控件
+ ///
+ private bool IsSelectControl;
+ ///
+ /// 标记是否正在进行连接操作
+ ///
+ //private bool IsConnecting;
+ ///
+ /// 标记是否正在拖动控件
+ ///
+ private bool IsControlDragging;
+ ///
+ /// 标记是否正在拖动画布
+ ///
+ private bool IsCanvasDragging;
+ private bool IsSelectDragging;
+
+ ///
+ /// 当前选取的控件
+ ///
+ private readonly List selectNodeControls = [];
+
+ ///
+ /// 记录开始拖动节点控件时的鼠标位置
+ ///
+ private Point startControlDragPoint;
+ ///
+ /// 记录移动画布开始时的鼠标位置
+ ///
+ private Point startCanvasDragPoint;
+ ///
+ /// 记录开始选取节点控件时的鼠标位置
+ ///
+ private Point startSelectControolPoint;
+
+
+ ///
+ /// 记录开始连接的文本块
+ ///
+ //private NodeControlBase? startConnectNodeControl;
+ ///
+ /// 当前正在绘制的连接线
+ ///
+ //private Line? currentLine;
+ ///
+ /// 当前正在绘制的真假分支属性
+ ///
+ //private ConnectionInvokeType currentConnectionType;
+
+
+ ///
+ /// 组合变换容器
+ ///
+ private readonly TransformGroup canvasTransformGroup;
+ ///
+ /// 缩放画布
+ ///
+ private readonly ScaleTransform scaleTransform;
+ ///
+ /// 平移画布
+ ///
+ private readonly TranslateTransform translateTransform;
+ #endregion
+
+ private IFlowEnvironment EnvDecorator;
+ public FlowCanvasView()
+ {
+ ViewModel = App.GetService().FlowCanvasViewModel;
+
+ EnvDecorator = App.GetService();
+
+ InitializeComponent();
+
+ #region 缩放平移容器
+ canvasTransformGroup = new TransformGroup();
+ scaleTransform = new ScaleTransform();
+ translateTransform = new TranslateTransform();
+ canvasTransformGroup.Children.Add(scaleTransform);
+ canvasTransformGroup.Children.Add(translateTransform);
+ FlowChartCanvas.RenderTransform = canvasTransformGroup;
+ #endregion
+ }
+
+ ///
+ /// 鼠标在画布移动。
+ /// 选择控件状态下,调整选择框大小
+ /// 连接状态下,实时更新连接线的终点位置。
+ /// 移动画布状态下,移动画布。
+ ///
+ private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
+ {
+ var myData = GlobalJunctionData.MyGlobalConnectingData;
+ if (myData.IsCreateing && e.LeftButton == MouseButtonState.Pressed)
+ {
+
+ if (myData.Type == JunctionOfConnectionType.Invoke)
+ {
+ ViewModel.IsConnectionInvokeNode = true; // 正在连接节点的调用关系
+
+ }
+ else
+ {
+ ViewModel.IsConnectionArgSourceNode = true; // 正在连接节点的调用关系
+ }
+ var currentPoint = e.GetPosition(FlowChartCanvas);
+ currentPoint.X -= 2;
+ currentPoint.Y -= 2;
+ myData.UpdatePoint(currentPoint);
+ return;
+ }
+
+
+
+ if (IsCanvasDragging && e.MiddleButton == MouseButtonState.Pressed) // 正在移动画布(按住中键)
+ {
+ Point currentMousePosition = e.GetPosition(this);
+ double deltaX = currentMousePosition.X - startCanvasDragPoint.X;
+ double deltaY = currentMousePosition.Y - startCanvasDragPoint.Y;
+
+ translateTransform.X += deltaX;
+ translateTransform.Y += deltaY;
+
+ startCanvasDragPoint = currentMousePosition;
+
+ foreach (var line in Connections)
+ {
+ line.RefreshLine(); // 画布移动时刷新所有连接线
+ }
+ }
+
+ if (IsSelectControl) // 正在选取节点
+ {
+ IsSelectDragging = e.LeftButton == MouseButtonState.Pressed;
+ // 获取当前鼠标位置
+ Point currentPoint = e.GetPosition(FlowChartCanvas);
+
+ // 更新选取矩形的位置和大小
+ double x = Math.Min(currentPoint.X, startSelectControolPoint.X);
+ double y = Math.Min(currentPoint.Y, startSelectControolPoint.Y);
+ double width = Math.Abs(currentPoint.X - startSelectControolPoint.X);
+ double height = Math.Abs(currentPoint.Y - startSelectControolPoint.Y);
+
+ Canvas.SetLeft(SelectionRectangle, x);
+ Canvas.SetTop(SelectionRectangle, y);
+ SelectionRectangle.Width = width;
+ SelectionRectangle.Height = height;
+
+ }
+ }
+
+
+ ///
+ /// 放置操作,根据拖放数据创建相应的控件,并处理相关操作
+ ///
+ ///
+ ///
+ private void FlowChartCanvas_Drop(object sender, DragEventArgs e)
+ {
+ try
+ {
+ var canvasDropPosition = e.GetPosition(FlowChartCanvas); // 更新画布落点
+ PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y);
+ if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas))
+ {
+ if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeData nodeData)
+ {
+ Task.Run(async () =>
+ {
+ await EnvDecorator.CreateNodeAsync(nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象
+ });
+ }
+ }
+ else if (e.Data.GetDataPresent(MouseNodeType.CreateBaseNodeInCanvas))
+ {
+ if (e.Data.GetData(MouseNodeType.CreateBaseNodeInCanvas) is Type droppedType)
+ {
+ NodeControlType nodeControlType = droppedType switch
+ {
+ Type when typeof(ConditionRegionControl).IsAssignableFrom(droppedType) => NodeControlType.ConditionRegion, // 条件区域
+ Type when typeof(ConditionNodeControl).IsAssignableFrom(droppedType) => NodeControlType.ExpCondition,
+ Type when typeof(ExpOpNodeControl).IsAssignableFrom(droppedType) => NodeControlType.ExpOp,
+ Type when typeof(GlobalDataControl).IsAssignableFrom(droppedType) => NodeControlType.GlobalData,
+ Type when typeof(ScriptNodeControl).IsAssignableFrom(droppedType) => NodeControlType.Script,
+ Type when typeof(NetScriptNodeControl).IsAssignableFrom(droppedType) => NodeControlType.NetScript,
+ _ => NodeControlType.None,
+ };
+ if (nodeControlType != NodeControlType.None)
+ {
+ Task.Run(async () =>
+ {
+ await EnvDecorator.CreateNodeAsync(nodeControlType, position); // 创建基础节点对象
+ });
+ }
+ }
+ }
+ e.Handled = true;
+ }
+ catch (Exception ex)
+ {
+ SereinEnv.WriteLine(InfoType.ERROR, ex.ToString());
+ }
+ }
+
+ ///
+ /// 拖动效果,根据拖放数据是否为指定类型设置拖放效果
+ ///
+ ///
+ ///
+ private void FlowChartCanvas_DragOver(object sender, DragEventArgs e)
+ {
+ if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas)
+ || e.Data.GetDataPresent(MouseNodeType.CreateBaseNodeInCanvas))
+ {
+ e.Effects = DragDropEffects.Move;
+ }
+ else
+ {
+ e.Effects = DragDropEffects.None;
+ }
+ e.Handled = true;
+ }
+
+
+
+
+
+
+ ///
+ /// 在画布中尝试选取控件
+ ///
+ ///
+ ///
+ private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
+ {
+ if (GlobalJunctionData.MyGlobalConnectingData.IsCreateing)
+ {
+ return;
+ }
+ if (!IsSelectControl)
+ {
+ // 进入选取状态
+ IsSelectControl = true;
+ IsSelectDragging = false; // 初始化为非拖动状态
+
+ // 记录鼠标起始点
+ startSelectControolPoint = e.GetPosition(FlowChartCanvas);
+
+ // 初始化选取矩形的位置和大小
+ Canvas.SetLeft(SelectionRectangle, startSelectControolPoint.X);
+ Canvas.SetTop(SelectionRectangle, startSelectControolPoint.Y);
+ SelectionRectangle.Width = 0;
+ SelectionRectangle.Height = 0;
+
+ // 显示选取矩形
+ SelectionRectangle.Visibility = Visibility.Visible;
+ SelectionRectangle.ContextMenu ??= ConfiguerSelectionRectangle();
+
+ // 捕获鼠标,以便在鼠标移动到Canvas外部时仍能处理事件
+ FlowChartCanvas.CaptureMouse();
+ }
+ else
+ {
+ // 如果已经是选取状态,单击则认为结束框选
+ CompleteSelection();
+ }
+
+ e.Handled = true; // 防止事件传播影响其他控件
+ }
+
+ ///
+ /// 在画布中释放鼠标按下,结束选取状态 / 停止创建连线,尝试连接节点
+ ///
+ ///
+ ///
+ private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
+ {
+ if (IsSelectControl)
+ {
+ // 松开鼠标时判断是否为拖动操作
+ if (IsSelectDragging)
+ {
+ // 完成拖动框选
+ CompleteSelection();
+ }
+
+ // 释放鼠标捕获
+ FlowChartCanvas.ReleaseMouseCapture();
+ }
+
+ // 创建连线
+ if (GlobalJunctionData.MyGlobalConnectingData is ConnectingData myData && myData.IsCreateing)
+ {
+
+ if (myData.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));
+
+ if (changingJunctionRect.Contains(currentendPoint)) // 可以创建连接
+ {
+ #region 方法调用关系创建
+ if (myData.Type == JunctionOfConnectionType.Invoke)
+ {
+ this.EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
+ myData.StartJunction.JunctionType,
+ myData.CurrentJunction.JunctionType,
+ myData.ConnectionInvokeType);
+ }
+ #endregion
+
+ #region 参数来源关系创建
+ else if (myData.Type == JunctionOfConnectionType.Arg)
+ {
+ var argIndex = 0;
+ if (myData.StartJunction is ArgJunctionControl argJunction1)
+ {
+ argIndex = argJunction1.ArgIndex;
+ }
+ else if (myData.CurrentJunction is ArgJunctionControl argJunction2)
+ {
+ argIndex = argJunction2.ArgIndex;
+ }
+
+ this.EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
+ myData.StartJunction.JunctionType,
+ myData.CurrentJunction.JunctionType,
+ myData.ConnectionArgSourceType,
+ argIndex);
+ }
+ #endregion
+ }
+ EndConnection();
+ }
+
+ }
+ e.Handled = true;
+
+ }
+
+
+
+
+ #region 拖动画布实现缩放平移效果
+ private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e)
+ {
+ IsCanvasDragging = true;
+ startCanvasDragPoint = e.GetPosition(this);
+ FlowChartCanvas.CaptureMouse();
+ e.Handled = true; // 防止事件传播影响其他控件
+ }
+
+ private void FlowChartCanvas_MouseUp(object sender, MouseButtonEventArgs e)
+ {
+
+
+
+ if (IsCanvasDragging)
+ {
+ IsCanvasDragging = false;
+ FlowChartCanvas.ReleaseMouseCapture();
+ }
+ }
+
+ // 单纯缩放画布,不改变画布大小
+ private void FlowChartCanvas_MouseWheel(object sender, MouseWheelEventArgs e)
+ {
+ // if (Keyboard.IsKeyDown(Key.LeftCtrl) || Keyboard.IsKeyDown(Key.RightCtrl))
+ {
+ if (e.Delta < 0 && scaleTransform.ScaleX < 0.05) return;
+ if (e.Delta > 0 && scaleTransform.ScaleY > 2.0) return;
+ // 获取鼠标在 Canvas 内的相对位置
+ var mousePosition = e.GetPosition(FlowChartCanvas);
+
+ // 缩放因子,根据滚轮方向调整
+ //double zoomFactor = e.Delta > 0 ? 0.1 : -0.1;
+ double zoomFactor = e.Delta > 0 ? 1.1 : 0.9;
+
+ // 当前缩放比例
+ double oldScale = scaleTransform.ScaleX;
+ double newScale = oldScale * zoomFactor;
+ //double newScale = oldScale + zoomFactor;
+ // 更新缩放比例
+ scaleTransform.ScaleX = newScale;
+ scaleTransform.ScaleY = newScale;
+
+ // 计算缩放前后鼠标相对于 Canvas 的位置差异
+ // double offsetX = mousePosition.X - (mousePosition.X * zoomFactor);
+ // double offsetY = mousePosition.Y - (mousePosition.Y * zoomFactor);
+
+ // 更新 TranslateTransform,确保以鼠标位置为中心进行缩放
+ translateTransform.X -= (mousePosition.X * (newScale - oldScale));
+ translateTransform.Y -= (mousePosition.Y * (newScale - oldScale));
+ }
+ }
+
+ // 设置画布宽度高度
+ private void InitializeCanvas(double width, double height)
+ {
+ FlowChartCanvas.Width = width;
+ FlowChartCanvas.Height = height;
+ }
+
+
+ #region 动态调整区域大小
+ //private void Thumb_DragDelta_TopLeft(object sender, DragDeltaEventArgs e)
+ //{
+ // // 从左上角调整大小
+ // double newWidth = Math.Max(FlowChartCanvas.ActualWidth - e.HorizontalChange, 0);
+ // double newHeight = Math.Max(FlowChartCanvas.ActualHeight - e.VerticalChange, 0);
+
+ // FlowChartCanvas.Width = newWidth;
+ // FlowChartCanvas.Height = newHeight;
+
+ // Canvas.SetLeft(FlowChartCanvas, Canvas.GetLeft(FlowChartCanvas) + e.HorizontalChange);
+ // Canvas.SetTop(FlowChartCanvas, Canvas.GetTop(FlowChartCanvas) + e.VerticalChange);
+ //}
+
+ //private void Thumb_DragDelta_TopRight(object sender, DragDeltaEventArgs e)
+ //{
+ // // 从右上角调整大小
+ // double newWidth = Math.Max(FlowChartCanvas.ActualWidth + e.HorizontalChange, 0);
+ // double newHeight = Math.Max(FlowChartCanvas.ActualHeight - e.VerticalChange, 0);
+
+ // FlowChartCanvas.Width = newWidth;
+ // FlowChartCanvas.Height = newHeight;
+
+ // Canvas.SetTop(FlowChartCanvas, Canvas.GetTop(FlowChartCanvas) + e.VerticalChange);
+ //}
+
+ //private void Thumb_DragDelta_BottomLeft(object sender, DragDeltaEventArgs e)
+ //{
+ // // 从左下角调整大小
+ // double newWidth = Math.Max(FlowChartCanvas.ActualWidth - e.HorizontalChange, 0);
+ // double newHeight = Math.Max(FlowChartCanvas.ActualHeight + e.VerticalChange, 0);
+
+ // FlowChartCanvas.Width = newWidth;
+ // FlowChartCanvas.Height = newHeight;
+
+ // Canvas.SetLeft(FlowChartCanvas, Canvas.GetLeft(FlowChartCanvas) + e.HorizontalChange);
+ //}
+
+ private void Thumb_DragDelta_BottomRight(object sender, DragDeltaEventArgs e)
+ {
+ // 获取缩放后的水平和垂直变化
+ double horizontalChange = e.HorizontalChange * scaleTransform.ScaleX;
+ double verticalChange = e.VerticalChange * scaleTransform.ScaleY;
+
+ // 计算新的宽度和高度,确保不会小于400
+ double newWidth = Math.Max(FlowChartCanvas.ActualWidth + horizontalChange, 400);
+ double newHeight = Math.Max(FlowChartCanvas.ActualHeight + verticalChange, 400);
+
+ newHeight = newHeight < 400 ? 400 : newHeight;
+ newWidth = newWidth < 400 ? 400 : newWidth;
+
+ InitializeCanvas(newWidth, newHeight);
+
+ //// 从右下角调整大小
+ //double newWidth = Math.Max(FlowChartCanvas.ActualWidth + e.HorizontalChange * scaleTransform.ScaleX, 0);
+ //double newHeight = Math.Max(FlowChartCanvas.ActualHeight + e.VerticalChange * scaleTransform.ScaleY, 0);
+
+ //newWidth = newWidth < 400 ? 400 : newWidth;
+ //newHeight = newHeight < 400 ? 400 : newHeight;
+
+ //if (newWidth > 400 && newHeight > 400)
+ //{
+ // FlowChartCanvas.Width = newWidth;
+ // FlowChartCanvas.Height = newHeight;
+
+ // double x = e.HorizontalChange > 0 ? -0.5 : 0.5;
+ // double y = e.VerticalChange > 0 ? -0.5 : 0.5;
+
+ // double deltaX = x * scaleTransform.ScaleX;
+ // double deltaY = y * scaleTransform.ScaleY;
+ // Test(deltaX, deltaY);
+ //}
+ }
+
+ //private void Thumb_DragDelta_Left(object sender, DragDeltaEventArgs e)
+ //{
+ // // 从左侧调整大小
+ // double newWidth = Math.Max(FlowChartCanvas.ActualWidth - e.HorizontalChange, 0);
+
+ // FlowChartCanvas.Width = newWidth;
+ // Canvas.SetLeft(FlowChartCanvas, Canvas.GetLeft(FlowChartCanvas) + e.HorizontalChange);
+ //}
+
+ private void Thumb_DragDelta_Right(object sender, DragDeltaEventArgs e)
+ {
+ //从右侧调整大小
+ // 获取缩放后的水平变化
+ double horizontalChange = e.HorizontalChange * scaleTransform.ScaleX;
+
+ // 计算新的宽度,确保不会小于400
+ double newWidth = Math.Max(FlowChartCanvas.ActualWidth + horizontalChange, 400);
+
+ newWidth = newWidth < 400 ? 400 : newWidth;
+ InitializeCanvas(newWidth, FlowChartCanvas.Height);
+
+ }
+
+ //private void Thumb_DragDelta_Top(object sender, DragDeltaEventArgs e)
+ //{
+ // // 从顶部调整大小
+ // double newHeight = Math.Max(FlowChartCanvas.ActualHeight - e.VerticalChange, 0);
+
+ // FlowChartCanvas.Height = newHeight;
+ // Canvas.SetTop(FlowChartCanvas, Canvas.GetTop(FlowChartCanvas) + e.VerticalChange);
+ //}
+
+ private void Thumb_DragDelta_Bottom(object sender, DragDeltaEventArgs e)
+ {
+ // 获取缩放后的垂直变化
+ double verticalChange = e.VerticalChange * scaleTransform.ScaleY;
+ // 计算新的高度,确保不会小于400
+ double newHeight = Math.Max(FlowChartCanvas.ActualHeight + verticalChange, 400);
+ newHeight = newHeight < 400 ? 400 : newHeight;
+ InitializeCanvas(FlowChartCanvas.Width, newHeight);
+ }
+
+
+ private void Test(double deltaX, double deltaY)
+ {
+ //Console.WriteLine((translateTransform.X, translateTransform.Y));
+ //translateTransform.X += deltaX;
+ //translateTransform.Y += deltaY;
+ }
+
+ #endregion
+ #endregion
+
+
+
+ /// 完成选取操作
+ ///
+ private void CompleteSelection()
+ {
+ IsSelectControl = false;
+
+ // 隐藏选取矩形
+ SelectionRectangle.Visibility = Visibility.Collapsed;
+
+ // 获取选取范围
+ Rect selectionArea = new Rect(Canvas.GetLeft(SelectionRectangle),
+ Canvas.GetTop(SelectionRectangle),
+ SelectionRectangle.Width,
+ SelectionRectangle.Height);
+
+ // 处理选取范围内的控件
+ // selectNodeControls.Clear();
+ foreach (UIElement element in FlowChartCanvas.Children)
+ {
+ Rect elementBounds = new Rect(Canvas.GetLeft(element), Canvas.GetTop(element),
+ element.RenderSize.Width, element.RenderSize.Height);
+
+ if (selectionArea.Contains(elementBounds))
+ {
+ if (element is NodeControlBase control)
+ {
+ if (!selectNodeControls.Contains(control))
+ {
+ selectNodeControls.Add(control);
+ }
+ }
+ }
+ }
+
+ // 选中后的操作
+ SelectedNode();
+ }
+
+
+ private void SelectedNode()
+ {
+
+ if (selectNodeControls.Count == 0)
+ {
+ //Console.WriteLine($"没有选择控件");
+ SelectionRectangle.Visibility = Visibility.Collapsed;
+ return;
+ }
+ if (selectNodeControls.Count == 1)
+ {
+ // ChangeViewerObjOfNode(selectNodeControls[0]);
+ }
+
+ //Console.WriteLine($"一共选取了{selectNodeControls.Count}个控件");
+ foreach (var node in selectNodeControls)
+ {
+ //node.ViewModel.IsSelect =true;
+ // node.ViewModel.CancelSelect();
+ node.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FFC700"));
+ node.BorderThickness = new Thickness(4);
+ }
+ }
+
+ ///
+ /// 结束连接操作,清理状态并移除虚线。
+ ///
+ private void EndConnection()
+ {
+ Mouse.OverrideCursor = null; // 恢复视觉效果
+ ViewModel.IsConnectionArgSourceNode = false;
+ ViewModel.IsConnectionInvokeNode = false;
+ GlobalJunctionData.OK();
+ }
+
+ ///
+ /// 创建菜单子项
+ ///
+ ///
+ ///
+ ///
+ public static MenuItem CreateMenuItem(string header, RoutedEventHandler handler)
+ {
+ var menuItem = new MenuItem { Header = header };
+ menuItem.Click += handler;
+ return menuItem;
+ }
+
+
+
+ private ContextMenu ConfiguerSelectionRectangle()
+ {
+ var contextMenu = new ContextMenu();
+ contextMenu.Items.Add(CreateMenuItem("删除", (s, e) =>
+ {
+ if (selectNodeControls.Count > 0)
+ {
+ foreach (var node in selectNodeControls.ToArray())
+ {
+ var guid = node?.ViewModel?.NodeModel?.Guid;
+ if (!string.IsNullOrEmpty(guid))
+ {
+ EnvDecorator.RemoveNodeAsync(guid);
+ }
+ }
+ }
+ SelectionRectangle.Visibility = Visibility.Collapsed;
+ }));
+ return contextMenu;
+ // nodeControl.ContextMenu = contextMenu;
+ }
+
+
+
+ }
+}
diff --git a/Workbench/Views/FlowEditView.xaml b/Workbench/Views/FlowEditView.xaml
new file mode 100644
index 0000000..41fbebe
--- /dev/null
+++ b/Workbench/Views/FlowEditView.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
diff --git a/Workbench/Views/FlowEditView.xaml.cs b/Workbench/Views/FlowEditView.xaml.cs
new file mode 100644
index 0000000..c23fe16
--- /dev/null
+++ b/Workbench/Views/FlowEditView.xaml.cs
@@ -0,0 +1,31 @@
+using Serein.Workbench.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Serein.Workbench.Views
+{
+ ///
+ /// FlowEditView.xaml 的交互逻辑
+ ///
+ public partial class FlowEditView : UserControl
+ {
+ public FlowEditView()
+ {
+ this.DataContext = App.GetService().FlowEditViewModel;
+
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Workbench/Views/FlowLibrarysView.xaml b/Workbench/Views/FlowLibrarysView.xaml
new file mode 100644
index 0000000..8c86de0
--- /dev/null
+++ b/Workbench/Views/FlowLibrarysView.xaml
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Workbench/Views/FlowLibrarysView.xaml.cs b/Workbench/Views/FlowLibrarysView.xaml.cs
new file mode 100644
index 0000000..cd9f80a
--- /dev/null
+++ b/Workbench/Views/FlowLibrarysView.xaml.cs
@@ -0,0 +1,30 @@
+using Serein.Workbench.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Serein.Workbench.Views
+{
+ ///
+ /// FlowLibrarysView.xaml 的交互逻辑
+ ///
+ public partial class FlowLibrarysView : UserControl
+ {
+ public FlowLibrarysView()
+ {
+ this.DataContext = App.GetService().FlowLibrarysViewModel;
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Workbench/Views/FlowWorkbenchView.xaml b/Workbench/Views/FlowWorkbenchView.xaml
new file mode 100644
index 0000000..051570f
--- /dev/null
+++ b/Workbench/Views/FlowWorkbenchView.xaml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Workbench/Views/FlowWorkbenchView.xaml.cs b/Workbench/Views/FlowWorkbenchView.xaml.cs
new file mode 100644
index 0000000..98b6d79
--- /dev/null
+++ b/Workbench/Views/FlowWorkbenchView.xaml.cs
@@ -0,0 +1,45 @@
+using Serein.Workbench.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Shapes;
+
+namespace Serein.Workbench.Views
+{
+ ///
+ /// FlowWorkbenchView.xaml 的交互逻辑
+ ///
+ public partial class FlowWorkbenchView : Window
+ {
+ public FlowWorkbenchView()
+ {
+ this.DataContext = App.GetService().FlowWorkbenchViewModel;
+ InitializeComponent();
+ }
+
+ private void Window_Loaded(object sender, RoutedEventArgs e)
+ {
+ this.MaxHeight = SystemParameters.PrimaryScreenHeight;
+ this.WindowState = WindowState.Maximized;
+ // 设置全屏
+ /*this.WindowState = System.Windows.WindowState.Normal;
+ this.WindowStyle = System.Windows.WindowStyle.None;
+ this.ResizeMode = System.Windows.ResizeMode.NoResize;
+ this.Topmost = true;
+
+ this.Left = 0.0;
+ this.Top = 0.0;
+ this.Width = System.Windows.SystemParameters.PrimaryScreenWidth;
+ this.Height = System.Windows.SystemParameters.PrimaryScreenHeight;*/
+ }
+ }
+}
diff --git a/Workbench/Views/MainMenuBarView.xaml b/Workbench/Views/MainMenuBarView.xaml
new file mode 100644
index 0000000..5c6d43d
--- /dev/null
+++ b/Workbench/Views/MainMenuBarView.xaml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
diff --git a/Workbench/Views/MainMenuBarView.xaml.cs b/Workbench/Views/MainMenuBarView.xaml.cs
new file mode 100644
index 0000000..82b89a1
--- /dev/null
+++ b/Workbench/Views/MainMenuBarView.xaml.cs
@@ -0,0 +1,30 @@
+using Serein.Workbench.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Serein.Workbench.Views
+{
+ ///
+ /// MainMenuBarView.xaml 的交互逻辑
+ ///
+ public partial class MainMenuBarView : UserControl
+ {
+ public MainMenuBarView()
+ {
+ this.DataContext = App.GetService().MainViewModel;
+ InitializeComponent();
+ }
+ }
+}
diff --git a/Workbench/Views/MainView.xaml b/Workbench/Views/MainView.xaml
new file mode 100644
index 0000000..84f6710
--- /dev/null
+++ b/Workbench/Views/MainView.xaml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/Workbench/Views/MainView.xaml.cs b/Workbench/Views/MainView.xaml.cs
new file mode 100644
index 0000000..88c51d7
--- /dev/null
+++ b/Workbench/Views/MainView.xaml.cs
@@ -0,0 +1,30 @@
+using Serein.Workbench.ViewModels;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows;
+using System.Windows.Controls;
+using System.Windows.Data;
+using System.Windows.Documents;
+using System.Windows.Input;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows.Navigation;
+using System.Windows.Shapes;
+
+namespace Serein.Workbench.Views
+{
+ ///
+ /// MainView.xaml 的交互逻辑
+ ///
+ public partial class MainView : UserControl
+ {
+ public MainView()
+ {
+ this.DataContext = App.GetService().MainViewModel;
+ InitializeComponent();
+ }
+ }
+}