运行环境新增了画布相关的属性

This commit is contained in:
fengjiayi
2025-03-22 18:14:48 +08:00
parent f99aff3c2c
commit cf7760ef84
29 changed files with 1179 additions and 1103 deletions

View File

@@ -3,9 +3,10 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:Serein.Workbench"
xmlns:view="clr-namespace:Serein.Workbench.Views"
StartupUri="MainWindow.xaml"
StartupUri="Views/FlowWorkbenchView.xaml"
Startup="Application_Startup">
<!--StartupUri="Views/FlowWorkbenchView.xaml"-->
<!--StartupUri="MainWindow.xaml"-->
<Application.Resources>
<ResourceDictionary>

View File

@@ -39,7 +39,7 @@ namespace Serein.Workbench
{
collection.AddSingleton<IFlowEEForwardingService, FlowEEForwardingService>(); // 流程事件管理
collection.AddSingleton<IWorkbenchEventService, WorkbenchEventService>(); // 流程事件管理
collection.AddSingleton<INodeOperationService, NodeOperationService>(); // 节点操作管理
collection.AddSingleton<NodeControlService>(); // 节点操作管理
// collection.AddSingleton<IKeyEventService, KeyEventService>(); // 按键事件管理
//collection.AddSingleton<FlowNodeControlService>(); // 流程节点控件管理
}
@@ -90,19 +90,20 @@ namespace Serein.Workbench
public App()
{
_ = Task.Run(async () =>
{
await Task.Delay(500);
await this.LoadLocalProjectAsync();
});
return;
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();
App.GetService<IFlowEnvironment>().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath);
});
}
@@ -124,7 +125,6 @@ namespace Serein.Workbench
App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
var dir = Path.GetDirectoryName(filePath);
//App.GetService<IFlowEnvironment>().LoadProject(new FlowEnvInfo { Project = App.FlowProjectData },App.FileDataPath);
}
#endif
}

View File

