重新设计流程画布Canvas与节点Node的关联

This commit is contained in:
fengjiayi
2025-03-24 15:44:34 +08:00
parent cf7760ef84
commit b1a9679138
15 changed files with 322 additions and 55 deletions

View File

@@ -313,13 +313,12 @@ namespace Serein.Library.Api
/// </summary> /// </summary>
public class CanvasCreateEventArgs : FlowEventArgs public class CanvasCreateEventArgs : FlowEventArgs
{ {
public CanvasCreateEventArgs( public CanvasCreateEventArgs(FlowCanvasModel model)
FlowCanvasInfo info)
{ {
Info = info; Model = model;
} }
public FlowCanvasInfo Info { get; } public FlowCanvasModel Model { get; }
} }
/// <summary> /// <summary>
@@ -888,7 +887,7 @@ namespace Serein.Library.Api
/// </summary> /// </summary>
/// <param name="canvasGuid">画布Guid</param> /// <param name="canvasGuid">画布Guid</param>
/// <returns></returns> /// <returns></returns>
Task<bool> RemoteCanvasAsync(string canvasGuid); Task<bool> RemoveCanvasAsync(string canvasGuid);
/// <summary> /// <summary>

View File

@@ -783,9 +783,9 @@ namespace Serein.NodeFlow.Env
Name = canvasName, Name = canvasName,
Width = height, Width = height,
}; };
var info = model.ToInfo();
FlowCanvass.Add(model.Guid, model); FlowCanvass.Add(model.Guid, model);
OnCanvasCreate.Invoke(new CanvasCreateEventArgs(info)); OnCanvasCreate.Invoke(new CanvasCreateEventArgs(model));
var info = model.ToInfo();
return Task.FromResult(info); return Task.FromResult(info);
} }
@@ -794,7 +794,7 @@ namespace Serein.NodeFlow.Env
/// </summary> /// </summary>
/// <param name="canvasGuid">画布Guid</param> /// <param name="canvasGuid">画布Guid</param>
/// <returns></returns> /// <returns></returns>
public Task<bool> RemoteCanvasAsync(string canvasGuid) public Task<bool> RemoveCanvasAsync(string canvasGuid)
{ {
if (!FlowCanvass.TryGetValue(canvasGuid, out var model)) if (!FlowCanvass.TryGetValue(canvasGuid, out var model))

View File

@@ -248,9 +248,9 @@ namespace Serein.NodeFlow.Env
/// </summary> /// </summary>
/// <param name="canvasGuid">画布Guid</param> /// <param name="canvasGuid">画布Guid</param>
/// <returns></returns> /// <returns></returns>
public async Task<bool> RemoteCanvasAsync(string canvasGuid) public async Task<bool> RemoveCanvasAsync(string canvasGuid)
{ {
return await currentFlowEnvironment.RemoteCanvasAsync(canvasGuid); return await currentFlowEnvironment.RemoveCanvasAsync(canvasGuid);
} }

View File

@@ -321,7 +321,7 @@ namespace Serein.NodeFlow.Env
[AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveCanvas, IsReturnValue = false)] [AutoSocketHandle(ThemeValue = EnvMsgTheme.RemoveCanvas, IsReturnValue = false)]
public async Task<object> RemoveCanvas([Needful] string canvasGuid) public async Task<object> RemoveCanvas([Needful] string canvasGuid)
{ {
var result = await environment.RemoteCanvasAsync(canvasGuid); // 监听到客户端创建节点的请求 var result = await environment.RemoveCanvasAsync(canvasGuid); // 监听到客户端创建节点的请求
return new { state = result} ; return new { state = result} ;
} }

View File

@@ -458,7 +458,7 @@ namespace Serein.NodeFlow.Env
/// </summary> /// </summary>
/// <param name="canvasGuid">画布Guid</param> /// <param name="canvasGuid">画布Guid</param>
/// <returns></returns> /// <returns></returns>
public async Task<bool> RemoteCanvasAsync(string canvasGuid) public async Task<bool> RemoveCanvasAsync(string canvasGuid)
{ {
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveCanvas, new var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.RemoveCanvas, new
{ {

View File

@@ -10,7 +10,7 @@ namespace Serein.Workbench.Api
/// <summary> /// <summary>
/// 流程事件管理,转发流程运行环境中触发的事件到工作台各个订阅者 /// 流程事件管理,转发流程运行环境中触发的事件到工作台各个订阅者
/// </summary> /// </summary>
internal interface IFlowEEForwardingService : IFlowEnvironmentEvent public interface IFlowEEForwardingService : IFlowEnvironmentEvent
{ {
} }

View File

@@ -39,7 +39,7 @@ namespace Serein.Workbench
{ {
collection.AddSingleton<IFlowEEForwardingService, FlowEEForwardingService>(); // 流程事件管理 collection.AddSingleton<IFlowEEForwardingService, FlowEEForwardingService>(); // 流程事件管理
collection.AddSingleton<IWorkbenchEventService, WorkbenchEventService>(); // 流程事件管理 collection.AddSingleton<IWorkbenchEventService, WorkbenchEventService>(); // 流程事件管理
collection.AddSingleton<NodeControlService>(); // 节点操作管理 collection.AddSingleton<FlowNodeService>(); // 节点操作管理
// collection.AddSingleton<IKeyEventService, KeyEventService>(); // 按键事件管理 // collection.AddSingleton<IKeyEventService, KeyEventService>(); // 按键事件管理
//collection.AddSingleton<FlowNodeControlService>(); // 流程节点控件管理 //collection.AddSingleton<FlowNodeControlService>(); // 流程节点控件管理
} }

View File

@@ -0,0 +1,192 @@
using Serein.Library;
using Serein.Library.Api;
using Serein.Workbench.Api;
using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel;
using Serein.Workbench.Views;
namespace Serein.Workbench.Services
{
/// <summary>
/// 流程节点管理
/// </summary>
public class FlowNodeService
{
#region
public Action<FlowCanvasView> OnCreateFlowCanvasView { get; set; }
public Action<string> OnRemoveFlowCanvasView { get; set; }
#endregion
#region
/// <summary>
/// 当前查看的画布
/// </summary>
public FlowCanvasView CurrentSelectCanvas { get; set; }
/// <summary>
/// 当前拖动的方法信息
/// </summary>
public MethodDetailsInfo? CurrentDragMdInfo { get; set; }
/// <summary>
/// 当前需要创建的节点类型
/// </summary>
public NodeControlType? CurrentNodeControlType { get; set; }
/// <summary>
/// 当前鼠标位置
/// </summary>
public PositionOfUI? CurrentMouseLocation { get; set; }
/// <summary>
/// 当前选中的节点
/// </summary>
public NodeControlBase? CurrentSelectNodeControl { get; set; }
#endregion
/// <summary>
/// 连接开始节点
/// </summary>
public NodeControlBase? ConnectionStartNode { get; set; }
/// <summary>
/// 连接最终落点节点
/// </summary>
public NodeControlBase? ConnectionEndNode { get; set; }
/*
*/
/// <summary>
/// 记录流程画布
/// </summary>
private readonly Dictionary<string, FlowCanvasView> FlowCanvasViews = [];
/// <summary>
/// 记录加载的节点
/// </summary>
private readonly Dictionary<string, NodeControlViewModelBase> NodeControls = [];
private readonly IFlowEnvironment flowEnvironment;
private readonly IFlowEEForwardingService flowEEForwardingService;
#region
public FlowNodeService(IFlowEnvironment flowEnvironment,
IFlowEEForwardingService flowEEForwardingService)
{
this.flowEnvironment = flowEnvironment;
this.flowEEForwardingService = flowEEForwardingService;
InitFlowEvent();
}
public void InitFlowEvent()
{
flowEEForwardingService.OnCanvasCreate += FlowEEForwardingService_OnCanvasCreate;
flowEEForwardingService.OnCanvasRemove += FlowEEForwardingService_OnCanvasRemove;
flowEEForwardingService.OnNodeCreate += FlowEEForwardingService_OnNodeCreate;
flowEEForwardingService.OnNodeRemove += FlowEEForwardingService_OnNodeRemove;
}
private void FlowEEForwardingService_OnNodeRemove(NodeRemoveEventArgs eventArgs)
{
throw new NotImplementedException();
}
private void FlowEEForwardingService_OnNodeCreate(NodeCreateEventArgs eventArgs)
{
throw new NotImplementedException();
}
private void FlowEEForwardingService_OnCanvasRemove(CanvasRemoveEventArgs eventArgs)
{
OnRemoveFlowCanvasView.Invoke(eventArgs.CanvasGuid);
}
private void FlowEEForwardingService_OnCanvasCreate(CanvasCreateEventArgs eventArgs)
{
var info = eventArgs.Model;
var model = eventArgs.Model;
FlowCanvasView canvasView = new FlowCanvasView();
canvasView.ViewModel.CanvasGuid = info.Guid;
canvasView.ViewModel.Model = model;
FlowCanvasViews.Add(info.Guid, canvasView);
OnCreateFlowCanvasView.Invoke(canvasView); // 传递给订阅者
}
#endregion
#region
/// <summary>
/// 向运行环境发出请求:添加画布
/// </summary>
/// <returns></returns>
public void CreateFlowCanvas()
{
string canvasName = "";
int height = 1000;
int width = 600;
_ = flowEnvironment.CreateCanvasAsync(canvasName, width, height);
}
/// <summary>
/// 向运行环境发出请求:移除画布
/// </summary>
public void RemoveFlowCanvas()
{
if (CurrentSelectCanvas is null)
{
return;
}
_ = flowEnvironment.RemoveCanvasAsync(CurrentSelectCanvas.ViewModel.CanvasGuid);
}
/// <summary>
/// 向运行环境发出请求:创建节点
/// </summary>
public void CreateNode()
{
string canvasGuid = CurrentSelectCanvas.ViewModel.CanvasGuid;
NodeControlType? nodeType = CurrentNodeControlType;
PositionOfUI? position = CurrentMouseLocation;
MethodDetailsInfo? methodDetailsInfo = CurrentDragMdInfo;
if (nodeType is null)
{
return;
}
if (position is null)
{
return;
}
_ = flowEnvironment.CreateNodeAsync(canvasGuid, (NodeControlType)nodeType, position, methodDetailsInfo);
}
/// <summary>
/// 向运行环境发出请求:移除节点
/// </summary>
public void RemoteNode()
{
NodeControlBase? node = CurrentSelectNodeControl;
if (node is null)
{
return;
}
var model = node.ViewModel.NodeModel;
if (model is null)
{
return;
}
_ = flowEnvironment.RemoveNodeAsync(model.CanvasGuid, model.Guid);
}
#endregion
}
}

View File

@@ -1,13 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Services
{
internal class ProjectService
{
}
}

View File

@@ -1,4 +1,6 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library;
using Serein.Workbench.Node.View;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -9,6 +11,12 @@ namespace Serein.Workbench.ViewModels
{ {
public partial class FlowCanvasViewModel : ObservableObject public partial class FlowCanvasViewModel : ObservableObject
{ {
/// <summary>
/// 画布当前选中的节点
/// </summary>
public NodeControlBase CurrentSelectNodeControl { get; set; }
/// <summary> /// <summary>
/// 正在创建节点方法调用关系 /// 正在创建节点方法调用关系
/// </summary> /// </summary>
@@ -33,6 +41,12 @@ namespace Serein.Workbench.ViewModels
[ObservableProperty] [ObservableProperty]
private string _canvasGuid; private string _canvasGuid;
/// <summary>
/// 画布数据实体
/// </summary>
[ObservableProperty]
private FlowCanvasModel _model;
public FlowCanvasViewModel() public FlowCanvasViewModel()

View File

@@ -1,6 +1,7 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input; using CommunityToolkit.Mvvm.Input;
using Serein.Workbench.Models; using Serein.Workbench.Models;
using Serein.Workbench.Services;
using Serein.Workbench.Views; using Serein.Workbench.Views;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -10,6 +11,7 @@ using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using System.Windows.Input; using System.Windows.Input;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
namespace Serein.Workbench.ViewModels namespace Serein.Workbench.ViewModels
{ {
@@ -18,7 +20,7 @@ namespace Serein.Workbench.ViewModels
/// </summary> /// </summary>
public partial class FlowEditViewModel : ObservableObject public partial class FlowEditViewModel : ObservableObject
{ {
public ObservableCollection<FlowCanvasModel> Tabs { get; set; } public ObservableCollection<FlowCanvasModel> Tabs { get; set; } = [];
public ICommand AddTabCommand { get; set; } public ICommand AddTabCommand { get; set; }
public ICommand RemoveTabCommand { get; set; } public ICommand RemoveTabCommand { get; set; }
public ICommand RenameTabCommand { get; set; } public ICommand RenameTabCommand { get; set; }
@@ -27,39 +29,59 @@ namespace Serein.Workbench.ViewModels
private FlowCanvasModel _selectedTab; private FlowCanvasModel _selectedTab;
private int _addCount = 0; private int _addCount = 0;
private readonly FlowNodeService flowNodeService;
public FlowEditViewModel() public FlowEditViewModel(FlowNodeService flowNodeService)
{ {
Tabs = new ObservableCollection<FlowCanvasModel>(); this.flowNodeService = flowNodeService;
AddTabCommand = new RelayCommand(AddTab); AddTabCommand = new RelayCommand(AddTab);
RemoveTabCommand = new RelayCommand(RemoveTab, CanRemoveTab); RemoveTabCommand = new RelayCommand(RemoveTab, CanRemoveTab);
// 初始化时添加一个默认的Tab flowNodeService.OnCreateFlowCanvasView += OnCreateFlowCanvasView; // 环境创建了节点
AddTab(); // 添加一个默认选项卡 flowNodeService.OnRemoveFlowCanvasView += OnRemoveFlowCanvasView;
this.PropertyChanged += OnPropertyChanged;
} }
private void AddTab() private void OnPropertyChanged(object? value, PropertyChangedEventArgs e)
{ {
var flowCanvasView = new FlowCanvasView(); // 创建FlowCanvasView实例 if (nameof(SelectedTab).Equals(e.PropertyName) && value is FlowCanvasModel model)
Tabs.Add(new FlowCanvasModel { Content = flowCanvasView ,Name = $"New Tab {_addCount++}"});
SelectedTab = Tabs[Tabs.Count - 1]; // 选择刚添加的Tab
}
private void RemoveTab()
{
if (Tabs.Count > 0 && SelectedTab != null)
{ {
Tabs.Remove(SelectedTab); flowNodeService.CurrentSelectCanvas = model.Content; // 选中的视图发生改变
SelectedTab = Tabs.Count > 0 ? Tabs[Tabs.Count - 1] : null;
} }
} }
private bool CanRemoveTab() #region
private void OnCreateFlowCanvasView(FlowCanvasView FlowCanvasView)
{ {
return SelectedTab != null; var model = new FlowCanvasModel { Content = FlowCanvasView, Name = FlowCanvasView.ViewModel.Name };
Tabs.Add(model);
}
private void OnRemoveFlowCanvasView(string canvasGuid)
{
var tab = Tabs.FirstOrDefault(t => t.Content.ViewModel.Model.Guid.Equals(canvasGuid, StringComparison.OrdinalIgnoreCase));
if (tab is null)
{
return;
}
Tabs.Remove(tab);
Tabs.Remove(SelectedTab);
if(Tabs.Count > 0 && Tabs[^1] is FlowCanvasModel view )
{
SelectedTab = view;
}
}
#endregion
private void AddTab() => flowNodeService.CreateFlowCanvas();
private void RemoveTab()
{
if (Tabs.Count > 0 && SelectedTab != null) flowNodeService.RemoveFlowCanvas();
} }
private bool CanRemoveTab() => SelectedTab != null;
/// <summary> /// <summary>
/// 进入编辑模式 /// 进入编辑模式

View File

@@ -2,6 +2,7 @@
using Serein.Library; using Serein.Library;
using Serein.Workbench.Api; using Serein.Workbench.Api;
using Serein.Workbench.Models; using Serein.Workbench.Models;
using Serein.Workbench.Services;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.ObjectModel; using System.Collections.ObjectModel;

View File

@@ -1,12 +1,61 @@
using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Serein.Library.Api;
using System.Windows.Input;
namespace Serein.Workbench.ViewModels namespace Serein.Workbench.ViewModels
{ {
public class MainMenuBarViewModel : ObservableObject public class MainMenuBarViewModel : ObservableObject
{ {
public MainMenuBarViewModel() private readonly IFlowEnvironment environment;
/// <summary>
/// 保存项目
/// </summary>
public ICommand SaveProjectCommand { get; private set; }
/// <summary>
/// 加载本地文件
/// </summary>
public ICommand LoadLocalProjectCommand { get; private set; }
/// <summary>
/// 加载远程项目
/// </summary>
public ICommand LoadRemoteProjectCommand { get; private set; }
/// <summary>
/// 增加流程图
/// </summary>
public ICommand CreateFlowCanvasCommand { get; private set; }
/// <summary>
/// 增加流程图
/// </summary>
public ICommand RemoteFlowCanvasCommand { get; private set; }
/// <summary>
/// 打开环境输出窗口
/// </summary>
public ICommand OpenEnvOutWindowCommand { get; private set; }
/// <summary>
/// 打开动态编译窗口
/// </summary>
public ICommand OpenDynamicCompilerCommand { get; private set; }
public MainMenuBarViewModel(IFlowEnvironment environment)
{ {
this.environment = environment;
SaveProjectCommand = new RelayCommand(SaveProject);
} }
public void SaveProject()
{
}
} }
} }

View File

@@ -26,7 +26,7 @@ namespace Serein.Workbench.Views
/// </summary> /// </summary>
public partial class FlowCanvasView : UserControl public partial class FlowCanvasView : UserControl
{ {
private FlowCanvasViewModel ViewModel; public FlowCanvasViewModel ViewModel => ViewModel as FlowCanvasViewModel;
/// <summary> /// <summary>
/// 存储所有的连接。考虑集成在运行环境中。 /// 存储所有的连接。考虑集成在运行环境中。
/// </summary> /// </summary>

View File

@@ -10,25 +10,28 @@
<Menu DockPanel.Dock="Top" Grid.Row="0" Grid.ColumnSpan="5" Height="20"> <Menu DockPanel.Dock="Top" Grid.Row="0" Grid.ColumnSpan="5" Height="20">
<MenuItem Header="项目"> <MenuItem Header="项目">
<MenuItem Header="保存项目" ></MenuItem> <MenuItem Header="保存项目" ></MenuItem>
<MenuItem Header="打开本地文件" ></MenuItem> <MenuItem Header="加载本地项目" ></MenuItem>
<MenuItem Header="加载远程项目"></MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="拓展"> <MenuItem Header="编辑">
<MenuItem Header="动态编译" ></MenuItem> <MenuItem Header="增加画布"></MenuItem>
<MenuItem Header="删除当前画布"></MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="调试"> <MenuItem Header="调试">
<MenuItem Header="运行(从起始节点)"></MenuItem> <MenuItem Header="运行(从起始节点)"></MenuItem>
<MenuItem Header="运行(从选定节点)" ></MenuItem> <MenuItem Header="运行(从选定节点)" ></MenuItem>
<MenuItem Header="结束流程" ></MenuItem> <MenuItem Header="结束流程" ></MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="视图"> <MenuItem Header="视图">
<MenuItem Header="输出窗口" ></MenuItem> <MenuItem Header="输出窗口" ></MenuItem>
<MenuItem Header="重置画布"></MenuItem> <MenuItem Header="重置画布"></MenuItem>
<MenuItem Header="定位节点" ></MenuItem> <MenuItem Header="定位节点" ></MenuItem>
</MenuItem> </MenuItem>
<MenuItem Header="远程"> <MenuItem Header="拓展">
<MenuItem Header="动态编译" ></MenuItem>
<MenuItem Header="启动远程服务"></MenuItem> <MenuItem Header="启动远程服务"></MenuItem>
<MenuItem Header="连接远程环境"></MenuItem>
</MenuItem> </MenuItem>
<!--<MenuItem Header="说明"></MenuItem>--> <!--<MenuItem Header="说明"></MenuItem>-->
</Menu> </Menu>