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(); + } + } +}