@@ -356,7 +356,7 @@ namespace Serein.Workbench
projectData.Basic = new Basic
{
Canvas = new FlowCanvas
Canvas = new FlowCanvasInfo
{
Height = FlowChartCanvas.Height,
Width = FlowChartCanvas.Width,
@@ -763,9 +763,11 @@ namespace Serein.Workbench
NodeControls.TryAdd(nodeModel.Guid, nodeControl); // 添加到
if (TryPlaceNodeInRegion(nodeControl, position, out var regionControl)) // 判断添加到区域容器
{
var canvasGuid = nodeControl.ViewModel.NodeModel.CanvasGuid;
// 通知运行环境调用加载节点子项的方法
_ = EnvDecorator.PlaceNodeToContainerAsync(nodeControl.ViewModel.NodeModel.Guid, // 待移动的节点
regionControl.ViewModel.NodeModel.Guid); // 目标的容器节点
_ = EnvDecorator.PlaceNodeToContainerAsync(canvasGuid,
nodeControl.ViewModel.NodeModel.Guid, // 待移动的节点
regionControl.ViewModel.NodeModel.Guid); // 目标的容器节点
}
else
{
@@ -1148,7 +1150,7 @@ namespace Serein.Workbench
/// </param>
private void ConfigureContextMenu(NodeControlBase nodeControl)
{
var canvasGuid = nodeControl.ViewModel.NodeModel.CanvasGuid;
var contextMenu = new ContextMenu();
var nodeGuid = nodeControl.ViewModel?.NodeModel?.Guid;
#region
@@ -1187,10 +1189,10 @@ namespace Serein.Workbench
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => EnvDecorator.SetStartNodeAsync(nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => EnvDecorator.SetStartNodeAsync(canvasGuid, nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("删除", async (s, e) =>
{
var result = await EnvDecorator.RemoveNodeAsync(nodeGuid);
var result = await EnvDecorator.RemoveNodeAsync(canvasGuid, nodeGuid);
}));
#region -
@@ -1407,7 +1409,8 @@ namespace Serein.Workbench
{
Task.Run(async () =>
{
await EnvDecorator.CreateNodeAsync(nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象
await EnvDecorator.CreateNodeAsync("MainCanvas", nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象
});
}
}
@@ -1429,7 +1432,7 @@ namespace Serein.Workbench
{
Task.Run(async () =>
{
await EnvDecorator.CreateNodeAsync(nodeControlType, position); // 创建基础节点对象
await EnvDecorator.CreateNodeAsync("MainCanvas", nodeControlType, position); // 创建基础节点对象
});
}
}
@@ -1580,7 +1583,7 @@ namespace Serein.Workbench
var newLeft = oldLeft + deltaX;
var newTop = oldTop + deltaY;
this.EnvDecorator.MoveNode(nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
this.EnvDecorator.MoveNode("MainCanvas", nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
// 计算控件实际移动的距离
var actualDeltaX = newLeft - oldLeft;
@@ -1593,7 +1596,7 @@ namespace Serein.Workbench
{
var otherNewLeft = Canvas.GetLeft(nodeControl) + actualDeltaX;
var otherNewTop = Canvas.GetTop(nodeControl) + actualDeltaY;
this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点
this.EnvDecorator.MoveNode("MainCanvas", nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点
}
}
@@ -1613,7 +1616,7 @@ namespace Serein.Workbench
double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量
double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距
double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距
this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
this.EnvDecorator.MoveNode("MainCanvas", nodeControl.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
nodeControl.UpdateLocationConnections();
}
startControlDragPoint = currentPosition; // 更新起始点位置
@@ -1975,7 +1978,9 @@ namespace Serein.Workbench
#region
if (myData.Type == JunctionOfConnectionType.Invoke)
{
await EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
await EnvDecorator.ConnectInvokeNodeAsync(
"MainCanvas",
myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType,
myData.ConnectionInvokeType);
@@ -1995,7 +2000,9 @@ namespace Serein.Workbench
argIndex = argJunction2.ArgIndex;
}
await EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
await EnvDecorator.ConnectArgSourceNodeAsync(
"MainCanvas",
myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType,
myData.ConnectionArgSourceType,
@@ -2060,7 +2067,7 @@ namespace Serein.Workbench
var guid = node?.ViewModel?.NodeModel?.Guid;
if (!string.IsNullOrEmpty(guid))
{
EnvDecorator.RemoveNodeAsync(guid);
EnvDecorator.RemoveNodeAsync("MainCanvas", guid);
}
}
}

View File

@@ -0,0 +1,50 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Newtonsoft.Json.Linq;
using Serein.Workbench.ViewModels;
using Serein.Workbench.Views;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
namespace Serein.Workbench.Models
{
public partial class FlowCanvasModel : ObservableObject
{
public string Name
{
get
{
var vm = (FlowCanvasViewModel)content.DataContext;
return vm.Name;
}
set
{
var vm = (FlowCanvasViewModel)content.DataContext;
vm.Name = value;
OnPropertyChanged(nameof(Name));
}
}
[ObservableProperty]
private bool _isSelected;
[ObservableProperty]
private bool _isEditing;
[ObservableProperty]
private FlowCanvasView content;
public FlowCanvasModel()
{
}
}
}

View File

@@ -237,13 +237,14 @@ namespace Serein.Workbench.Node.View
{
Canvas.Children.Remove(BezierLine);
var env = Start.MyNode.Env;
var canvasGuid = Start.MyNode.CanvasGuid;
if (Start.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Invoke)
{
env.RemoveConnectInvokeAsync(Start.MyNode.Guid, End.MyNode.Guid, InvokeType);
env.RemoveConnectInvokeAsync(canvasGuid, Start.MyNode.Guid, End.MyNode.Guid, InvokeType);
}
else if (Start.JunctionType.ToConnectyionType() == JunctionOfConnectionType.Arg)
{
env.RemoveConnectArgSourceAsync(Start.MyNode.Guid, End.MyNode.Guid, ArgIndex) ;
env.RemoveConnectArgSourceAsync(canvasGuid,Start.MyNode.Guid, End.MyNode.Guid, ArgIndex) ;
}
}

View File

@@ -109,6 +109,14 @@ namespace Serein.Workbench.Services
/// 运行环境输出事件
/// </summary>
public event EnvOutHandler? OnEnvOut;
/// <summary>
/// 添加画布事件
/// </summary>
public event CanvasCreateHandler OnCanvasCreate;
/// <summary>
/// 移除了画布事件
/// </summary>
public event CanvasRemoveHandler OnCanvasRemove;
#endregion
@@ -119,6 +127,8 @@ namespace Serein.Workbench.Services
flowEnvironmentEvent.OnDllLoad += FlowEnvironment_DllLoadEvent;
flowEnvironmentEvent.OnProjectSaving += EnvDecorator_OnProjectSaving;
flowEnvironmentEvent.OnProjectLoaded += FlowEnvironment_OnProjectLoaded;
flowEnvironmentEvent.OnCanvasCreate += FlowEnvironmentEvent_OnCanvasCreate;
flowEnvironmentEvent.OnCanvasRemove += FlowEnvironmentEvent_OnCanvasRemove;
flowEnvironmentEvent.OnStartNodeChange += FlowEnvironment_StartNodeChangeEvent;
flowEnvironmentEvent.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt;
flowEnvironmentEvent.OnNodeCreate += FlowEnvironment_NodeCreateEvent;
@@ -139,6 +149,7 @@ namespace Serein.Workbench.Services
flowEnvironmentEvent.OnEnvOut += FlowEnvironment_OnEnvOutEvent;
}
private void ResetFlowEnvironmentEvent()
{
flowEnvironmentEvent.OnDllLoad -= FlowEnvironment_DllLoadEvent;
@@ -224,6 +235,28 @@ namespace Serein.Workbench.Services
OnNodeConnectChange?.Invoke(eventArgs);
}
/// <summary>
/// 添加了画布
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
private void FlowEnvironmentEvent_OnCanvasCreate(CanvasCreateEventArgs eventArgs)
{
OnCanvasCreate?.Invoke(eventArgs);
}
/// <summary>
/// 移除了画布
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
private void FlowEnvironmentEvent_OnCanvasRemove(CanvasRemoveEventArgs eventArgs)
{
OnCanvasRemove?.Invoke(eventArgs);
}
/// <summary>
/// 节点移除事件
/// </summary>

View File

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

View File

@@ -1,406 +0,0 @@
using Newtonsoft.Json;
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow;
using Serein.NodeFlow.Env;
using Serein.Workbench.Api;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Node;
using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Controls;
namespace Serein.Workbench.Api
{
/// <summary>
/// 提供节点操作的接口
/// </summary>
internal interface INodeOperationService
{
/// <summary>
/// 连接数据
/// </summary>
// ConnectingManage ConnectingManage { get; }
/// <summary>
/// 主画布
/// </summary>
Canvas MainCanvas { get; set; }
/// <summary>
/// 节点创建事件
/// </summary>
event NodeViewCreateHandle OnNodeViewCreate;
/// <summary>
/// 创建节点控件
/// </summary>
/// <param name="nodeType">控件类型</param>
/// <param name="position">创建坐标</param>
/// <param name="methodDetailsInfo">节点方法信息</param>
public void CreateNodeView(MethodDetailsInfo methodDetailsInfo, PositionOfUI position);
/// <summary>
/// 尝试从连接控制点创建连接
/// </summary>
/// <param name="startJunction"></param>
//void TryCreateConnectionOnJunction(NodeJunctionView startJunction);
}
#region
/// <summary>
/// 创建节点控件事件
/// </summary>
/// <param name="eventArgs"></param>
internal delegate bool NodeViewCreateHandle(NodeViewCreateEventArgs eventArgs);
/// <summary>
/// 创建节点控件事件参数
/// </summary>
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
{
/// <summary>
/// 节点操作相关服务
/// </summary>
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<IFlowEnvironment>();
var filePath = @"C:\Users\Az\source\repos\CLBanyunqiState\CLBanyunqiState\bin\debug\net8.0\project.dnf";
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
var projectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
var projectDfilePath = System.IO.Path.GetDirectoryName(filePath)!;
flowEnvironment.LoadProject(new FlowEnvInfo { Project = projectData }, projectDfilePath);
}, CancellationToken.None);
}
#region
//public ConnectingManage ConnectingManage { get; private set; } = new ConnectingManage();
public Canvas MainCanvas { get; set; }
#endregion
#region
/// <summary>
/// 存储所有与节点有关的控件
/// </summary>
private Dictionary<string, NodeControlBase> NodeControls { get; } = [];
/// <summary>
/// 存储所有连接
/// </summary>
//private List<NodeConnectionLineControl> Connections { get; } = [];
/// <summary>
/// 流程运行环境
/// </summary>
private readonly IFlowEnvironment flowEnvironment;
/// <summary>
/// 流程运行环境事件转发
/// </summary>
private readonly IFlowEEForwardingService feefService;
#endregion
#region
/// <summary>
/// 创建了节点控件
/// </summary>
public event NodeViewCreateHandle OnNodeViewCreate;
#endregion
#region
/// <summary>
/// 从工作台事件转发器监听节点创建事件
/// </summary>
/// <param name="eventArgs"></param>
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); // 缓存起来,通知其它地方拿取这个控件
}
}
/// <summary>
/// 运行环境连接了节点事件
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
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
/// <summary>
/// 创建节点控件
/// </summary>
/// <param name="viewType">节点控件视图控件类型</param>
/// <param name="viewModelType">节点控件ViewModel类型</param>
/// <param name="nodeModel">节点Model实例</param>
/// <param name="nodeView">返回的节点对象</param>
/// <returns>是否创建成功</returns>
/// <exception cref="Exception">无法创建节点控件</exception>
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
/// <summary>
/// 创建节点控件
/// </summary>
/// <param name="nodeType">控件类型</param>
/// <param name="position">创建坐标</param>
/// <param name="methodDetailsInfo">节点方法信息基础节点传null</param>
public void CreateNodeView(MethodDetailsInfo methodDetailsInfo, PositionOfUI position)
{
Task.Run(async () =>
{
if (EnumHelper.TryConvertEnum<NodeControlType>(methodDetailsInfo.NodeType, out var nodeType))
{
await flowEnvironment.CreateNodeAsync(nodeType, position, methodDetailsInfo);
}
});
}
#endregion
}
}

