using Microsoft.Win32;
using Newtonsoft.Json.Linq;
using Serein.Library.Api;
using Serein.Library.Entity;
using Serein.Library.Enums;
using Serein.Library.Utils;
using Serein.NodeFlow;
using Serein.NodeFlow.Base;
using Serein.NodeFlow.Model;
using Serein.WorkBench.Node.View;
using Serein.WorkBench.Node.ViewModel;
using Serein.WorkBench.Themes;
using Serein.WorkBench.tool;
using System.IO;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Input;
using System.Windows.Interop;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using DataObject = System.Windows.DataObject;
namespace Serein.WorkBench
{
///
/// 拖拽创建节点类型
///
public static class MouseNodeType
{
public static string CreateDllNodeInCanvas { get; } = nameof(CreateDllNodeInCanvas);
public static string CreateBaseNodeInCanvas { get; } = nameof(CreateBaseNodeInCanvas);
//public static string RegionType { get; } = nameof(RegionType);
//public static string BaseNodeType { get; } = nameof(BaseNodeType);
//public static string DllNodeType { get; } = nameof(DllNodeType);
}
///
/// Interaction logic for MainWindow.xaml,第一次用git,不太懂
///
public partial class MainWindow : Window
{
///
/// 一种轻量的IOC容器
///
private SereinIoc ServiceContainer { get; } = new SereinIoc();
///
/// 全局捕获Console输出事件,打印在这个窗体里面
///
private readonly LogWindow logWindow;
///
/// 存储加载的程序集
///
// private readonly List loadedAssemblies = [];
///
/// 存储加载的程序集路径
///
// private readonly List loadedAssemblyPaths = [];
///
/// 存储所有方法信息
///
// private static ConcurrentDictionary DllMethodDetails { get; } = [];
///
/// 存储所有与节点有关的控件
///
private readonly Dictionary NodeControls = [];
///
/// 存储所有的连接
///
private readonly List Connections = [];
///
/// 存放触发器节点(运行时全部调用)
///
// private readonly List flipflopNodes = [];
///
/// 节点的命名空间
///
public const string NodeSpaceName = $"{nameof(Serein)}.{nameof(Serein.NodeFlow)}.{nameof(Serein.NodeFlow.Model)}";
///
/// 流程运行环境
///
private IFlowEnvironment FlowEnvironment;
///
/// 流程启动器
///
// private FlowStarter nodeFlowStarter;
#region 与画布相关的字段
///
/// 当前选取的控件
///
private readonly List selectControls = [];
///
/// 记录拖动开始时的鼠标位置
///
private Point startPoint;
///
/// 流程图起点的控件
///
// private NodeControlBase? flowStartBlock;
///
/// 记录开始连接的文本块
///
private NodeControlBase? startConnectNodeControl;
///
/// 当前正在绘制的连接线
///
private Line? currentLine;
///
/// 当前正在绘制的真假分支属性
///
private ConnectionType currentConnectionType;
///
/// 标记是否正在进行连接操作
///
private bool IsConnecting;
///
/// 标记是否正在尝试选取控件
///
private bool IsSelectControl;
///
/// 标记是否正在拖动控件
///
private bool IsControlDragging;
///
/// 标记是否正在拖动画布
///
private bool IsCanvasDragging;
///
/// 组合变换容器
///
private TransformGroup canvasTransformGroup;
///
/// 缩放画布
///
private ScaleTransform scaleTransform;
///
/// 平移画布
///
private TranslateTransform translateTransform;
#endregion
private Point canvasDropPosition;
// private MainWindowViewModel mainWindowViewModel { get; }
public MainWindow()
{
// mainWindowViewModel = new MainWindowViewModel(this);
InitializeComponent();
logWindow = new LogWindow();
logWindow.Show();
// 重定向 Console 输出
var logTextWriter = new LogTextWriter(WriteLog);
Console.SetOut(logTextWriter);
InitFlowEvent();
InitUI();
}
private void InitFlowEvent()
{
FlowEnvironment = new FlowEnvironment();
FlowEnvironment.OnDllLoad += FlowEnvironment_DllLoadEvent;
FlowEnvironment.OnLoadNode += FlowEnvironment_NodeLoadEvent;
FlowEnvironment.OnStartNodeChange += FlowEnvironment_StartNodeChangeEvent;
FlowEnvironment.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt;
FlowEnvironment.OnNodeCreate += FlowEnvironment_NodeCreateEvent;
FlowEnvironment.OnNodeRemote += FlowEnvironment_NodeRemoteEvent;
FlowEnvironment.OnFlowRunComplete += FlowEnvironment_OnFlowRunComplete;
}
private void InitUI()
{
canvasTransformGroup = new TransformGroup();
scaleTransform = new ScaleTransform();
translateTransform = new TranslateTransform();
canvasTransformGroup.Children.Add(scaleTransform);
canvasTransformGroup.Children.Add(translateTransform);
FlowChartCanvas.RenderTransform = canvasTransformGroup;
FlowChartCanvas.RenderTransformOrigin = new Point(0.5, 0.5);
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
logWindow.Close();
System.Windows.Application.Current.Shutdown();
}
public void WriteLog(string message)
{
logWindow.AppendText(message);
}
#region 运行环境事件
///
/// 运行完成
///
///
///
private void FlowEnvironment_OnFlowRunComplete(FlowEventArgs eventArgs)
{
WriteLog("-------运行完成---------\r\n");
}
///
/// 加载了DLL文件,dll内容
///
private void FlowEnvironment_DllLoadEvent(LoadDLLEventArgs eventArgs)
{
Assembly assembly = eventArgs.Assembly;
List methodDetailss = eventArgs.MethodDetailss;
var dllControl = new DllControl
{
Header = "DLL name : " + assembly.GetName().Name // 设置控件标题为程序集名称
};
foreach (var methodDetails in methodDetailss)
{
switch (methodDetails.MethodDynamicType)
{
case Library.Enums.NodeType.Action:
dllControl.AddAction(methodDetails.Clone()); // 添加动作类型到控件
break;
case Library.Enums.NodeType.Flipflop:
dllControl.AddFlipflop(methodDetails.Clone()); // 添加触发器方法到控件
break;
}
}
DllStackPanel.Children.Add(dllControl); // 将控件添加到界面上显示
}
///
/// 运行环境成功加载了节点,需要在画布上创建节点控件
///
///
///
private void FlowEnvironment_NodeLoadEvent(LoadNodeEventArgs eventArgs)
{
if (!eventArgs.IsSucceed)
{
MessageBox.Show(eventArgs.ErrorTips);
return;
}
NodeInfo nodeInfo = eventArgs.NodeInfo;
MethodDetails methodDetailss = eventArgs.MethodDetailss;
// 创建对应的实例(包含NodeModel,NodeControl,NodeControlViewModel)
NodeControlBase? nodeControl = CreateNodeControlOfNodeInfo(nodeInfo, methodDetailss);
if (nodeControl == null)
{
WriteLog($"无法为节点类型创建节点控件: {nodeInfo.MethodName}\r\n");
return;
// ConfigureNodeControl(nodeInfo, nodeControl, nodeControls, regionControls);
}
// 判断是否属于区域控件,如果是,则加载区域子项
if (nodeControl is ActionRegionControl || nodeControl is ConditionRegionControl)
{
AddNodeControlInRegeionControl(nodeControl, nodeInfo.ChildNodes);
}
NodeControls.TryAdd(nodeInfo.Guid, nodeControl); // 存放对应的控件
PlaceNodeOnCanvas(nodeControl, nodeInfo.Position.X, nodeInfo.Position.Y); // 配置节点,并放置在画布上
// 添加到画布
//FlowChartCanvas.Dispatcher.Invoke(() =>
//{
// // 添加控件到画布
// FlowChartCanvas.Children.Add(nodeControl);
// Canvas.SetLeft(nodeControl, nodeInfo.Position.x);
// Canvas.SetTop(nodeControl, nodeInfo.Position.y);
// nodeControls.TryAdd(nodeInfo.Guid, nodeControl); // 存放对应的控件
// ConfigureContextMenu(nodeControl); // 配置节点右键菜单
// ConfigureNodeEvents(nodeControl); // 配置节点事件
//});
}
///
/// 节点连接关系变更
///
///
///
///
private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs)
{
string fromNodeGuid = eventArgs.FromNodeGuid;
string toNodeGuid = eventArgs.ToNodeGuid;
if (!NodeControls.TryGetValue(fromNodeGuid, out var fromNode) || !NodeControls.TryGetValue(toNodeGuid, out var toNode))
{
return;
}
ConnectionType connectionType = eventArgs.ConnectionType;
if(eventArgs.ChangeType == NodeConnectChangeEventArgs.ChangeTypeEnum.Create)
{
// 添加连接
var connection = new Connection
{
Start = fromNode,
End = toNode,
Type = connectionType
};
BsControl.Draw(FlowChartCanvas, connection); // 添加贝塞尔曲线显示
ConfigureLineContextMenu(connection); // 设置连接右键事件
Connections.Add(connection);
EndConnection();
}
else if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ChangeTypeEnum.Remote)
{
// 需要移除连接
var removeConnections = Connections.Where(c => c.Start.ViewModel.Node.Guid.Equals(fromNodeGuid)
&& c.End.ViewModel.Node.Guid.Equals(toNodeGuid))
.ToList();
foreach(var connection in removeConnections)
{
connection.RemoveFromCanvas(FlowChartCanvas);
Connections.Remove(connection);
}
}
}
///
/// 节点移除事件
///
///
private void FlowEnvironment_NodeRemoteEvent(NodeRemoteEventArgs eventArgs)
{
var nodeGuid = eventArgs.NodeGuid;
if (!NodeControls.TryGetValue(nodeGuid, out var nodeControl))
{
return;
}
FlowChartCanvas.Children.Remove(nodeControl);
NodeControls.Remove(nodeControl.ViewModel.Node.Guid);
}
///
/// 编辑项目时添加了节点
///
///
///
private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs)
{
if (eventArgs.NodeModel is not NodeModelBase nodeModelBase)
{
return;
}
// 创建对应控件
NodeControlBase? nodeControl = nodeModelBase.ControlType switch
{
NodeControlType.Action => CreateNodeControl(nodeModelBase), //typeof(ActionNodeControl),
NodeControlType.Flipflop => CreateNodeControl(nodeModelBase),
NodeControlType.ExpCondition => CreateNodeControl(nodeModelBase),
NodeControlType.ExpOp => CreateNodeControl(nodeModelBase),
NodeControlType.ConditionRegion => CreateNodeControl(nodeModelBase),
_ => null,
};
if(nodeControl == null)
{
return;
}
NodeControls.TryAdd(nodeModelBase.Guid, nodeControl);
if (!TryPlaceNodeInRegion(nodeControl))
{
PlaceNodeOnCanvas(nodeControl, canvasDropPosition.X, canvasDropPosition.Y);
}
}
///
/// 设置了流程起始控件
///
///
///
private void FlowEnvironment_StartNodeChangeEvent(StartNodeChangeEventArgs eventArgs)
{
string oldNodeGuid = eventArgs.OldNodeGuid;
string newNodeGuid = eventArgs.NewNodeGuid;
if (!NodeControls.TryGetValue(newNodeGuid, out var newStartNodeControl))
{
return;
}
if (newStartNodeControl == null)
{
return;
}
if (!string.IsNullOrEmpty(oldNodeGuid))
{
NodeControls.TryGetValue(oldNodeGuid, out var oldStartNodeControl);
if (oldStartNodeControl != null)
{
oldStartNodeControl.BorderBrush = Brushes.Black;
oldStartNodeControl.BorderThickness = new Thickness(0);
}
}
newStartNodeControl.BorderBrush = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"));
newStartNodeControl.BorderThickness = new Thickness(2);
}
#endregion
#region 加载 DynamicNodeFlow 文件
private void Window_Loaded(object sender, RoutedEventArgs e)
{
var project = App.FData;
if (project == null)
{
return;
}
InitializeCanvas(project.Basic.canvas.width, project.Basic.canvas.lenght);// 设置画布大小
FlowEnvironment.LoadProject(project, App.FileDataPath); // 加载项目
//LoadDll(project); // 加载DLL
//LoadNodeControls(project); // 加载节点
//var startNode = nodeControls.Values.FirstOrDefault(control => control.ViewModel.Node.Guid.Equals(project.StartNode));
//var startNodeGuid = nodeControls.Keys.FirstOrDefault(guid => guid.Equals(project.StartNode));
//if (!string.IsNullOrEmpty(startNodeGuid))
//{
// FlowEnvironment.SetStartNode(startNodeGuid);
//}
}
///
/// 运行环节加载了项目文件,需要创建节点控件
///
///
///
///
///
private NodeControlBase? CreateNodeControlOfNodeInfo(NodeInfo nodeInfo, MethodDetails methodDetailss)
{
// 创建控件实例
NodeControlBase nodeControl = nodeInfo.Type switch
{
$"{NodeSpaceName}.{nameof(SingleActionNode)}" =>
CreateNodeControl(methodDetailss),// 动作节点控件
$"{NodeSpaceName}.{nameof(SingleFlipflopNode)}" =>
CreateNodeControl(methodDetailss), // 触发器节点控件
$"{NodeSpaceName}.{nameof(SingleConditionNode)}" =>
CreateNodeControl(), // 条件表达式控件
$"{NodeSpaceName}.{nameof(SingleExpOpNode)}" =>
CreateNodeControl(), // 操作表达式控件
$"{NodeSpaceName}.{nameof(CompositeConditionNode)}" =>
CreateNodeControl(), // 条件区域控件
_ => throw new NotImplementedException($"非预期的节点类型{nodeInfo.Type}"),
};
return nodeControl;
}
///
/// 加载文件时,添加节点到区域中
///
///
///
private void AddNodeControlInRegeionControl(NodeControlBase regionControl, NodeInfo[] childNodes)
{
foreach (var childNode in childNodes)
{
if (FlowEnvironment.TryGetMethodDetails(childNode.MethodName, out MethodDetails md))
{
var childNodeControl = CreateNodeControlOfNodeInfo(childNode, md);
if (childNodeControl == null)
{
WriteLog($"无法为节点类型创建节点控件: {childNode.MethodName}\r\n");
continue;
}
if (regionControl is ConditionRegionControl conditionRegion)
{
conditionRegion.AddCondition(childNodeControl);
}
}
}
}
#endregion
#region 节点控件的创建
private static TControl CreateNodeControl(NodeModelBase model)
where TControl : NodeControlBase
where TViewModel : NodeControlViewModelBase
{
if (model == null)
{
throw new Exception("无法创建节点控件");
}
var viewModel = Activator.CreateInstance(typeof(TViewModel), [model]);
var controlObj = Activator.CreateInstance(typeof(TControl), [viewModel]);
if (controlObj is TControl control)
{
return control;
}
else
{
throw new Exception("无法创建节点控件");
}
}
private static TControl CreateNodeControl(MethodDetails? methodDetails = null)
where TNode : NodeModelBase
where TControl : NodeControlBase
where TViewModel : NodeControlViewModelBase
{
var nodeObj = Activator.CreateInstance(typeof(TNode));
var nodeBase = nodeObj as NodeModelBase;
if (nodeBase == null)
{
throw new Exception("无法创建节点控件");
}
nodeBase.Guid = Guid.NewGuid().ToString();
if (methodDetails != null)
{
var md = methodDetails.Clone();
nodeBase.DisplayName = md.MethodTips;
nodeBase.MethodDetails = md;
}
var viewModel = Activator.CreateInstance(typeof(TViewModel), [nodeObj]);
var controlObj = Activator.CreateInstance(typeof(TControl), [viewModel] );
if(controlObj is TControl control)
{
return control;
}
else
{
throw new Exception("无法创建节点控件");
}
}
///
/// 创建了节点,添加到画布。配置默认事件
///
///
///
private void PlaceNodeOnCanvas(NodeControlBase nodeControl, double x, double y)
{
FlowChartCanvas.Dispatcher.Invoke(() =>
{
// 添加控件到画布
FlowChartCanvas.Children.Add(nodeControl);
Canvas.SetLeft(nodeControl, x);
Canvas.SetTop(nodeControl, y);
ConfigureContextMenu(nodeControl); // 配置节点右键菜单
ConfigureNodeEvents(nodeControl); // 配置节点事件
});
}
///
/// 配置节点右键菜单
///
///
private void ConfigureContextMenu(NodeControlBase nodeControl)
{
var contextMenu = new ContextMenu();
// var nodeModel = nodeControl.ViewModel.Node;
if (nodeControl.ViewModel.Node?.MethodDetails?.ReturnType is Type returnType && returnType != typeof(void))
{
contextMenu.Items.Add(CreateMenuItem("查看返回类型", (s, e) =>
{
DisplayReturnTypeTreeViewer(returnType);
}));
}
var nodeGuid = nodeControl?.ViewModel?.Node?.Guid;
contextMenu.Items.Add(CreateMenuItem("设为起点", (s, e) => FlowEnvironment.SetStartNode(nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("删除", (s, e) => FlowEnvironment.RemoteNode(nodeGuid)));
contextMenu.Items.Add(CreateMenuItem("添加 真分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsSucceed)));
contextMenu.Items.Add(CreateMenuItem("添加 假分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsFail)));
contextMenu.Items.Add(CreateMenuItem("添加 异常分支", (s, e) => StartConnection(nodeControl, ConnectionType.IsError)));
contextMenu.Items.Add(CreateMenuItem("添加 上游分支", (s, e) => StartConnection(nodeControl, ConnectionType.Upstream)));
nodeControl.ContextMenu = contextMenu;
}
///
/// 创建菜单子项
///
///
///
///
private static MenuItem CreateMenuItem(string header, RoutedEventHandler handler)
{
var menuItem = new MenuItem { Header = header };
menuItem.Click += handler;
return menuItem;
}
///
/// 配置节点事件
///
///
private void ConfigureNodeEvents(NodeControlBase nodeControl)
{
nodeControl.MouseLeftButtonDown += Block_MouseLeftButtonDown;
nodeControl.MouseMove += Block_MouseMove;
nodeControl.MouseLeftButtonUp += Block_MouseLeftButtonUp;
}
#endregion
#region 右键菜单事件
///
/// 开始创建连接 True线 操作,设置起始块和绘制连接线。
///
private void StartConnection(NodeControlBase startNodeControl, ConnectionType connectionType)
{
var tf = Connections.FirstOrDefault(it => it.Start == startNodeControl)?.Type;
IsConnecting = true;
currentConnectionType = connectionType;
startConnectNodeControl = startNodeControl;
// 确保起点和终点位置的正确顺序
currentLine = new Line
{
Stroke = connectionType == ConnectionType.IsSucceed ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10"))
: connectionType == ConnectionType.IsFail ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905"))
: connectionType == ConnectionType.IsError ? new SolidColorBrush((Color)ColorConverter.ConvertFromString("#AB616B"))
: new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
StrokeDashArray = new DoubleCollection([2]),
StrokeThickness = 2,
X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2,
Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2,
X2 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2, // 初始时终点与起点重合
Y2 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2,
};
FlowChartCanvas.Children.Add(currentLine);
this.KeyDown += MainWindow_KeyDown;
}
///
/// 配置连接曲线的右键菜单
///
///
private void ConfigureLineContextMenu(Connection connection)
{
var contextMenu = new ContextMenu();
contextMenu.Items.Add(CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
connection.ArrowPath.ContextMenu = contextMenu;
connection.BezierPath.ContextMenu = contextMenu;
}
///
/// 删除该连线
///
///
private void DeleteConnection(Connection connection)
{
var connectionToRemove = connection;
if (connectionToRemove == null)
{
return;
}
// 获取起始节点与终止节点,消除映射关系
var fromNodeGuid = connectionToRemove.Start.ViewModel.Node.Guid;
var toNodeGuid = connectionToRemove.End.ViewModel.Node.Guid;
FlowEnvironment.RemoteConnect(fromNodeGuid, toNodeGuid, connection.Type);
}
///
/// 查看返回类型(树形结构展开类型的成员)
///
///
private void DisplayReturnTypeTreeViewer(Type type)
{
try
{
var typeViewerWindow = new TypeViewerWindow
{
Type = type,
};
typeViewerWindow.LoadTypeInformation();
typeViewerWindow.Show();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
#endregion
#region 拖拽DLL文件到左侧功能区,加载相关节点清单
///
/// 当拖动文件到窗口时触发,加载DLL文件
///
///
///
private void Window_Drop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
foreach (string file in files)
{
if (file.EndsWith(".dll"))
{
FlowEnvironment.LoadDll(file);
}
}
}
}
///
/// 当拖动文件经过窗口时触发,设置拖放效果为复制
///
///
///
private void Window_DragOver(object sender, DragEventArgs e)
{
e.Effects = DragDropEffects.Copy;
e.Handled = true;
}
#endregion
#region 与流程图相关的拖拽操作
///
/// 基础节点的拖拽放置创建
///
///
///
private void BaseNodeControl_PreviewMouseMove(object sender, MouseEventArgs e)
{
if (sender is UserControl control)
{
// 创建一个 DataObject 用于拖拽操作,并设置拖拽效果
var dragData = new DataObject(MouseNodeType.CreateBaseNodeInCanvas, control.GetType());
DragDrop.DoDragDrop(control, dragData, DragDropEffects.Move);
}
}
///
/// 放置操作,根据拖放数据创建相应的控件,并处理相关操作
///
///
///
private void FlowChartCanvas_Drop(object sender, DragEventArgs e)
{
canvasDropPosition = e.GetPosition(FlowChartCanvas); // 更新画布落点
if (e.Data.GetDataPresent(MouseNodeType.CreateDllNodeInCanvas))
{
if (e.Data.GetData(MouseNodeType.CreateDllNodeInCanvas) is MoveNodeData nodeData)
{
// 创建DLL文件的节点对象
FlowEnvironment.CreateNode(nodeData.NodeControlType, nodeData.MethodDetails);
}
}
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,
_ => NodeControlType.None,
};
if(nodeControlType != NodeControlType.None)
{
// 创建基础节点对象
FlowEnvironment.CreateNode(nodeControlType);
}
}
}
e.Handled = true;
}
///
/// 尝试将节点放置在区域中
///
///
///
///
///
private bool TryPlaceNodeInRegion(NodeControlBase nodeControl)
{
HitTestResult hitTestResult = VisualTreeHelper.HitTest(FlowChartCanvas, canvasDropPosition);
if (hitTestResult != null && hitTestResult.VisualHit is UIElement hitElement)
{
// 准备放置条件表达式控件
if (nodeControl.ViewModel.Node.ControlType == NodeControlType.ExpCondition)
{
ConditionRegionControl conditionRegion = GetParentOfType(hitElement);
if (conditionRegion != null)
{
// 如果存在条件区域容器
conditionRegion.AddCondition(nodeControl);
return true;
}
}
}
return false;
}
///
/// 穿透元素获取区域容器
///
///
///
///
private static T GetParentOfType(DependencyObject element) where T : DependencyObject
{
while (element != null)
{
if (element is T)
{
return element as T;
}
element = VisualTreeHelper.GetParent(element);
}
return null;
}
///
/// 拖动效果,根据拖放数据是否为指定类型设置拖放效果
///
///
///
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 Block_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (IsConnecting)
return;
IsControlDragging = true;
startPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置
((UIElement)sender).CaptureMouse(); // 捕获鼠标
}
///
/// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。
///
private void Block_MouseMove(object sender, MouseEventArgs e)
{
if (IsControlDragging) // 如果正在拖动控件
{
Point currentPosition = e.GetPosition(FlowChartCanvas); // 获取当前鼠标位置
// 获取引发事件的控件
if (sender is not UserControl block)
{
return;
}
double deltaX = currentPosition.X - startPoint.X; // 计算X轴方向的偏移量
double deltaY = currentPosition.Y - startPoint.Y; // 计算Y轴方向的偏移量
double newLeft = Canvas.GetLeft(block) + deltaX; // 新的左边距
double newTop = Canvas.GetTop(block) + deltaY; // 新的上边距
// 限制控件不超出FlowChartCanvas的边界
if (newLeft >= 0 && newLeft + block.ActualWidth <= FlowChartCanvas.ActualWidth)
{
Canvas.SetLeft(block, newLeft);
}
if (newTop >= 0 && newTop + block.ActualHeight <= FlowChartCanvas.ActualHeight)
{
Canvas.SetTop(block, newTop);
}
UpdateConnections(block);
startPoint = currentPosition; // 更新起始点位置
}
}
#region UI连接控件操作
///
/// 控件的鼠标左键松开事件,结束拖动操作,创建连线
///
private void Block_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (IsControlDragging)
{
IsControlDragging = false;
((UIElement)sender).ReleaseMouseCapture(); // 释放鼠标捕获
}
else if (IsConnecting)
{
var formNodeGuid = startConnectNodeControl?.ViewModel.Node.Guid;
var toNodeGuid = (sender as NodeControlBase)?.ViewModel.Node.Guid;
if (string.IsNullOrEmpty(formNodeGuid) || string.IsNullOrEmpty(toNodeGuid))
{
return;
}
FlowEnvironment.ConnectNode(formNodeGuid, toNodeGuid, currentConnectionType);
}
/*else if (IsConnecting)
{
bool isRegion = false;
NodeControlBase? targetBlock;
if (sender is ActionNodeControl)
{
targetBlock = sender as ActionNodeControl; // 动作
}
else if (sender is ActionRegionControl)
{
targetBlock = sender as ActionRegionControl; // 组合动作
isRegion = true;
}
else if (sender is ConditionNodeControl)
{
targetBlock = sender as ConditionNodeControl; // 条件
}
else if (sender is ConditionRegionControl)
{
targetBlock = sender as ConditionRegionControl; // 组合条件
isRegion = true;
}
else if (sender is FlipflopNodeControl)
{
targetBlock = sender as FlipflopNodeControl; // 触发器
}
else if (sender is ExpOpNodeControl)
{
targetBlock = sender as ExpOpNodeControl; // 触发器
}
else
{
targetBlock = null;
}
if (targetBlock == null)
{
return;
}
if (startConnectBlock != null && targetBlock != null && startConnectBlock != targetBlock)
{
var connection = new Connection { Start = startConnectBlock, End = targetBlock, Type = currentConnectionType };
if (currentConnectionType == ConnectionType.IsSucceed)
{
startConnectBlock.ViewModel.Node.SucceedBranch.Add(targetBlock.ViewModel.Node);
}
else if (currentConnectionType == ConnectionType.IsFail)
{
startConnectBlock.ViewModel.Node.FailBranch.Add(targetBlock.ViewModel.Node);
}
else if (currentConnectionType == ConnectionType.IsError)
{
startConnectBlock.ViewModel.Node.ErrorBranch.Add(targetBlock.ViewModel.Node);
}
else if (currentConnectionType == ConnectionType.Upstream)
{
startConnectBlock.ViewModel.Node.UpstreamBranch.Add(targetBlock.ViewModel.Node);
}
// 保存连接关系
BsControl.Draw(FlowChartCanvas, connection);
ConfigureLineContextMenu(connection);
targetBlock.ViewModel.Node.PreviousNodes.Add(startConnectBlock.ViewModel.Node); // 将当前发起连接的节点,添加到被连接的节点的上一节点队列。(用于回溯)
connections.Add(connection);
}
EndConnection();
}*/
}
///
/// 主窗口的KeyDown事件处理,用于在连接操作中按下Esc键取消连接。
///
private void MainWindow_KeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Escape && IsConnecting)
{
this.KeyDown -= MainWindow_KeyDown;
EndConnection();
}
}
///
/// 结束连接操作,清理状态并移除虚线。
///
private void EndConnection()
{
IsConnecting = false;
startConnectNodeControl = null;
// 移除虚线
if (currentLine != null)
{
FlowChartCanvas.Children.Remove(currentLine);
currentLine = null;
}
}
///
/// 更新与指定控件相关的所有连接的位置。
///
private void UpdateConnections(UserControl block)
{
foreach (var connection in Connections)
{
if (connection.Start == block || connection.End == block)
{
BezierLineDrawer.UpdateBezierLine(FlowChartCanvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
}
}
}
#endregion
#region 画布中框选节点控件动作
///
/// 在画布中尝试选取控件
///
///
///
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift))
{
IsSelectControl = true;
// 开始选取时,记录鼠标起始点
startPoint = e.GetPosition(FlowChartCanvas);
// 初始化选取矩形的位置和大小
Canvas.SetLeft(SelectionRectangle, startPoint.X);
Canvas.SetTop(SelectionRectangle, startPoint.Y);
SelectionRectangle.Width = 0;
SelectionRectangle.Height = 0;
// 显示选取矩形
SelectionRectangle.Visibility = Visibility.Visible;
// 捕获鼠标,以便在鼠标移动到Canvas外部时仍能处理事件
FlowChartCanvas.CaptureMouse();
}
}
///
/// 在画布中释放鼠标按下,结束选取状态
///
///
///
private void FlowChartCanvas_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
if (IsSelectControl)
{
IsSelectControl = false;
// 释放鼠标捕获
FlowChartCanvas.ReleaseMouseCapture();
// 隐藏选取矩形(如果需要保持选取状态,可以删除此行)
SelectionRectangle.Visibility = Visibility.Collapsed;
// 处理选取区域内的元素(例如,获取选取范围内的控件)
Rect selectionArea = new Rect(Canvas.GetLeft(SelectionRectangle),
Canvas.GetTop(SelectionRectangle),
SelectionRectangle.Width,
SelectionRectangle.Height);
selectControls.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)
{
selectControls.Add(control);
}
}
}
Console.WriteLine($"一共选取了{selectControls.Count}个控件");
}
}
///
/// 鼠标在画布移动。
/// 选择控件状态下,调整选择框大小
/// 连接状态下,实时更新连接线的终点位置。
/// 移动画布状态下,移动画布。
///
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
{
if (IsSelectControl && e.LeftButton == MouseButtonState.Pressed)
{
// 获取当前鼠标位置
Point currentPoint = e.GetPosition(FlowChartCanvas);
// 更新选取矩形的位置和大小
double x = Math.Min(currentPoint.X, startPoint.X);
double y = Math.Min(currentPoint.Y, startPoint.Y);
double width = Math.Abs(currentPoint.X - startPoint.X);
double height = Math.Abs(currentPoint.Y - startPoint.Y);
Canvas.SetLeft(SelectionRectangle, x);
Canvas.SetTop(SelectionRectangle, y);
SelectionRectangle.Width = width;
SelectionRectangle.Height = height;
}
if (IsConnecting)
{
Point position = e.GetPosition(FlowChartCanvas);
if (currentLine == null || startConnectNodeControl == null)
{
return;
}
currentLine.X1 = Canvas.GetLeft(startConnectNodeControl) + startConnectNodeControl.ActualWidth / 2;
currentLine.Y1 = Canvas.GetTop(startConnectNodeControl) + startConnectNodeControl.ActualHeight / 2;
currentLine.X2 = position.X;
currentLine.Y2 = position.Y;
}
if (IsCanvasDragging)
{
Point currentMousePosition = e.GetPosition(this);
double deltaX = currentMousePosition.X - startPoint.X;
double deltaY = currentMousePosition.Y - startPoint.Y;
translateTransform.X += deltaX;
translateTransform.Y += deltaY;
startPoint = currentMousePosition;
// AdjustCanvasSizeAndContent(deltaX, deltaY);
foreach (var line in Connections)
{
line.Refresh();
}
e.Handled = true; // 防止事件传播影响其他控件
}
}
#endregion
#region 拖动画布实现缩放平移效果
private void FlowChartCanvas_MouseDown(object sender, MouseButtonEventArgs e)
{
if (e.MiddleButton == MouseButtonState.Pressed)
{
IsCanvasDragging = true;
startPoint = 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.2) return;
if (e.Delta > 0 && scaleTransform.ScaleX > 2.0) return;
double scale = e.Delta > 0 ? 0.1 : -0.1;
scaleTransform.ScaleX += scale;
scaleTransform.ScaleY += scale;
}
}
// 设置画布宽度高度
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 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;
FlowChartCanvas.Width = newWidth;
FlowChartCanvas.Height = newHeight;
}
//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 newWidth = Math.Max(FlowChartCanvas.ActualWidth + e.HorizontalChange * scaleTransform.ScaleX, 0);
newWidth = newWidth < 400 ? 400 : newWidth;
FlowChartCanvas.Width = newWidth;
}
//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 newHeight = Math.Max(FlowChartCanvas.ActualHeight + e.VerticalChange * scaleTransform.ScaleY, 0);
newHeight = newHeight < 400 ? 400 : newHeight;
FlowChartCanvas.Height = newHeight;
}
#endregion
#endregion
#endregion
///
/// 卸载DLL文件,清空当前项目
///
///
///
private void UnloadAllButton_Click(object sender, RoutedEventArgs e)
{
FlowEnvironment.ClearAll();
}
///
/// 卸载DLL文件,清空当前项目
///
///
///
private void UnloadAllAssemblies()
{
DllStackPanel.Children.Clear();
FlowChartCanvas.Children.Clear();
Connections.Clear();
NodeControls.Clear();
currentLine = null;
startConnectNodeControl = null;
MessageBox.Show("所有DLL已卸载。", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
}
///
/// 运行测试
///
///
///
private async void ButtonDebugRun_Click(object sender, RoutedEventArgs e)
{
logWindow?.Show();
await FlowEnvironment.StartAsync();
}
///
/// 退出
///
///
///
private void ButtonDebugFlipflopNode_Click(object sender, RoutedEventArgs e)
{
FlowEnvironment.Exit();
}
///
/// 保存为项目文件 (正在重写)
/// JsonConvert.SerializeObject 对象序列化字符串
/// JArray.FromObject 数组序列化
///
///
///
private void ButtonSaveFile_Click(object sender, RoutedEventArgs e)
{
var projectData = FlowEnvironment.SaveProject();
projectData.Basic = new Basic
{
canvas = new FlowCanvas
{
lenght = (float)FlowChartCanvas.Width,
width = (float)FlowChartCanvas.Height,
},
versions = "1",
};
foreach(var node in projectData.Nodes)
{
var control = new ActionNodeControl(null);// GetControl(node.Guid);
Point positionRelativeToParent = control.TranslatePoint(new Point(0, 0), FlowChartCanvas);
node.Position = new Position
{
X = (float)positionRelativeToParent.X,
Y = (float)positionRelativeToParent.Y,
};
}
var projectJsonData = JArray.FromObject(projectData);
var savePath = SaveContentToFile(projectJsonData.ToString());
savePath = System.IO.Path.GetDirectoryName(savePath);
// 复制dll文件
//if (string.IsNullOrEmpty(savePath))
//{
// return;
//}
//foreach (var dll in loadedAssemblies)
//{
// try
// {
// string targetPath = System.IO.Path.Combine(savePath, System.IO.Path.GetFileName(dll.CodeBase));
// // 确保目标目录存在
// Directory.CreateDirectory(savePath);
// var sourceFile = new Uri(dll.CodeBase).LocalPath;
// // 复制文件到目标目录
// File.Copy(sourceFile, targetPath, true);
// }
// catch (Exception ex)
// {
// WriteLog($"DLL复制失败:{dll.CodeBase} \r\n错误:{ex}\r\n");
// }
//}
/*
try
{
// 生成节点信息
var nodeInfos = nodeControls.Select(control =>
{
var node = control.ViewModel.Node;
Point positionRelativeToParent = control.TranslatePoint(new Point(0, 0), FlowChartCanvas);
var trueNodes = control.ViewModel.Node.SucceedBranch.Select(item => item.Guid); // 真分支
var falseNodes = control.ViewModel.Node.FailBranch.Select(item => item.Guid);// 假分支
var upstreamNodes = control.ViewModel.Node.UpstreamBranch.Select(item => item.Guid);// 上游分支
// 常规节点的参数信息
List parameterData = [];
if (node?.MethodDetails?.ExplicitDatas is not null
&& (node.MethodDetails.MethodDynamicType == Serein.Library.Enums.NodeType.Action
|| node.MethodDetails.MethodDynamicType == Serein.Library.Enums.NodeType.Flipflop))
{
parameterData = node.MethodDetails
.ExplicitDatas
.Where(it => it is not null)
.Select(it => new Parameterdata
{
state = it.IsExplicitData,
value = it.DataValue
})
.ToList();
}
else if (node is SingleExpOpNode expOpNode)
{
parameterData.Add(new Parameterdata
{
state = true,
expression = expOpNode.Expression,
});
}
else if (node is SingleConditionNode conditionNode)
{
parameterData.Add(new Parameterdata
{
state = conditionNode.IsCustomData,
expression = conditionNode.Expression,
value = conditionNode.CustomData switch
{
Type when conditionNode.CustomData.GetType() == typeof(int)
&& conditionNode.CustomData.GetType() == typeof(double)
&& conditionNode.CustomData.GetType() == typeof(float)
=> ((double)conditionNode.CustomData).ToString(),
Type when conditionNode.CustomData.GetType() == typeof(bool) => ((bool)conditionNode.CustomData).ToString(),
_ => conditionNode.CustomData?.ToString()!,
}
});
}
return new NodeInfo
{
Guid = node.Guid,
Name = node.MethodDetails?.MethodName,
Label = node.DisplayName ?? "",
Type = node.GetType().ToString(),
Position = new Position
{
x = (float)positionRelativeToParent.X,
y = (float)positionRelativeToParent.Y,
},
TrueNodes = trueNodes.ToArray(),
FalseNodes = falseNodes.ToArray(),
UpstreamNodes = upstreamNodes.ToArray(),
ParameterData = parameterData.ToArray(),
};
}).ToList();
// 保存区域
var regionObjs = nodeControls.Where(item =>
item.GetType() == typeof(ConditionRegionControl) ||
item.GetType() == typeof(ActionRegionControl))
.ToList()
.Select(region =>
{
WriteLog(region.GetType().ToString() + "\r\n");
if (region is ConditionRegionControl && region.ViewModel.Node is CompositeConditionNode conditionRegion) // 条件区域控件
{
List