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 readonly TransformGroup canvasTransformGroup; /// /// 缩放画布 /// private readonly ScaleTransform scaleTransform; /// /// 平移画布 /// private readonly TranslateTransform translateTransform; #endregion private IFlowEnvironment EnvDecorator; public FlowCanvasView() { ViewModel = App.GetService().FlowCanvasViewModel; this.DataContext = ViewModel; 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) { var canvasGuid = this.ViewModel.CanvasGuid; Task.Run(async () => { await EnvDecorator.CreateNodeAsync(canvasGuid, 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) { var canvasGuid = this.ViewModel.CanvasGuid; Task.Run(async () => { await EnvDecorator.CreateNodeAsync(canvasGuid, 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 async 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) { 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); } #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; } var canvasGuid = this.ViewModel.CanvasGuid; await EnvDecorator.ConnectArgSourceNodeAsync( canvasGuid, 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)) { var canvasGuid = this.ViewModel.CanvasGuid; EnvDecorator.RemoveNodeAsync(canvasGuid, guid); } } } SelectionRectangle.Visibility = Visibility.Collapsed; })); return contextMenu; // nodeControl.ContextMenu = contextMenu; } } }