View File

@@ -0,0 +1,28 @@
using System;
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.Tool.Converters
{
public class BoolToVisibilityConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool b)
{
return b ? Visibility.Visible : Visibility.Collapsed;
}
return Visibility.Collapsed;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return value is Visibility v && v == Visibility.Visible;
}
}
}

View File

@@ -9,11 +9,35 @@ namespace Serein.Workbench.ViewModels
{
public partial class FlowCanvasViewModel : ObservableObject
{
/// <summary>
/// 正在创建节点方法调用关系
/// </summary>
[ObservableProperty]
private bool _isConnectionInvokeNode;
/// <summary>
/// 正在创建节点参数连接关系
/// </summary>
[ObservableProperty]
private bool _isConnectionArgSourceNode;
/// <summary>
/// 画布显示名称
/// </summary>
[ObservableProperty]
private string _name;
/// <summary>
/// 画布ID
/// </summary>
[ObservableProperty]
private string _canvasGuid;
public FlowCanvasViewModel()
{
}
}
}

View File

@@ -1,9 +1,15 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CommunityToolkit.Mvvm.Input;
using Serein.Workbench.Models;
using Serein.Workbench.Views;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Input;
namespace Serein.Workbench.ViewModels
{
@@ -12,5 +18,80 @@ namespace Serein.Workbench.ViewModels
/// </summary>
public partial class FlowEditViewModel : ObservableObject
{
public ObservableCollection<FlowCanvasModel> Tabs { get; set; }
public ICommand AddTabCommand { get; set; }
public ICommand RemoveTabCommand { get; set; }
public ICommand RenameTabCommand { get; set; }
[ObservableProperty]
private FlowCanvasModel _selectedTab;
private int _addCount = 0;
public FlowEditViewModel()
{
Tabs = new ObservableCollection<FlowCanvasModel>();
AddTabCommand = new RelayCommand(AddTab);
RemoveTabCommand = new RelayCommand(RemoveTab, CanRemoveTab);
// 初始化时添加一个默认的Tab
AddTab(); // 添加一个默认选项卡
}
private void AddTab()
{
var flowCanvasView = new FlowCanvasView(); // 创建FlowCanvasView实例
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);
SelectedTab = Tabs.Count > 0 ? Tabs[Tabs.Count - 1] : null;
}
}
private bool CanRemoveTab()
{
return SelectedTab != null;
}
/// <summary>
/// 进入编辑模式
/// </summary>
/// <param name="tab"></param>
public void StartEditingTab(FlowCanvasModel tab)
{
if (tab != null)
{
tab.IsEditing = true;
OnPropertyChanged(nameof(Tabs)); // 刷新Tabs集合以便更新UI
}
}
/// <summary>
/// 结束编辑,重命名
/// </summary>
/// <param name="tab"></param>
/// <param name="newName"></param>
public void EndEditingTab(FlowCanvasModel tab, string newName)
{
if (tab != null)
{
tab.IsEditing = false;
tab.Name = newName; // 设置新名称
OnPropertyChanged(nameof(Tabs)); // 刷新Tabs集合
}
}
}
}

View File

@@ -15,17 +15,14 @@
<tool:HorizontalCenterThumbPositionConverter x:Key="HorizontalCenterThumbPositionConverter" />
</UserControl.Resources>
<StackPanel x:Name="FlowChartStackPanel"
<DockPanel x:Name="FlowChartStackPanel"
ClipToBounds="True">
<!-- 虚拟化 VirtualizingStackPanel.IsVirtualizing="True" -->
<Canvas
<Canvas ClipToBounds="True"
x:Name="FlowChartCanvas"
Background="#E1FBEA"
AllowDrop="True"
Width="1920"
Height="1080"
Width="1000"
Height="600"
MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown"
MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp"
MouseDown="FlowChartCanvas_MouseDown"
@@ -116,6 +113,6 @@ Canvas.Top="{Binding ActualHeight, ElementName=FlowChartCanvas, Mode=OneWay, Con
</Canvas>
</StackPanel>
</DockPanel>
</UserControl>

View File

@@ -71,19 +71,6 @@ namespace Serein.Workbench.Views
private Point startSelectControolPoint;
/// <summary>
/// 记录开始连接的文本块
/// </summary>
//private NodeControlBase? startConnectNodeControl;
/// <summary>
/// 当前正在绘制的连接线
/// </summary>
//private Line? currentLine;
/// <summary>
/// 当前正在绘制的真假分支属性
/// </summary>
//private ConnectionInvokeType currentConnectionType;
/// <summary>
/// 组合变换容器
@@ -103,9 +90,8 @@ namespace Serein.Workbench.Views
public FlowCanvasView()
{
ViewModel = App.GetService<Locator>().FlowCanvasViewModel;
this.DataContext = ViewModel;
EnvDecorator = App.GetService<IFlowEnvironment>();
InitializeComponent();
#region
@@ -185,7 +171,6 @@ namespace Serein.Workbench.Views
}
}
/// <summary>
/// 放置操作,根据拖放数据创建相应的控件,并处理相关操作
/// </summary>
@@ -201,9 +186,10 @@ namespace Serein.Workbench.Views
{
if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeData nodeData)
{
var canvasGuid = this.ViewModel.CanvasGuid;
Task.Run(async () =>
{
await EnvDecorator.CreateNodeAsync(nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象
await EnvDecorator.CreateNodeAsync(canvasGuid, nodeData.NodeControlType, position, nodeData.MethodDetailsInfo); // 创建DLL文件的节点对象
});
}
}
@@ -223,9 +209,10 @@ namespace Serein.Workbench.Views
};
if (nodeControlType != NodeControlType.None)
{
var canvasGuid = this.ViewModel.CanvasGuid;
Task.Run(async () =>
{
await EnvDecorator.CreateNodeAsync(nodeControlType, position); // 创建基础节点对象
await EnvDecorator.CreateNodeAsync(canvasGuid, nodeControlType, position); // 创建基础节点对象
});
}
}
@@ -257,11 +244,6 @@ namespace Serein.Workbench.Views
e.Handled = true;
}
/// <summary>
/// 在画布中尝试选取控件
/// </summary>
@@ -340,7 +322,12 @@ namespace Serein.Workbench.Views
#region
if (myData.Type == JunctionOfConnectionType.Invoke)
{
await EnvDecorator.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
var canvasGuid = this.ViewModel.CanvasGuid;
await EnvDecorator.ConnectInvokeNodeAsync(
canvasGuid,
myData.StartJunction.MyNode.Guid,
myData.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType,
myData.ConnectionInvokeType);
@@ -359,8 +346,12 @@ namespace Serein.Workbench.Views
{
argIndex = argJunction2.ArgIndex;
}
var canvasGuid = this.ViewModel.CanvasGuid;
await EnvDecorator.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
await EnvDecorator.ConnectArgSourceNodeAsync(
canvasGuid,
myData.StartJunction.MyNode.Guid,
myData.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType,
myData.ConnectionArgSourceType,
@@ -376,9 +367,6 @@ namespace Serein.Workbench.Views
}
#region
private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
@@ -568,7 +556,6 @@ namespace Serein.Workbench.Views
#endregion
/// 完成选取操作
/// </summary>
private void CompleteSelection()
@@ -607,7 +594,9 @@ namespace Serein.Workbench.Views
SelectedNode();
}
/// <summary>
/// 选择控件
/// </summary>
private void SelectedNode()
{
@@ -657,7 +646,10 @@ namespace Serein.Workbench.Views
}
/// <summary>
/// 选择范围配置
/// </summary>
/// <returns></returns>
private ContextMenu ConfiguerSelectionRectangle()
{
var contextMenu = new ContextMenu();
@@ -670,7 +662,8 @@ namespace Serein.Workbench.Views
var guid = node?.ViewModel?.NodeModel?.Guid;
if (!string.IsNullOrEmpty(guid))
{
EnvDecorator.RemoveNodeAsync(guid);
var canvasGuid = this.ViewModel.CanvasGuid;
EnvDecorator.RemoveNodeAsync(canvasGuid, guid);
}
}
}

View File

@@ -4,10 +4,62 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Serein.Workbench.Views"
xmlns:vm="clr-namespace:Serein.Workbench.ViewModels"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
Background="#FFD3D3D3">
d:DataContext="{d:DesignInstance vm:FlowEditViewModel}"
xmlns:converters="clr-namespace:Serein.Workbench.Tool.Converters"
Background="#E7F0F6">
<UserControl.Resources>
<converters:InvertableBooleanToVisibilityConverter x:Key="InvertableBooleanToVisibilityConverter"/>
</UserControl.Resources>
<Grid>
<local:FlowCanvasView></local:FlowCanvasView>
<!-- TabControl -->
<!--DragOver="TabControl_DragOver"
Drop="TabControl_Drop"
AllowDrop="True"-->
<TabControl SelectedItem="{Binding SelectedTab}" >
<TabControl.ItemTemplate>
<DataTemplate>
<StackPanel>
<!-- 双击选项卡名称来进入编辑模式 -->
<TextBlock Text="{Binding Name}"
FontSize="18"
Visibility="{Binding IsEditing, Converter={StaticResource InvertableBooleanToVisibilityConverter},ConverterParameter=Inverted}"
PreviewMouseLeftButtonDown="TextBlock_PreviewMouseLeftButtonDown"
MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"/>
<!-- 编辑模式下显示TextBox -->
<TextBox Text="{Binding Name, Mode=TwoWay}"
FontSize="18"
Visibility="{Binding IsEditing, Converter={StaticResource InvertableBooleanToVisibilityConverter},ConverterParameter=Normal}"
KeyDown="TextBox_KeyDown"
LostFocus="TextBox_LostFocus"/>
</StackPanel>
</DataTemplate>
</TabControl.ItemTemplate>
<!-- Tabs Collection -->
<TabControl.ItemsSource>
<Binding Path="Tabs" />
</TabControl.ItemsSource>
<!-- Content of the Tab (e.g., FlowCanvasView) -->
<TabControl.ContentTemplate>
<DataTemplate>
<Grid Background="#FFD3D3D3">
<ContentControl Content="{Binding Content}" />
</Grid>
</DataTemplate>
</TabControl.ContentTemplate>
</TabControl>
<!-- Tab control buttons -->
<StackPanel Orientation="Horizontal" VerticalAlignment="Top" HorizontalAlignment="Right">
<Button Content="添加" Command="{Binding AddTabCommand}" Margin="5" Width="80"/>
<Button Content="移除" Command="{Binding RemoveTabCommand}" Margin="5" Width="80"/>
<!--<Button Content="Rename Tab" Command="{Binding RenameTabCommand}" />-->
</StackPanel>
</Grid>
</UserControl>

View File

@@ -1,4 +1,5 @@
using Serein.Workbench.ViewModels;
using Serein.Workbench.Models;
using Serein.Workbench.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -8,11 +9,19 @@ using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Forms;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;
using static System.Windows.Forms.VisualStyles.VisualStyleElement;
using DragDropEffects = System.Windows.DragDropEffects;
using DragEventArgs = System.Windows.DragEventArgs;
using TabControl = System.Windows.Controls.TabControl;
using TextBox = System.Windows.Controls.TextBox;
using UserControl = System.Windows.Controls.UserControl;
namespace Serein.Workbench.Views
{
@@ -24,8 +33,99 @@ namespace Serein.Workbench.Views
public FlowEditView()
{
this.DataContext = App.GetService<Locator>().FlowEditViewModel;
InitializeComponent();
}
private void TextBlock_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
var textBlock = sender as TextBlock;
var tab = textBlock?.DataContext as FlowCanvasModel;
if (tab != null)
{
DragDrop.DoDragDrop(textBlock, tab, DragDropEffects.Move);
}
}
private void TabControl_DragOver(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(typeof(FlowCanvasModel)))
{
e.Effects = DragDropEffects.Move;
}
else
{
e.Effects = DragDropEffects.None;
}
}
private void TabControl_Drop(object sender, DragEventArgs e)
{
var sourceTab = e.Data.GetData(typeof(FlowCanvasModel)) as FlowCanvasModel;
var targetTab = (sender as TabControl)?.SelectedItem as FlowCanvasModel;
var viewModel = (FlowEditViewModel)this.DataContext;
if (sourceTab != null && targetTab != null && sourceTab != targetTab)
{
var sourceIndex = viewModel.Tabs.IndexOf(sourceTab);
var targetIndex = viewModel.Tabs.IndexOf(targetTab);
// 删除源项并插入到目标位置
viewModel.Tabs.Remove(sourceTab);
viewModel.Tabs.Insert(targetIndex, sourceTab);
// 更新视图模型中的选中的Tab
viewModel.SelectedTab = sourceTab;
}
}
private void TextBox_KeyDown(object sender, System.Windows.Input.KeyEventArgs e)
{
if (e.Key == Key.Enter || e.Key == Key.Escape)
{
var textBox = sender as TextBox;
var newName = textBox?.Text;
if (string.IsNullOrEmpty(newName))
{
return;
}
var tab = textBox?.DataContext as FlowCanvasModel;
if (tab != null)
{
var viewModel = (FlowEditViewModel)this.DataContext;
viewModel.EndEditingTab(tab, newName); // 确认新名称
}
}
}
private void TextBox_LostFocus(object sender, RoutedEventArgs e)
{
var textBox = sender as TextBox;
var newName = textBox?.Text;
if (string.IsNullOrEmpty(newName))
{
return;
}
var tab = textBox?.DataContext as FlowCanvasModel;
if (tab != null && tab.IsEditing)
{
var viewModel = (FlowEditViewModel)this.DataContext;
viewModel.EndEditingTab(tab, newName); // 确认新名称
}
}
private void TextBlock_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (e.ClickCount == 2)
{
var textBlock = sender as TextBlock;
var tab = textBlock?.DataContext as FlowCanvasModel;
if (tab != null)
{
var viewModel = (FlowEditViewModel)this.DataContext;
viewModel.StartEditingTab(tab);
}
}
}
}
}

View File

@@ -25,7 +25,6 @@ namespace Serein.Workbench.Views
this.DataContext = App.GetService<Locator>().FlowWorkbenchViewModel;
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.MaxHeight = SystemParameters.PrimaryScreenHeight;