Delete Serein.Workbench.Avalonia directory

没写
This commit is contained in:
一泓秋水
2025-07-30 12:04:24 +08:00
committed by GitHub
parent 4a82f88f80
commit 8fc2b3583a
60 changed files with 0 additions and 5206 deletions

View File

@@ -1,17 +0,0 @@
using Serein.Library.Api;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Api
{
/// <summary>
/// 流程事件管理,转发流程运行环境中触发的事件到工作台各个订阅者
/// </summary>
internal interface IFlowEEForwardingService : IFlowEnvironmentEvent
{
}
}

View File

@@ -1,32 +0,0 @@
using Serein.Workbench.Avalonia.Custom.Node.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Api
{
/// <summary>
/// 约束具有容器功能的节点控件应该有什么方法
/// </summary>
public interface INodeContainerControl
{
/// <summary>
/// 放置一个节点
/// </summary>
/// <param name="nodeControl"></param>
bool PlaceNode(NodeControlBase nodeControl);
/// <summary>
/// 取出一个节点
/// </summary>
/// <param name="nodeControl"></param>
bool TakeOutNode(NodeControlBase nodeControl);
/// <summary>
/// 取出所有节点(用于删除容器)
/// </summary>
void TakeOutAll();
}
}

View File

@@ -1,23 +0,0 @@
using Serein.Library;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Api
{
//internal interface INodeControl
//{
// /// <summary>
// /// 对应的节点实体
// /// </summary>
// NodeModelBase NodeModelBase { get; }
// /// <summary>
// /// 初始化使用的方法,设置节点实体
// /// </summary>
// /// <param name="nodeModel"></param>
// void SetNodeModel(NodeModelBase nodeModel);
//}
}

View File

@@ -1,52 +0,0 @@
using Serein.Library;
using Serein.Workbench.Avalonia.Custom.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Api
{
/// <summary>
/// 约束一个节点应该有哪些控制点
/// </summary>
public interface INodeJunction
{
/// <summary>
/// 方法执行入口控制点
/// </summary>
NodeJunctionView ExecuteJunction { get; }
/// <summary>
/// 执行完成后下一个要执行的方法控制点
/// </summary>
NodeJunctionView NextStepJunction { get; }
/// <summary>
/// 参数节点控制点
/// </summary>
NodeJunctionView[] ArgDataJunction { get; }
/// <summary>
/// 返回值控制点
/// </summary>
NodeJunctionView ReturnDataJunction { get; }
/// <summary>
/// 获取目标参数控制点用于防止wpf释放资源导致找不到目标节点返回-1,-1的坐标
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
NodeJunctionView GetJunctionOfArgData(int index)
{
var arr = ArgDataJunction;
if (index >= arr.Length)
{
return null;
}
return arr[index];
}
}
}

View File

@@ -1,21 +0,0 @@
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="Serein.Workbench.Avalonia.App"
xmlns:cv="clr-namespace:Serein.Workbench.Avalonia.Custom.Views"
RequestedThemeVariant="Default">
<!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->
<Application.Styles>
<FluentTheme />
</Application.Styles>
<Application.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<!--自定义控件:依赖信息-->
<ResourceInclude Source="/Custom/Views/FlowLibraryInfoView.axaml" />
</ResourceDictionary.MergedDictionaries>
</ResourceDictionary>
</Application.Resources>
</Application>

View File

@@ -1,140 +0,0 @@
using Avalonia;
using Avalonia.Controls.ApplicationLifetimes;
using Avalonia.Data.Core.Plugins;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Microsoft.Extensions.DependencyInjection;
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow;
using Serein.NodeFlow.Env;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Custom.Node.ViewModels;
using Serein.Workbench.Avalonia.Custom.Node.Views;
using Serein.Workbench.Avalonia.Custom.ViewModels;
using Serein.Workbench.Avalonia.Services;
using Serein.Workbench.Avalonia.ViewModels;
using Serein.Workbench.Avalonia.Views;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia;
public static class ServiceCollectionExtensions
{
/// <summary>
/// 注册ViewModel
/// </summary>
/// <param name="collection"></param>
public static void AddViewModelServices(this IServiceCollection collection)
{
collection.AddTransient<MainViewModel>(); // 主窗体
collection.AddTransient<MainMenuBarViewModel>(); // 主窗体菜单
collection.AddTransient<FlowLibrarysViewModel>(); // 依赖集合
collection.AddTransient<FlowLibraryMethodInfoViewModel>(); // 预览的方法信息
//collection.AddTransient<ParameterDetailsViewModel>(); // 节点参数信息
collection.AddTransient<NodeContainerViewModel>(); // 节点容器(画布)
collection.AddTransient<ActionNodeViewModel>(); // 节点容器(画布)
//collection.AddTransient<FlowLibraryInfoViewModel>(); // 依赖信息
}
public static void AddWorkbenchServices(this IServiceCollection collection)
{
collection.AddSingleton<IFlowEEForwardingService, FlowEEForwardingService>(); // 流程事件管理
collection.AddSingleton<IWorkbenchEventService, WorkbenchEventService>(); // 流程事件管理
collection.AddSingleton<INodeOperationService, NodeOperationService>(); // 节点操作管理
collection.AddSingleton<IKeyEventService, KeyEventService>(); // 按键事件管理
//collection.AddSingleton<FlowNodeControlService>(); // 流程节点控件管理
}
/// <summary>
/// 注册流程接口相关实例
/// </summary>
/// <param name="collection"></param>
public static void AddFlowServices(this IServiceCollection collection)
{
#region
Func<SynchronizationContext> getSyncContext = null;
Dispatcher.UIThread.Invoke(() =>
{
var uiContext = SynchronizationContext.Current; // 在UI线程上获取UI线程上下文信息
if(uiContext is not null)
{
getSyncContext = () => uiContext;
}
});
UIContextOperation? uIContextOperation = null;
uIContextOperation = new UIContextOperation(getSyncContext); // 封装一个调用UI线程的工具类
var flowEnvironmentDecorator = new FlowEnvironmentDecorator();
flowEnvironmentDecorator.SetUIContextOperation(uIContextOperation);
collection.AddSingleton<UIContextOperation>(uIContextOperation); // 注册UI线程操作上下文
collection.AddSingleton<IFlowEnvironment>(flowEnvironmentDecorator); // 注册运行环境
collection.AddSingleton<IFlowEnvironmentEvent>(flowEnvironmentDecorator); // 注册运行环境事件
//var strte = tcs.Task.ConfigureAwait(false).GetAwaiter().GetResult();
//if (strte) // 等待实例生成完成
//{
//}
#endregion
}
}
public partial class App : Application
{
private static IServiceProvider? ServiceProvider;
public static T GetService<T>() where T : class
{
return ServiceProvider?.GetService<T>() ?? throw new NullReferenceException();
}
public override void Initialize()
{
AvaloniaXamlLoader.Load(this);
}
public override async void OnFrameworkInitializationCompleted()
{
// 如果使用 CommunityToolkit则需要用下面一行移除 Avalonia 数据验证。
// 如果没有这一行,数据验证将会在 Avalonia 和 CommunityToolkit 中重复。
BindingPlugins.DataValidators.RemoveAt(0);
// 注册应用程序运行所需的所有服务
var collection = new ServiceCollection();
collection.AddWorkbenchServices();
collection.AddFlowServices();
collection.AddViewModelServices();
var services = collection.BuildServiceProvider(); // 绑定并返回获取实例的服务接口
App.ServiceProvider = services;
var vm = App.ServiceProvider?.GetRequiredService<MainViewModel>();
if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)
{
desktop.MainWindow = new MainWindow
{
DataContext = vm
};
}
else if (ApplicationLifetime is ISingleViewApplicationLifetime singleViewPlatform)
{
singleViewPlatform.MainView = new MainView
{
DataContext = vm
};
}
base.OnFrameworkInitializationCompleted();
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

View File

@@ -1,38 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Commands
{
internal abstract class CommandBase
{
// CanExecuteChanged 事件
public event EventHandler CanExecuteChanged;
/// <summary>
/// 是否可以执行命令,子类可以重写这个方法来提供具体的可执行条件
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public virtual bool CanExecute(object parameter)
{
return true; // 默认实现返回 true表示命令可以执行
}
/// <summary>
/// 执行命令,子类可以重写这个方法来实现具体的命令逻辑
/// </summary>
/// <param name="parameter"></param>
public abstract void Execute(object parameter);
/// <summary>
/// 用于触发 CanExecuteChanged 事件
/// </summary>
protected void OnCanExecuteChanged()
{
CanExecuteChanged?.Invoke(this, EventArgs.Empty);
}
}
}

View File

@@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Commands
{
/// <summary>
/// 流程控制命令
/// </summary>
internal class MyCommand : CommandBase
{
private readonly Action _execute;
private readonly Func<bool> _canExecute;
/// <summary>
/// 构造函数接收执行动作和是否可执行的条件
/// </summary>
/// <param name="execute"></param>
/// <param name="canExecute"></param>
/// <exception cref="ArgumentNullException"></exception>
public MyCommand(Action execute, Func<bool> canExecute = null)
{
_execute = execute ?? throw new ArgumentNullException(nameof(execute));
_canExecute = canExecute;
}
/// <summary>
/// 重写 CanExecute 方法,基于 _canExecute 委托的结果来判断命令是否可执行
/// </summary>
/// <param name="parameter"></param>
/// <returns></returns>
public override bool CanExecute(object parameter)
{
return _canExecute?.Invoke() ?? true;
}
/// <summary>
/// 重写 Execute 方法,执行具体的命令逻辑
/// </summary>
/// <param name="parameter"></param>
public override void Execute(object parameter)
{
_execute();
}
}
}

View File

@@ -1,127 +0,0 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Avalonia.Threading;
using Avalonia;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia.VisualTree;
namespace Serein.Workbench.Avalonia.Controls
{
/// <summary>
/// 实现拖动的控件
/// </summary>
public partial class DragControls : UserControl
{
/// <summary>
/// 记录上一次鼠标位置
/// </summary>
private Point lastMousePosition;
/// <summary>
/// 用于平滑更新坐标的计时器
/// </summary>
private DispatcherTimer _timer;
/// <summary>
/// 标记是否先启动了拖动
/// </summary>
private bool isDragging = false;
/// <summary>
/// 需要更新的坐标点
/// </summary>
private PixelPoint _targetPosition;
public DragControls()
{
InitializeComponent();
// 添加当前控件的事件监听
PointerPressed += OnPointerPressed;
PointerMoved += OnPointerMoved;
PointerReleased += OnPointerReleased;
// 初始化计时器
_timer = new DispatcherTimer
{
Interval = TimeSpan.FromMilliseconds(10)
};
_timer.Tick += OnTimerTick;
}
/// <summary>
/// 计时器事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void OnTimerTick(object sender, EventArgs e)
{
var window = this.FindAncestorOfType<Window>();
if (window != null && window.Position != _targetPosition)
{
// 更新坐标
window.Position = _targetPosition;
}
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
private void OnPointerPressed(object sender, PointerPressedEventArgs e)
{
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return;
// 启动拖动
isDragging = true;
// 记录当前坐标
lastMousePosition = e.GetPosition(this);
e.Handled = true;
// 启动计时器
_timer.Start();
}
private void OnPointerReleased(object sender, PointerReleasedEventArgs e)
{
if (!isDragging) return;
// 停止拖动
isDragging = false;
e.Handled = true;
// 停止计时器
_timer.Stop();
}
private void OnPointerMoved(object sender, PointerEventArgs e)
{
if (!e.GetCurrentPoint(this).Properties.IsLeftButtonPressed) return;
// 如果没有启动拖动,则不执行
if (!isDragging) return;
var currentMousePosition = e.GetPosition(this);
var offset = currentMousePosition - lastMousePosition;
var window = this.FindAncestorOfType<Window>();
if (window != null)
{
// 记录当前坐标
_targetPosition = new PixelPoint(window.Position.X + (int)offset.X,
window.Position.Y + (int)offset.Y);
}
}
}
}

View File

@@ -1,31 +0,0 @@
using Avalonia.Data.Converters;
using Avalonia.Media;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Converters
{
public class BoolToBrushConverter : IValueConverter
{
public IBrush TrueBrush { get; set; } = Brushes.LightBlue;
public IBrush FalseBrush { get; set; } = Brushes.Transparent;
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is bool boolValue)
{
return boolValue ? TrueBrush : FalseBrush;
}
return FalseBrush;
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
return false;
}
}
}

View File

@@ -1,42 +0,0 @@
using Avalonia.Data;
using Avalonia.Data.Converters;
using Serein.Library;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Converters
{
internal class IsVisibleOfParameterConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
if(value is ParameterDetails pd)
{
if (pd.InputType == ParameterValueInputType.Input)
{
return false;
}
else
{
return true;
}
}
// converter used for the wrong type
return new BindingNotification(new InvalidCastException(), BindingErrorType.Error);
}
public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
}

View File

@@ -1,245 +0,0 @@
using Avalonia.Controls.Shapes;
using Avalonia.Controls;
using Avalonia.Media;
using Serein.Workbench.Avalonia.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Avalonia;
using Avalonia.VisualTree;
using Serein.Library;
namespace Serein.Workbench.Avalonia.Custom.Junction
{
public abstract class JunctionControlBase : Control
{
protected JunctionControlBase()
{
this.Width = 25;
this.Height = 20;
this.PointerPressed += JunctionControlBase_PointerPressed;
this.PointerMoved += JunctionControlBase_PointerMoved;
this.PointerExited += JunctionControlBase_PointerExited;
}
/*
#region 控件属性,所在的节点
public static readonly DependencyProperty NodeProperty =
DependencyProperty.Register(nameof(MyNode), typeof(NodeModelBase), typeof(JunctionControlBase), new PropertyMetadata(default(NodeModelBase)));
//public NodeModelBase NodeModel;
/// <summary>
/// 所在的节点
/// </summary>
public NodeModelBase MyNode
{
get { return (NodeModelBase)GetValue(NodeProperty); }
set { SetValue(NodeProperty, value); }
}
#endregion
#region 控件属性,连接器类型
public static readonly DependencyProperty JunctionTypeProperty =
DependencyProperty.Register(nameof(JunctionType), typeof(string), typeof(JunctionControlBase), new PropertyMetadata(default(string)));
/// <summary>
/// 控制点类型
/// </summary>
public JunctionType JunctionType
{
get { return EnumHelper.ConvertEnum<JunctionType>(GetValue(JunctionTypeProperty).ToString()); }
set { SetValue(JunctionTypeProperty, value.ToString()); }
}
#endregion
protected override Geometry DefiningGeometry => StreamGeometry;
*/
protected readonly StreamGeometry StreamGeometry = new StreamGeometry();
/// <summary>
/// 重绘方法
/// </summary>
/// <param name="drawingContext"></param>
public abstract void OnRender(DrawingContext drawingContext);
/// <summary>
/// 中心点
/// </summary>
public abstract Point MyCenterPoint { get; }
/// <summary>
/// 禁止连接
/// </summary>
private bool IsConnectionDisable;
/// <summary>
/// 处理鼠标悬停状态
/// </summary>
private bool _isMouseOver;
public bool IsMouseOver
{
get => _isMouseOver;
set
{
if (_isMouseOver != value)
{
//GlobalJunctionData.MyGlobalConnectingData.CurrentJunction = this;
_isMouseOver = value;
InvalidateVisual();
}
}
}
/// <summary>
/// 控件重绘事件
/// </summary>
/// <param name="drawingContext"></param>
public override void Render(DrawingContext drawingContext)
{
OnRender(drawingContext);
}
/// <summary>
/// 获取背景颜色
/// </summary>
/// <returns></returns>
protected Brush GetBackgrounp()
{
return (Brush)Brushes.Transparent;
//var myData = GlobalJunctionData.MyGlobalConnectingData;
//if (!myData.IsCreateing)
//{
// return Brushes.Transparent;
//}
//if (IsMouseOver)
//{
// if (myData.IsCanConnected)
// {
// if (myData.Type == JunctionOfConnectionType.Invoke)
// {
// return myData.ConnectionInvokeType.ToLineColor();
// }
// else
// {
// return myData.ConnectionArgSourceType.ToLineColor();
// }
// }
// else
// {
// return Brushes.Red;
// }
//}
//else
//{
// return Brushes.Transparent;
//}
}
private object lockObj = new object();
/// <summary>
/// 控件获得鼠标焦点事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void JunctionControlBase_PointerMoved(object? sender, global::Avalonia.Input.PointerEventArgs e)
{
//if (!GlobalJunctionData.MyGlobalConnectingData.IsCreateing) return;
//if (IsMouseOver) return;
IsMouseOver = true;
this.InvalidateVisual();
}
/// <summary>
/// 控件失去鼠标焦点事件
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void JunctionControlBase_PointerExited(object? sender, global::Avalonia.Input.PointerEventArgs e)
{
IsMouseOver = false;
e.Handled = true;
}
/// <summary>
/// 在碰撞点上按下鼠标控件开始进行移动
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void JunctionControlBase_PointerPressed(object? sender, global::Avalonia.Input.PointerPressedEventArgs e)
{
throw new NotImplementedException();
//if (e.LeftButton == MouseButtonState.Pressed)
//{
// var canvas = MainWindow.GetParentOfType<Canvas>(this);
// if (canvas != null)
// {
// var myData = GlobalJunctionData.MyGlobalConnectingData;
// myData.Reset();
// myData.IsCreateing = true; // 表示开始连接
// myData.StartJunction = this;
// myData.CurrentJunction = this;
// myData.StartPoint = this.TranslatePoint(new Point(this.Width / 2, this.Height / 2), canvas);
// var junctionOfConnectionType = this.JunctionType.ToConnectyionType();
// ConnectionLineShape bezierLine; // 类别
// Brush brushColor; // 临时线的颜色
// if (junctionOfConnectionType == JunctionOfConnectionType.Invoke)
// {
// brushColor = ConnectionInvokeType.IsSucceed.ToLineColor();
// }
// else if (junctionOfConnectionType == JunctionOfConnectionType.Arg)
// {
// brushColor = ConnectionArgSourceType.GetOtherNodeData.ToLineColor();
// }
// else
// {
// return;
// }
// bezierLine = new ConnectionLineShape(LineType.Bezier,
// myData.StartPoint,
// myData.StartPoint,
// brushColor,
// isTop: true); // 绘制临时的线
// Mouse.OverrideCursor = Cursors.Cross; // 设置鼠标为正在创建连线
// myData.MyLine = new MyLine(canvas, bezierLine);
// }
//}
//e.Handled = true;
}
/// <summary>
/// 获取起始控制点
/// </summary>
/// <returns></returns>
private Point GetStartPoint()
{
if (this.GetTransformedBounds() is TransformedBounds transformed)
{
var size = transformed.Bounds.Size;
return new Point(size.Width / 2, size.Height / 2); // 起始节点选择右侧边缘中心
}
else
{
return new Point(0, 0);
}
}
}
}

View File

@@ -1,27 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library;
using Serein.NodeFlow.Model;
using Serein.Workbench.Avalonia.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Custom.Node.ViewModels
{
internal partial class ActionNodeViewModel : NodeViewModelBase
{
[ObservableProperty]
private SingleActionNode? nodeMoel;
internal override NodeModelBase NodeModelBase
{ get => NodeMoel ?? throw new NotImplementedException(); set => NodeMoel = (SingleActionNode)value; }
public ActionNodeViewModel()
{
}
}
}

View File

@@ -1,90 +0,0 @@
using Avalonia.Controls;
using Avalonia.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Custom.Node.Views;
using Serein.Workbench.Avalonia.Model;
using Serein.Workbench.Avalonia.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Custom.Node.ViewModels
{
/// <summary>
/// 节点ViewModel基类
/// </summary>
internal abstract class NodeViewModelBase : ViewModelBase
{
internal abstract NodeModelBase NodeModelBase { get; set; }
private Canvas NodeCanvas;
/// <summary>
/// 如果该节点放置在了某个容器节点,就会记录这个容器节点
/// </summary>
private INodeContainerControl NodeContainerControl { get; }
public NodeModelBase NodeModel { get; set; }
//public NodeControlViewModelBase ViewModel { get; set; }
public void SetNodeModel(NodeModelBase nodeModel) => this.NodeModel = nodeModel;
/// <summary>
/// 设置绑定:
/// Canvas.X and Y 画布位置
/// </summary>
public void SetBinding()
{
/* // 绑定 Canvas.Left
Binding leftBinding = new Binding("X")
{
Source = ViewModel.NodeModel.Position, // 如果 X 属性在当前 DataContext 中
Mode = BindingMode.TwoWay
};
BindingOperations.Apply(this, Canvas.LeftProperty, leftBinding);
// 绑定 Canvas.Top
Binding topBinding = new Binding("Y")
{
Source = ViewModel.NodeModel.Position, // 如果 Y 属性在当前 DataContext 中
Mode = BindingMode.TwoWay
};
BindingOperations.SetBinding(this, Canvas.TopProperty, topBinding);*/
}
/// <summary>
/// 穿透视觉树获取指定类型的第一个元素
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="parent"></param>
/// <returns></returns>
//protected T FindVisualChild<T>(DependencyObject parent) where T : DependencyObject
//{
// for (int i = 0; i < VisualTreeHelper.GetChildrenCount(parent); i++)
// {
// var child = VisualTreeHelper.GetChild(parent, i);
// if (child is T typedChild)
// {
// return typedChild;
// }
// var childOfChild = FindVisualChild<T>(child);
// if (childOfChild != null)
// {
// return childOfChild;
// }
// }
// return null;
//}
}
}

View File

@@ -1,50 +0,0 @@
<local:NodeControlBase xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="200" d:DesignHeight="100"
x:Class="Serein.Workbench.Avalonia.Custom.Node.Views.ActionNodeView"
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.Node.ViewModels"
xmlns:local="clr-namespace:Serein.Workbench.Avalonia.Custom.Node.Views"
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
xmlns:cv="clr-namespace:Serein.Workbench.Avalonia.Custom.Views"
xmlns:dtp="using:Serein.Workbench.Avalonia.DataTemplates"
Background="#C6EEF7"
x:DataType="vm:ActionNodeViewModel">
<Design.DataContext>
<vm:ActionNodeViewModel />
</Design.DataContext>
<Border>
<Grid RowDefinitions="25,*,*,*,*">
<!--调用控制点,方法名称,下一个方法调用控制点-->
<Grid x:Name="HeaderGrid" Grid.Row="0" ColumnDefinitions="auto,*,auto" VerticalAlignment="Center">
<cv:NodeJunctionView x:Name="ExecuteJunctionControl" Grid.Column="0" JunctionType="Execute" MyNode="{Binding NodeMoel}" Width="30" Height="15" Margin="4,0,2,0" />
<StackPanel Grid.Column="1" Grid.RowSpan="2" >
<TextBlock Text="{Binding NodeMoel.DisplayName}" FontSize="17" HorizontalAlignment="Center">
<ToolTip.Tip>
<StackPanel >
<TextBlock Text="{Binding NodeMoel.MethodDetails}" FontSize="12"/>
</StackPanel>
</ToolTip.Tip>
</TextBlock>
</StackPanel>
<cv:NodeJunctionView x:Name="NextStepJunctionControl" Grid.Column="2" JunctionType="NextStep" MyNode="{Binding NodeMoel}" Width="30" Height="15" Margin="2,0,8,0"/>
</Grid>
<!--入参信息-->
<StackPanel Grid.Row="1" Background="#E3FDFD">
<ItemsControl ItemsSource="{Binding NodeMoel.MethodDetails.ParameterDetailss}">
<ItemsControl.ItemTemplate>
<dtp:NodeMethodParameterInfoDataTemplate>
</dtp:NodeMethodParameterInfoDataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</StackPanel>
</Grid>
</Border>
</local:NodeControlBase>

View File

@@ -1,31 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Serein.Library;
using Serein.NodeFlow.Model;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Custom.Node.ViewModels;
using Serein.Workbench.Avalonia.Custom.Views;
namespace Serein.Workbench.Avalonia.Custom.Node.Views;
public partial class ActionNodeView : NodeControlBase, INodeJunction
{
private ActionNodeViewModel _vm;
public ActionNodeView()
{
InitializeComponent();
//_vm = App.GetService<ActionNodeViewModel>();
//DataContext = _vm;
}
public NodeJunctionView ExecuteJunction => this.ExecuteJunctionControl;
public NodeJunctionView NextStepJunction => this.NextStepJunctionControl;
public NodeJunctionView[] ArgDataJunction => throw new System.NotImplementedException();
public NodeJunctionView ReturnDataJunction => throw new System.NotImplementedException();
}

View File

@@ -1,106 +0,0 @@
using Avalonia.Controls;
using Avalonia.Data;
using Avalonia.Media;
using Serein.Library;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Custom.Views;
using Serein.Workbench.Avalonia.Model;
using Serein.Workbench.Avalonia.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Custom.Node.Views
{
public abstract class NodeControlBase : UserControl
{
/// <summary>
/// 记录与该节点控件有关的所有连接
/// </summary>
private readonly List<NodeConnectionLineControl> connectionControls = new List<NodeConnectionLineControl>();
protected NodeControlBase()
{
this.Background = Brushes.Transparent;
}
/// <summary>
/// 添加与该节点有关的连接后,记录下来
/// </summary>
/// <param name="connection"></param>
public void AddConnection(NodeConnectionLineControl connection)
{
connectionControls.Add(connection);
}
/// <summary>
/// 删除了连接之后,还需要从节点中的记录移除
/// </summary>
/// <param name="connection"></param>
public void RemoveConnection(NodeConnectionLineControl connection)
{
connectionControls.Remove(connection);
connection.Remove();
}
/// <summary>
/// 删除所有连接
/// </summary>
public void RemoveAllConection()
{
foreach (var connection in this.connectionControls)
{
connection.Remove();
}
}
/// <summary>
/// 更新与该节点有关的数据
/// </summary>
public void UpdateLocationConnections()
{
foreach (var connection in this.connectionControls)
{
connection.RefreshLineDsiplay(); // 主动更新连线位置
}
}
/// <summary>
/// 放置在某个节点容器中
/// </summary>
public void PlaceToContainer(INodeContainerControl nodeContainerControl)
{
//this.nodeContainerControl = nodeContainerControl;
//NodeCanvas.Children.Remove(this); // 临时从画布上移除
//var result = nodeContainerControl.PlaceNode(this);
//if (!result) // 检查是否放置成功,如果不成功,需要重新添加回来
//{
// NodeCanvas.Children.Add(this); // 从画布上移除
//}
}
/// <summary>
/// 从某个节点容器取出
/// </summary>
public void TakeOutContainer()
{
//var result = nodeContainerControl.TakeOutNode(this); // 从控件取出
//if (result) // 移除成功时才添加到画布上
//{
// NodeCanvas.Children.Add(this); // 重新添加到画布上
// if (nodeContainerControl is NodeControlBase containerControl)
// {
// NodeModel.Position.X = NodeModel.Position.X + containerControl.Width + 10;
// NodeModel.Position.Y = NodeModel.Position.Y;
// }
//}
}
}
}

View File

@@ -1,63 +0,0 @@
using Avalonia.Data;
using Avalonia.Interactivity;
using Avalonia;
using Serein.Library;
using Serein.Workbench.Avalonia.ViewModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Reactive.Subjects;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Input;
using System.Windows.Input;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Collections;
using System.Collections;
using CommunityToolkit.Mvvm.ComponentModel;
using System.Reflection;
namespace Serein.Workbench.Avalonia.Custom.ViewModels
{
internal partial class FlowLibraryInfoViewModel:ViewModelBase
{
/// <summary>
/// 依赖名称
/// </summary>
[ObservableProperty]
public string _libraryName;
private ObservableCollection<MethodDetailsInfo> activateMethods;
private ObservableCollection<MethodDetailsInfo> flipflopMethods;
/// <summary>
/// 动作节点
/// </summary>
public ObservableCollection<MethodDetailsInfo> ActivateMethods { get => activateMethods; set => SetProperty(ref activateMethods,value); }
/// <summary>
/// 触发器节点
/// </summary>
public ObservableCollection<MethodDetailsInfo> FlipflopMethods { get => flipflopMethods; set => SetProperty(ref activateMethods, value); }
///// <summary>
///// 加载项目信息
///// </summary>
///// <param name="libraryMds"></param>
//public void LoadLibraryInfo(LibraryMds libraryMds)
//{
// this.AssemblyName = libraryMds.AssemblyName;
//}
}
}

View File

@@ -1,67 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library;
using Serein.Workbench.Avalonia.Services;
using Serein.Workbench.Avalonia.ViewModels;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Custom.ViewModels
{
internal partial class FlowLibraryMethodInfoViewModel : ViewModelBase
{
/// <summary>
/// 当前预览的方法信息
/// </summary>
[ObservableProperty]
private MethodDetailsInfo methodDetailsInfo;
private IWorkbenchEventService workbenchEventService;
public FlowLibraryMethodInfoViewModel()
{
workbenchEventService = App.GetService<IWorkbenchEventService>();
workbenchEventService.OnPreviewlMethodInfo += WorkbenchEventService_OnPreviewlMethodInfo;
methodDetailsInfo = new MethodDetailsInfo
{
AssemblyName = "wait selection...",
MethodAnotherName = "wait selection...",
MethodName = "wait selection...",
NodeType = "wait selection...",
ReturnTypeFullName = "wait selection...",
IsParamsArgIndex = -1,
ParameterDetailsInfos = []
};
}
private void WorkbenchEventService_OnPreviewlMethodInfo(PreviewlMethodInfoEventArgs eventArgs)
{
var mdInfo = eventArgs.MethodDetailsInfo;
MethodDetailsInfo = mdInfo;
Debug.WriteLine($"预览了 {mdInfo.AssemblyName } - {mdInfo.MethodAnotherName} 方法");
}
}
}

View File

@@ -1,60 +0,0 @@
using Newtonsoft.Json;
using Serein.Library;
using Serein.Library.Api;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Services;
using Serein.Workbench.Avalonia.ViewModels;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Diagnostics.Tracing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Custom.ViewModels
{
internal partial class FlowLibrarysViewModel : ViewModelBase
{
/// <summary>
/// 运行环境
/// </summary>
private IFlowEnvironment flowEnvironment { get; }
/// <summary>
/// 流程运行环境事件服务
/// </summary>
private IFlowEEForwardingService feefService { get; }
/// <summary>
/// 运行环境加载的依赖
/// </summary>
public ObservableCollection<LibraryMds> LibraryList { get; } = new ObservableCollection<LibraryMds>();
public FlowLibrarysViewModel()
{
flowEnvironment = App.GetService<IFlowEnvironment>();
feefService = App.GetService<IFlowEEForwardingService>();
feefService.OnDllLoad += FeefService_OnDllLoad; ;
}
/// <summary>
/// 加载了依赖信息
/// </summary>
/// <param name="e"></param>
private void FeefService_OnDllLoad(LoadDllEventArgs e)
{
Debug.WriteLine(e.NodeLibraryInfo.AssemblyName + " count :" + e.MethodDetailss.Count);
var libraryMds = new LibraryMds { AssemblyName = e.NodeLibraryInfo.AssemblyName, Mds = e.MethodDetailss.ToArray() };
LibraryList.Add(libraryMds);
}
}
}

View File

@@ -1,36 +0,0 @@
using Microsoft.Extensions.DependencyInjection;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.Workbench.Avalonia.ViewModels;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Custom.ViewModels
{
internal partial class MainMenuBarViewModel : ViewModelBase
{
private IFlowEnvironment flowEnvironment { get; }
public MainMenuBarViewModel()
{
flowEnvironment = App.GetService<IFlowEnvironment>();
var uiContextOperation = App.GetService<UIContextOperation>();
}
public void SaveProjectCommand()
{
flowEnvironment.SaveProject();
}
}
}

View File

@@ -1,29 +0,0 @@
using Avalonia.Controls;
using Serein.Library.Api;
using Serein.NodeFlow.Env;
using Serein.Workbench.Avalonia.ViewModels;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Custom.ViewModels
{
internal class NodeContainerViewModel : ViewModelBase
{
/// <summary>
/// 正在创建方法调用关系的连接线
/// </summary>
public bool IsConnectionInvokeNode { get; set; } = false;
/// <summary>
/// 正在创建参数传递关系的连接线
/// </summary>
public bool IsConnectionArgSourceNode { get; set; } = false;
public NodeContainerViewModel()
{
}
}
}

View File

@@ -1,74 +0,0 @@
using Avalonia.Controls;
using Avalonia;
using CommunityToolkit.Mvvm.ComponentModel;
using Serein.Library;
using Serein.Workbench.Avalonia.Custom.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using Serein.Workbench.Avalonia.ViewModels;
namespace Serein.Workbench.Avalonia.Custom.ViewModels
{
internal partial class ParameterDetailsViewModel : ViewModelBase
{
public ParameterDetails ParameterDetails { get; set; }
public ParameterDetailsViewModel()
{
}
public ParameterDetailsViewModel(ParameterDetails parameterDetails)
{
ParameterDetails = parameterDetails;
RefreshIsVisible();
// 监视“是否为显式参数”更改
ParameterDetails.PropertyChanged += (o, e) =>
{
if (nameof(ParameterDetails.IsExplicitData).Equals(e.PropertyName))
RefreshIsVisible();
};
}
private void RefreshIsVisible()
{
if (!ParameterDetails.IsExplicitData)
{
// 并非显式设置参数
IsVisibleA = true;
IsVisibleB = false;
IsVisibleC = false;
return;
}
if (ParameterDetails.InputType == ParameterValueInputType.Input )
{
// 值类型
IsVisibleA = false;
IsVisibleB = true;
IsVisibleC = false;
}
else
{
// 选项类型
IsVisibleA = false;
IsVisibleB = false;
IsVisibleC = true;
}
}
[ObservableProperty]
private bool isVisibleA;
[ObservableProperty]
private bool isVisibleB;
[ObservableProperty]
private bool isVisibleC;
}
}

View File

@@ -1,239 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.Platform;
using Avalonia.Skia;
using Avalonia.Styling;
using Serein.Library.Utils;
using Serein.Workbench.Avalonia.Extension;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Custom.Views
{
public class ConnectionLineShape : Control
{
private readonly double strokeThickness;
/// <summary>
/// 确定起始坐标和目标坐标、外观样式的曲线
/// </summary>
/// <param name="left">起始坐标</param>
/// <param name="right">结束坐标</param>
/// <param name="brush">颜色</param>
/// <param name="isDotted">是否为虚线</param>
public ConnectionLineShape(Point left,
Point right,
Brush brush,
bool isDotted = false,
bool isTop = false)
{
this.brush = brush;
this.leftPoint = left;
this.rightPoint = right;
this.strokeThickness = 4;
InitElementPoint(isDotted, isTop);
InvalidateVisual(); // 触发重绘
}
public void InitElementPoint(bool isDotted, bool isTop = false)
{
//hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
//hitVisiblePen.Freeze(); // Freeze以提高性能
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
opacity = 1.0d;
//var dashStyle = new DashStyle();
if (isDotted)
{
opacity = 0.42d;
visualPen.DashStyle = new DashStyle(); // DashStyles.Dash; // 选择虚线样式
}
//visualPen.Freeze(); // Freeze以提高性能
linkSize = 4; // 整线条粗细
int zIndex = -999999;
this.ZIndex = zIndex;
//Panel.SetZIndex(this, zIndex); // 置底
}
/// <summary>
/// 更新线条落点位置
/// </summary>
/// <param name="left"></param>
/// <param name="right"></param>
public void UpdatePoint(Point left, Point right, Brush? brush = null)
{
if(brush is not null)
{
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
}
this.leftPoint = left;
this.rightPoint = right;
InvalidateVisual(); // 触发重绘
}
/// <summary>
/// 更新线条落点位置
/// </summary>
/// <param name="right"></param>
public void UpdateRightPoint(Point right, Brush? brush = null)
{
if (brush is not null)
{
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
}
this.rightPoint = right;
InvalidateVisual(); // 触发重绘
}
/// <summary>
/// 更新线条起点位置
/// </summary>
/// <param name="left"></param>
public void UpdateLeftPoints(Point left, Brush? brush = null)
{
if (brush is not null)
{
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
}
this.leftPoint = left;
InvalidateVisual(); // 触发重绘
}
/// <summary>
/// 刷新颜色
/// </summary>
/// <param name="brush"></param>
public void UpdateColor(Brush brush )
{
visualPen = new Pen(brush, 3.0); // 默认可视化Pen
InvalidateVisual(); // 触发重绘
}
/// <summary>
/// 控件重绘事件
/// </summary>
/// <param name="drawingContext"></param>
public override void Render(DrawingContext drawingContext)
{
// 刷新线条显示位置
DrawBezierCurve(drawingContext, leftPoint, rightPoint);
}
#region
private StreamGeometry streamGeometry = new StreamGeometry();
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
//private Pen hitVisiblePen; // 初始化碰撞检测线
private Pen visualPen; // 默认可视化Pen
private Point leftPoint; // 连接线的起始节点
private Point rightPoint; // 连接线的终点
private Brush brush; // 线条颜色
private double opacity; // 透明度
double linkSize; // 根据缩放比例调整线条粗细
//public void UpdateLineColor()
//{
// visualPen = new Pen(brush, 3.0); // 默认可视化Pen
// InvalidateVisual(); // 触发重绘
//}
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
private Vector axis = new Vector(1, 0);
private Vector startToEnd;
private int i = 0;
private void DrawBezierCurve(DrawingContext drawingContext,
Point left,
Point right)
{
// 控制点的计算逻辑
double power = 140; // 控制贝塞尔曲线的“拉伸”强度
drawingContext.PushOpacity(opacity);
// 计算轴向向量与起点到终点的向量
//var axis = new Vector(1, 0);
startToEnd = (right.ToVector() - left.ToVector()).NormalizeTo();
//var dp = axis.DotProduct(startToEnd);
//dp = dp < 50 ? 50 : dp;
//var pow = Math.Max(0, dp) ;
//var k = 1 - Math.Pow(pow, 10.0);
//
//Debug.WriteLine("pow : " + pow);
//Debug.WriteLine("k : " + k);
//Debug.WriteLine("");
// 计算拉伸程度k拉伸与水平夹角正相关
var dp = axis.DotProduct(startToEnd) ;
var pow = Math.Pow(dp, 10.0);
pow = pow > 0 ? 0 : pow;
var k = 1 - pow;
// 如果起点x大于终点x增加额外的偏移量避免重叠
var bias = left.X > right.X ? Math.Abs(left.X - right.X) * 0.25 : 0;
// 控制点的实际计算
c0 = new Point(+(power + bias) * k + left.X, left.Y);
c1 = new Point(-(power + bias) * k + right.X, right.Y);
// 准备StreamGeometry以用于绘制曲线
// why can't clearValue()?
//streamGeometry.ClearValue(ThemeProperty);
//var streamGeometry = new StreamGeometry();
//if( i++ > 100 && streamGeometry is AvaloniaObject avaloniaObject)
//{
// var platformImplInfo = streamGeometry.GetType().GetProperty("PlatformImpl", BindingFlags.NonPublic | BindingFlags.Instance);
// var platformImpl = platformImplInfo?.GetValue(streamGeometry);
// var pathCache = platformImpl?.GetType().GetField("_bounds", BindingFlags.NonPublic | BindingFlags.Instance);
// if(pathCache is IDisposable disposable)
// {
// disposable.Dispose();
// }
// //pathCache?.Invoke(platformImpl, []);
// Debug.WriteLine("invoke => InvalidateCaches");
// i = 0;
// //public class AvaloniaObject : IAvaloniaObjectDebug, INotifyPropertyChanged
// //private readonly ValueStore _values;
//}
// this is override "Render()" method
// display a bezier-line on canvas and follow the mouse in real time
// I don't want to re-instantiate StreamGeometry()
// because I want to reduce the number of GC
// but , it seems impossible to do so in avalonia
streamGeometry = new StreamGeometry();
// in wpf , this method allows display content to be cleared
// streamGeometry.Clear(); // this is wpf method
// in avalonia, why does this method need value of "AvaloniaProperty" data type ?
// but I don't know use what "AvaloniaProperty" to clear the displayed content
// if I don't clear the cache or re-instantiate it
// the canvas will display repeated lines , because exits cache inside streamGeometry
// streamGeometry.ClearValue("AvaloniaProperty");
using (var context = streamGeometry.Open())
{
context.BeginFigure(left, true); // start point of the bezier-line
context.CubicBezierTo(c0, c1, right, true); // drawing bezier-line
}
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
}
#endregion
}
}

View File

@@ -1,56 +0,0 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
xmlns:cv="using:Serein.Workbench.Avalonia.Custom.Views"
xmlns:converter="using:Serein.Workbench.Avalonia.Converters"
xmlns:dtp="using:Serein.Workbench.Avalonia.DataTemplates"
>
<!--预览-->
<Design.PreviewWith>
<StackPanel Width="400" Spacing="10">
<StackPanel Background="{DynamicResource SystemRegionBrush}">
<cv:FlowLibraryInfoView />
</StackPanel>
</StackPanel>
</Design.PreviewWith>
<ControlTheme x:Key="{x:Type cv:FlowLibraryInfoView}" TargetType="cv:FlowLibraryInfoView">
<Setter Property="Template">
<ControlTemplate>
<StackPanel>
<!--类库名称-->
<StackPanel HorizontalAlignment="Left" Orientation="Horizontal" Margin="2">
<TextBlock Text="library : " FontSize="18"></TextBlock>
<TextBlock Text="{TemplateBinding LibraryName}" FontSize="18"></TextBlock>
</StackPanel>
<!--Action Method Info-->
<!--动作节点方法信息-->
<ListBox x:Name="PART_ActionMethodInfos" ItemsSource="{TemplateBinding ActionMethods}" Background="#F1FBFB">
<ItemsControl.ItemTemplate>
<!--use custom DataTemplate create items -->
<!--使用自定义模板创建子项控件-->
<dtp:LibraryMethodInfoDataTemplate>
</dtp:LibraryMethodInfoDataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
<!--Flipflop Method Info-->
<!--触发器节点方法信息-->
<ListBox x:Name="PART_FlipflopMethodInfos" ItemsSource="{TemplateBinding FlipflopMethods}" Background="#FBF8F1">
<ItemsControl.ItemTemplate>
<dtp:LibraryMethodInfoDataTemplate>
</dtp:LibraryMethodInfoDataTemplate>
</ItemsControl.ItemTemplate>
</ListBox>
</StackPanel>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>

View File

@@ -1,236 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Presenters;
using Avalonia.Controls.Primitives;
using Avalonia.Controls.Templates;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.LogicalTree;
using Avalonia.VisualTree;
using Serein.Library;
using Serein.Workbench.Avalonia.Services;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
namespace Serein.Workbench.Avalonia.Custom.Views;
public partial class FlowLibraryInfoView : TemplatedControl
{
private IWorkbenchEventService workbenchEventService;
public FlowLibraryInfoView()
{
workbenchEventService = App.GetService<IWorkbenchEventService>();
}
protected override void OnPropertyChanged(AvaloniaPropertyChangedEventArgs change)
{
base.OnPropertyChanged(change);
// 如果改变的属性是 MdsProperty ,则加载方法信息
if (change.Property == MdsProperty)
{
if(change.NewValue is MethodDetailsInfo[] value)
{
onNext(value);
}
}
}
/// <summary>
/// 获取到控件信息
/// </summary>
/// <param name="e"></param>
protected override void OnApplyTemplate(TemplateAppliedEventArgs e)
{
base.OnApplyTemplate(e);
#region
if (e.NameScope.Find("PART_ActionMethodInfos") is ListBox p_am)
{
p_am.AddHandler(InputElement.PointerExitedEvent, ListBox_PointerExited);
p_am.AddHandler(SelectingItemsControl.SelectionChangedEvent, ListBox_SelectionChanged);
}
#endregion
#region
if (e.NameScope.Find("PART_FlipflopMethodInfos") is ListBox p_fm)
{
p_fm.SelectionChanged += ListBox_SelectionChanged;
p_fm.PointerExited += ListBox_PointerExited;
}
#endregion
}
private void ListBox_SelectionChanged(object? o, SelectionChangedEventArgs e)
{
if (o is ListBox listBox && listBox.SelectedIndex > 0 && listBox.SelectedItem is MethodDetailsInfo mdInfo)
{
workbenchEventService.PreviewLibraryMethodInfo(mdInfo); // 通知其它地方预览了某个方法信息
}
}
private void ListBox_PointerExited(object? o, PointerEventArgs e)
{
if (o is ListBox listBox && listBox.SelectedIndex > -1)
{
listBox.SelectedIndex = -1; // 如果鼠标离开了,取消已选状态
}
}
/// <summary>
/// 将信息加载出来
/// </summary>
/// <param name="value"></param>
private void onNext(MethodDetailsInfo[] value)
{
if(value is null)
{
return;
}
var fmd = value.Where(item => nameof(NodeType.Flipflop).Equals(item.NodeType));
FlipflopMethods = new ObservableCollection<MethodDetailsInfo>(fmd);
var amd = value.Where(item => nameof(NodeType.Action).Equals(item.NodeType));
ActionMethods = new ObservableCollection<MethodDetailsInfo>(amd);
}
#region Template Public Property /
public static readonly DirectProperty<FlowLibraryInfoView, string> LibraryNameProperty =
AvaloniaProperty.RegisterDirect<FlowLibraryInfoView, string>(nameof(LibraryName), o => o.LibraryName, (o, v) => o.LibraryName = v);
public static readonly DirectProperty<FlowLibraryInfoView, MethodDetailsInfo[]> MdsProperty =
AvaloniaProperty.RegisterDirect<FlowLibraryInfoView, MethodDetailsInfo[]>(nameof(Mds), o => o.Mds, (o, v) => o.Mds = v);
public static readonly DirectProperty<FlowLibraryInfoView, ObservableCollection<MethodDetailsInfo>> ActionMethodsProperty =
AvaloniaProperty.RegisterDirect<FlowLibraryInfoView, ObservableCollection<MethodDetailsInfo>>(nameof(ActionMethods), o => o.ActionMethods, (o, v) => o.ActionMethods = v);
public static readonly DirectProperty<FlowLibraryInfoView, ObservableCollection<MethodDetailsInfo>> FlipflopMethodsProperty =
AvaloniaProperty.RegisterDirect<FlowLibraryInfoView, ObservableCollection<MethodDetailsInfo>>(nameof(FlipflopMethods), o => o.FlipflopMethods, (o, v) => o.FlipflopMethods = v);
private string libraryName = string.Empty;
private ObservableCollection<MethodDetailsInfo> actionMethods;
private ObservableCollection<MethodDetailsInfo> flipflopMethods;
private MethodDetailsInfo[] mds = [];
public string LibraryName
{
get { return libraryName; }
set { SetAndRaise(LibraryNameProperty, ref libraryName, value); }
}
/*
public static readonly AttachedProperty<string> LibraryName2Property = AvaloniaProperty.RegisterAttached<FlowLibraryInfoView, Control, string>("LibraryName2");
public static string GetLibraryName2(Control element)
{
return element.GetValue(LibraryName2Property);
}
public static void SetLibraryName2(Control element, string value)
{
element.SetValue(LibraryName2Property, value);
}
*/
/// <summary>
/// Method Info
/// 方法信息
/// </summary>
public MethodDetailsInfo[] Mds
{
get { return mds; }
set
{
SetAndRaise(MdsProperty, ref mds, value);
}
}
/// <summary>
/// 动作节点的方法
/// </summary>
public ObservableCollection<MethodDetailsInfo> ActionMethods
{
get { return actionMethods; }
set
{
SetAndRaise(ActionMethodsProperty, ref actionMethods, value);
}
}
/// <summary>
/// 触发器节点的方法
/// </summary>
public ObservableCollection<MethodDetailsInfo> FlipflopMethods
{
get { return flipflopMethods; }
set
{
SetAndRaise(FlipflopMethodsProperty, ref flipflopMethods, value);
}
}
#endregion
}
public class ItemsChangeObservableCollection<T> : ObservableCollection<T> where T : INotifyPropertyChanged
{
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
RegisterPropertyChanged(e.NewItems);
}
else if (e.Action == NotifyCollectionChangedAction.Remove)
{
UnRegisterPropertyChanged(e.OldItems);
}
else if (e.Action == NotifyCollectionChangedAction.Replace)
{
UnRegisterPropertyChanged(e.OldItems);
RegisterPropertyChanged(e.NewItems);
}
base.OnCollectionChanged(e);
}
protected override void ClearItems()
{
UnRegisterPropertyChanged(this);
base.ClearItems();
}
private void RegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged += new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void UnRegisterPropertyChanged(IList items)
{
foreach (INotifyPropertyChanged item in items)
{
if (item != null)
{
item.PropertyChanged -= new PropertyChangedEventHandler(item_PropertyChanged);
}
}
}
private void item_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
//launch an event Reset with name of property changed
base.OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}

View File

@@ -1,40 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Serein.Workbench.Avalonia.Custom.Views.FlowLibraryMethodInfoView"
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
xmlns:cv="clr-namespace:Serein.Workbench.Avalonia.Custom.Views"
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
x:DataType="vm:FlowLibraryMethodInfoViewModel">
<Design.DataContext>
<vm:FlowLibraryMethodInfoViewModel />
</Design.DataContext>
<UserControl.Styles>
<Style Selector="StackPanel">
<Setter Property="Margin" Value="2,1,2,1" />
</Style>
</UserControl.Styles>
<StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="节点类型 - "/>
<TextBlock Text="{Binding MethodDetailsInfo.NodeType}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="方法描述 - "/>
<TextBlock Text="{Binding MethodDetailsInfo.MethodAnotherName}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text="方法名称 - "/>
<TextBlock Text="{Binding MethodDetailsInfo.MethodName}"/>
</StackPanel>
<StackPanel Orientation="Horizontal">
<TextBlock Text=" 返 回 值 - "/>
<TextBlock Text="{Binding MethodDetailsInfo.ReturnTypeFullName}"/>
</StackPanel>
</StackPanel>
</UserControl>

View File

@@ -1,50 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Serein.Library;
using Serein.Workbench.Avalonia.Custom.ViewModels;
using Serein.Workbench.Avalonia.Custom.Views;
using Serein.Workbench.Avalonia.Services;
using System.Diagnostics;
namespace Serein.Workbench.Avalonia.Custom.Views;
public partial class FlowLibraryMethodInfoView : UserControl
{
private FlowLibraryMethodInfoViewModel _vm;
public FlowLibraryMethodInfoView()
{
InitializeComponent();
_vm = App.GetService<FlowLibraryMethodInfoViewModel>();
DataContext = _vm;
//this.PointerPressed += FlowLibraryMethodInfoView_PointerPressed;
}
//private async void FlowLibraryMethodInfoView_PointerPressed(object? sender, PointerPressedEventArgs e)
//{
// if (_vm.MethodDetailsInfo is null)
// {
// return;
// }
// DataObject dragData = new DataObject();
// dragData.Set(DataFormats.Text, $"{_vm.MethodDetailsInfo.MethodAnotherName}");
// var result = await DragDrop.DoDragDrop(e, dragData, DragDropEffects.Copy);
// Debug.WriteLine("DoDrag :" + result);
// switch (result)
// {
// case DragDropEffects.Copy:
// Debug.WriteLine("文本来自 Copy");
// break;
// case DragDropEffects.Link:
// Debug.WriteLine("文本来自 Link");
// break;
// case DragDropEffects.None:
// Debug.WriteLine("拖拽操作被取消");
// break;
// }
//}
}

View File

@@ -1,39 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Serein.Workbench.Avalonia.Custom.Views.FlowLibrarysView"
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
xmlns:cv="clr-namespace:Serein.Workbench.Avalonia.Custom.Views"
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
x:DataType="vm:FlowLibrarysViewModel"
>
<Design.DataContext>
<vm:FlowLibrarysViewModel />
</Design.DataContext>
<UserControl.Resources>
<cv:FlowLibraryInfoView x:Key="FlowLibraryInfoView">
</cv:FlowLibraryInfoView>
</UserControl.Resources>
<!-- , DataType={x:Type vm:FlowLibrarysViewModel} -->
<!-- x:DataType="baselibrary:LibraryMds" -->
<!-- LibraryInfo="{Binding}"-->
<ScrollViewer HorizontalAlignment="Left" VerticalAlignment="Top">
<!--Displays dependecy information loaded from runtime environment-->
<!--显示从运行环境加载的所有依赖信息-->
<ItemsControl ItemsSource="{Binding LibraryList}">
<ItemsControl.ItemTemplate>
<DataTemplate x:DataType="baselibrary:LibraryMds" >
<cv:FlowLibraryInfoView LibraryName="{Binding AssemblyName}" Mds="{Binding Mds}"/>
<!--<StackPanel Background="{DynamicResource SystemRegionBrush}" Margin="2,2,2,8">
</StackPanel>-->
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</ScrollViewer>
</UserControl>

View File

@@ -1,17 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Serein.Library;
using Serein.Workbench.Avalonia.Custom.ViewModels;
using System;
namespace Serein.Workbench.Avalonia.Custom.Views;
public partial class FlowLibrarysView : UserControl
{
public FlowLibrarysView()
{
InitializeComponent();
DataContext = App.GetService<FlowLibrarysViewModel>();
}
}

View File

@@ -1,43 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Serein.Workbench.Avalonia.Custom.Views.MainMenuBarView"
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
x:DataType="vm:MainMenuBarViewModel">
<UserControl.Styles>
<Style Selector="MenuItem">
<Setter Property="FontSize" Value="20" />
</Style>
</UserControl.Styles>
<Design.DataContext>
<vm:MainMenuBarViewModel />
</Design.DataContext>
<StackPanel HorizontalAlignment="Center" >
<StackPanel.Resources>
<SolidColorBrush x:Key="MenuFlyoutBackground">#FFFFFF</SolidColorBrush>
</StackPanel.Resources>
<Menu Background="Transparent">
<MenuItem Header="项目">
<MenuItem Header="保存项目" Command="{Binding SaveProjectCommand}"/>
<MenuItem Header="打开本地项目"/>
</MenuItem>
<MenuItem Header="调试">
<MenuItem Header="运行(从起始节点)"/>
<MenuItem Header="运行(从当前节点)"/>
<MenuItem Header="结束流程"/>
</MenuItem>
<MenuItem Header="视图">
<MenuItem Header="输出窗口"/>
<MenuItem Header="重置画布"/>
<MenuItem Header="定位节点"/>
</MenuItem>
<MenuItem Header="远程">
<MenuItem Header="启动远程服务" />
<MenuItem Header="连接远程环境" />
</MenuItem>
</Menu>
</StackPanel>
</UserControl>

View File

@@ -1,16 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Serein.Workbench.Avalonia.Custom.ViewModels;
using System;
namespace Serein.Workbench.Avalonia.Custom.Views;
public partial class MainMenuBarView : UserControl
{
public MainMenuBarView()
{
InitializeComponent();
DataContext = App.GetService<MainMenuBarViewModel>();
}
}

View File

@@ -1,142 +0,0 @@
using static System.Math;
namespace Avalonia.Controls.PanAndZoom;
/// <summary>
/// Avalonia Matrix helper methods.
/// </summary>
public static class MatrixHelper
{
/// <summary>
/// Creates a translation matrix using the specified offsets.
/// </summary>
/// <param name="offsetX">X-coordinate offset.</param>
/// <param name="offsetY">Y-coordinate offset.</param>
/// <returns>The created translation matrix.</returns>
public static Matrix Translate(double offsetX, double offsetY)
{
return new Matrix(1.0, 0.0, 0.0, 1.0, offsetX, offsetY);
}
/// <summary>
/// Prepends a translation around the center of provided matrix.
/// </summary>
/// <param name="matrix">The matrix to prepend translation.</param>
/// <param name="offsetX">X-coordinate offset.</param>
/// <param name="offsetY">Y-coordinate offset.</param>
/// <returns>The created translation matrix.</returns>
public static Matrix TranslatePrepend(Matrix matrix, double offsetX, double offsetY)
{
return Translate(offsetX, offsetY) * matrix;
}
/// <summary>
/// Creates a matrix that scales along the x-axis and y-axis.
/// </summary>
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
/// <returns>The created scaling matrix.</returns>
public static Matrix Scale(double scaleX, double scaleY)
{
return new Matrix(scaleX, 0, 0, scaleY, 0.0, 0.0);
}
/// <summary>
/// Creates a matrix that is scaling from a specified center.
/// </summary>
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
/// <param name="centerX">The center X-coordinate of the scaling.</param>
/// <param name="centerY">The center Y-coordinate of the scaling.</param>
/// <returns>The created scaling matrix.</returns>
public static Matrix ScaleAt(double scaleX, double scaleY, double centerX, double centerY)
{
return new Matrix(scaleX, 0, 0, scaleY, centerX - (scaleX * centerX), centerY - (scaleY * centerY));
}
/// <summary>
/// Prepends a scale around the center of provided matrix.
/// </summary>
/// <param name="matrix">The matrix to prepend scale.</param>
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
/// <param name="centerX">The center X-coordinate of the scaling.</param>
/// <param name="centerY">The center Y-coordinate of the scaling.</param>
/// <returns>The created scaling matrix.</returns>
public static Matrix ScaleAtPrepend(Matrix matrix, double scaleX, double scaleY, double centerX, double centerY)
{
return ScaleAt(scaleX, scaleY, centerX, centerY) * matrix;
}
/// <summary>
/// Creates a translation and scale matrix using the specified offsets and scales along the x-axis and y-axis.
/// </summary>
/// <param name="scaleX">Scaling factor that is applied along the x-axis.</param>
/// <param name="scaleY">Scaling factor that is applied along the y-axis.</param>
/// <param name="offsetX">X-coordinate offset.</param>
/// <param name="offsetY">Y-coordinate offset.</param>
/// <returns>The created translation and scale matrix.</returns>
public static Matrix ScaleAndTranslate(double scaleX, double scaleY, double offsetX, double offsetY)
{
return new Matrix(scaleX, 0.0, 0.0, scaleY, offsetX, offsetY);
}
/// <summary>
/// Creates a skew matrix.
/// </summary>
/// <param name="angleX">Angle of skew along the X-axis in radians.</param>
/// <param name="angleY">Angle of skew along the Y-axis in radians.</param>
/// <returns>When the method completes, contains the created skew matrix.</returns>
public static Matrix Skew(float angleX, float angleY)
{
return new Matrix(1.0, Tan(angleX), Tan(angleY), 1.0, 0.0, 0.0);
}
/// <summary>
/// Creates a matrix that rotates.
/// </summary>
/// <param name="radians">Angle of rotation in radians. Angles are measured clockwise when looking along the rotation axis.</param>
/// <returns>The created rotation matrix.</returns>
public static Matrix Rotation(double radians)
{
double cos = Cos(radians);
double sin = Sin(radians);
return new Matrix(cos, sin, -sin, cos, 0, 0);
}
/// <summary>
/// Creates a matrix that rotates about a specified center.
/// </summary>
/// <param name="angle">Angle of rotation in radians.</param>
/// <param name="centerX">The center X-coordinate of the rotation.</param>
/// <param name="centerY">The center Y-coordinate of the rotation.</param>
/// <returns>The created rotation matrix.</returns>
public static Matrix Rotation(double angle, double centerX, double centerY)
{
return Translate(-centerX, -centerY) * Rotation(angle) * Translate(centerX, centerY);
}
/// <summary>
/// Creates a matrix that rotates about a specified center.
/// </summary>
/// <param name="angle">Angle of rotation in radians.</param>
/// <param name="center">The center of the rotation.</param>
/// <returns>The created rotation matrix.</returns>
public static Matrix Rotation(double angle, Vector center)
{
return Translate(-center.X, -center.Y) * Rotation(angle) * Translate(center.X, center.Y);
}
/// <summary>
/// Transforms a point by this matrix.
/// </summary>
/// <param name="matrix">The matrix to use as a transformation matrix.</param>
/// <param name="point">>The original point to apply the transformation.</param>
/// <returns>The result of the transformation for the input point.</returns>
public static Point TransformPoint(Matrix matrix, Point point)
{
return new Point(
(point.X * matrix.M11) + (point.Y * matrix.M21) + matrix.M31,
(point.X * matrix.M12) + (point.Y * matrix.M22) + matrix.M32);
}
}

View File

@@ -1,22 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Serein.Workbench.Avalonia.Custom.Views.NodeContainerView"
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
x:DataType="vm:NodeContainerViewModel"
Background="#F8FBF1"
DragDrop.AllowDrop="True">
<Design.DataContext>
<vm:NodeContainerViewModel />
</Design.DataContext>
<DockPanel>
<!--放置节点-->
<Canvas x:Name="PART_NodeContainer">
</Canvas>
</DockPanel>
</UserControl>

View File

@@ -1,432 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.PanAndZoom;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Media;
using Avalonia.VisualTree;
using Newtonsoft.Json.Linq;
using Serein.Library;
using Serein.Library.Utils;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Custom.Node.Views;
using Serein.Workbench.Avalonia.Custom.ViewModels;
using Serein.Workbench.Avalonia.Extension;
using Serein.Workbench.Avalonia.Services;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using Point = Avalonia.Point;
namespace Serein.Workbench.Avalonia.Custom.Views;
public partial class NodeContainerView : UserControl
{
private readonly NodeContainerViewModel _vm;
private readonly INodeOperationService nodeOperationService;
private readonly IKeyEventService keyEventService;
#region
/// <summary>
/// 是否正在预览节点控件
/// </summary>
private bool IsPreviewNodeControl;
/// <summary>
/// 标记是否正在尝试选取控件
/// </summary>
private bool IsSelectControl;
/// <summary>
/// 标记是否正在拖动控件
/// </summary>
private bool IsControlDragging;
/// <summary>
/// 标记是否正在拖动画布
/// </summary>
private bool IsCanvasDragging;
/// <summary>
/// 标记是否正在选取节点
/// </summary>
private bool IsSelectDragging;
/// <summary>
/// 当前选取的控件
/// </summary>
private readonly List<NodeControlBase> selectNodeControls = [];
/// <summary>
/// 记录开始拖动节点控件时的鼠标位置
/// </summary>
private Point startControlDragPoint;
/// <summary>
/// 记录移动画布开始时的鼠标位置
/// </summary>
private Point startCanvasDragPoint;
/// <summary>
/// 记录开始选取节点控件时的鼠标位置
/// </summary>
private Point startSelectControolPoint;
/// <summary>
/// 组合变换容器
/// </summary>
private readonly TransformGroup canvasTransformGroup;
/// <summary>
/// 缩放画布
/// </summary>
private readonly ScaleTransform scaleTransform;
/// <summary>
/// 平移画布
/// </summary>
private readonly TranslateTransform translateTransform;
#endregion
public NodeContainerView()
{
InitializeComponent();
_vm= App.GetService<NodeContainerViewModel>();
DataContext = _vm;
#region UI相关的服务
keyEventService = App.GetService<IKeyEventService>();
nodeOperationService = App.GetService<INodeOperationService>();
nodeOperationService.MainCanvas = PART_NodeContainer;
nodeOperationService.OnNodeViewCreate += NodeOperationService_OnNodeViewCreate; // 处理事件
keyEventService.KeyUp += (k) =>
{
if (k == Key.Escape)
{
IsCanvasDragging = false;
IsControlDragging = false;
nodeOperationService.ConnectingManage.Reset();
}
};
#endregion
#region UI事件
AddHandler(DragDrop.DropEvent, Drop); // 创建节点相关
PointerPressed += NodeContainerView_PointerPressed;
PointerReleased += NodeContainerView_PointerReleased;
PointerMoved += NodeContainerView_PointerMoved;
PointerWheelChanged += NodeContainerView_PointerWheelChanged;
#endregion
#region
canvasTransformGroup = new TransformGroup();
scaleTransform = new ScaleTransform();
translateTransform = new TranslateTransform();
canvasTransformGroup.Children.Add(scaleTransform);
canvasTransformGroup.Children.Add(translateTransform);
PART_NodeContainer.RenderTransform = canvasTransformGroup;
#endregion
}
#region
public Point GetPositionOfCanvas(PointerEventArgs e)
{
return e.GetPosition(PART_NodeContainer);
}
public Point GetPositionOfCanvas(DragEventArgs e)
{
return e.GetPosition(PART_NodeContainer);
}
#endregion
#region
#region
private void Drop(object? sender, DragEventArgs e)
{
if (e.Data.Contains(DataFormats.Text))
{
var json = e.Data.GetText();
if (string.IsNullOrEmpty(json))
{
return;
}
var mdInfo = json.ToJsonObject<MethodDetailsInfo>();
if (mdInfo is not null)
{
var canvasDropPosition = GetPositionOfCanvas(e); // 更新画布落点
PositionOfUI position = new PositionOfUI(canvasDropPosition.X, canvasDropPosition.Y);
nodeOperationService.CreateNodeView(mdInfo, position); // 提交创建节点的请求
}
}
else // if (e.Data.Contains(DataFormats.FileNames))
{
var files = e.Data.GetFiles();
var str = files?.Select(f => f.Path);
if (str is not null)
{
}
}
}
#endregion
#region
private void NodeContainerView_PointerPressed(object? sender, PointerPressedEventArgs e)
{
if (IsPreviewNodeControl)
{
IsCanvasDragging = false;
e.Handled = true;
return;
}
if (!IsCanvasDragging)
{
IsCanvasDragging = true;
startCanvasDragPoint = e.GetPosition(this);
e.Handled = true;
}
}
private void NodeContainerView_PointerReleased(object? sender, PointerReleasedEventArgs e)
{
IsCanvasDragging = false; // 不再拖动
}
private void NodeContainerView_PointerMoved(object? sender, PointerEventArgs e)
{
// 是否正在连接
var myData = nodeOperationService.ConnectingManage;
if (myData.IsCreateing)
{
var isPass = e.JudgePointer(sender, PointerType.Mouse, p => p.IsLeftButtonPressed);
if (isPass)
{
if (myData.Type == JunctionOfConnectionType.Invoke)
{
_vm.IsConnectionInvokeNode = true; // 正在连接节点的调用关系
}
else
{
_vm.IsConnectionArgSourceNode = true; // 正在连接节点的调用关系
}
var currentPoint = e.GetPosition(PART_NodeContainer);
//myData.CurrentJunction?.InvalidateVisual();
myData.UpdatePoint(new Point(currentPoint.X - 5, currentPoint.Y - 5));
e.Handled = true;
return;
}
}
if (IsCanvasDragging)
{
// 拖动画布
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;
}
}
// 缩放
private void NodeContainerView_PointerWheelChanged(object? sender, PointerWheelEventArgs e)
{
var delta = e.Delta.Y;
if (delta < 0 && scaleTransform.ScaleX < 0.02) return;
if (delta > 0 && scaleTransform.ScaleY > 4.0) return;
// 缩放因子,根据滚轮方向调整
double zoomFactor = delta > 0 ? 1.23 : 0.78;
// 当前缩放比例
double oldScale = scaleTransform.ScaleX;
double newScale = oldScale * zoomFactor;
// 记录缩放前的鼠标位置
var mousePosition = GetPositionOfCanvas(e);
// 更新缩放比例
scaleTransform.ScaleX = newScale;
scaleTransform.ScaleY = newScale;
// 记录缩放后的鼠标位置
var newMousePosition = GetPositionOfCanvas(e);
// 更新 TranslateTransform确保以鼠标位置为中心进行缩放
var s_position = newMousePosition - mousePosition; // 计算偏移量
translateTransform.X += s_position.X * newScale; // 根据缩放比例进行偏移
translateTransform.Y += s_position.Y * newScale; // 根据缩放比例进行偏移
}
#endregion
#endregion
#region
/// <summary>
/// 拖拽创建控件
/// </summary>
/// <param name="eventArgs"></param>
/// <returns></returns>
private bool NodeOperationService_OnNodeViewCreate(NodeViewCreateEventArgs eventArgs)
{
if (eventArgs.NodeControl is not Control control)
{
return false;
}
var position = eventArgs.Position;// 坐标
SetNodeEvent(control); // 设置该控件与画布交互的相关事件
DragControl(control, position.X, position.Y);
PART_NodeContainer.Children.Add(control);
return true;
}
/// <summary>
/// 设置节点与画布容器相关的操作事件
/// </summary>
/// <param name="nodeControl"></param>
private void SetNodeEvent(Control nodeControl)
{
nodeControl.PointerMoved += NodeControl_PointerMoved; ;
nodeControl.PointerExited += NodeControl_PointerExited;
nodeControl.PointerPressed += Block_MouseLeftButtonDown;
nodeControl.PointerMoved += Block_MouseMove;
nodeControl.PointerReleased += (s, e) => IsControlDragging = false;
}
#endregion
#region
/// <summary>
/// 移动控件
/// </summary>
/// <param name="nodeControl"></param>
/// <param name="x"></param>
/// <param name="y"></param>
private void DragControl(Control nodeControl, double x, double y)
{
Canvas.SetLeft(nodeControl, x);
Canvas.SetTop(nodeControl, y);
}
/// <summary>
/// 控件的鼠标右键按下事件,启动拖动操作。
/// </summary>
private void Block_MouseLeftButtonDown(object? sender, PointerPressedEventArgs e)
{
var isPass = e.JudgePointer(sender, PointerType.Mouse, p => p.IsRightButtonPressed);
if (!isPass)
{
return;
}
if (sender is NodeControlBase nodeControl)
{
IsControlDragging = true;
startControlDragPoint = GetPositionOfCanvas(e); // 记录鼠标按下时的位置
e.Handled = true; // 防止事件传播影响其他控件
}
}
/// <summary>
/// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。
/// </summary>
private void Block_MouseMove(object? sender, PointerEventArgs e)
{
if (sender is not NodeControlBase nodeControl)
{
return;
}
if (IsCanvasDragging)
return;
if (IsSelectControl)
return;
if (IsControlDragging) // 如果正在拖动控件
{
Point currentPosition = GetPositionOfCanvas(e); // 获取当前鼠标位置
// 单个移动
if (selectNodeControls.Count == 0 || !selectNodeControls.Contains(nodeControl))
{
double deltaX = currentPosition.X - startControlDragPoint.X; // 计算X轴方向的偏移量
double deltaY = currentPosition.Y - startControlDragPoint.Y; // 计算Y轴方向的偏移量
double newLeft = Canvas.GetLeft(nodeControl) + deltaX; // 新的左边距
double newTop = Canvas.GetTop(nodeControl) + deltaY; // 新的上边距
DragControl(nodeControl, newLeft, newTop);
nodeControl.UpdateLocationConnections();
}
// 批量移动
else
{
// 进行批量移动
// 获取旧位置
var oldLeft = Canvas.GetLeft(nodeControl);
var oldTop = Canvas.GetTop(nodeControl);
// 计算被选择控件的偏移量
var deltaX = /*(int)*/(currentPosition.X - startControlDragPoint.X);
var deltaY = /*(int)*/(currentPosition.Y - startControlDragPoint.Y);
// 移动被选择的控件
var newLeft = oldLeft + deltaX;
var newTop = oldTop + deltaY;
//this.EnvDecorator.MoveNode(nodeControlMain.ViewModel.NodeModel.Guid, newLeft, newTop); // 移动节点
DragControl(nodeControl, newLeft, newTop);
// 计算控件实际移动的距离
var actualDeltaX = newLeft - oldLeft;
var actualDeltaY = newTop - oldTop;
// 移动其它选中的控件
foreach (var selectItemNode in selectNodeControls)
{
if (selectItemNode != nodeControl) // 跳过已经移动的控件
{
var otherNewLeft = Canvas.GetLeft(selectItemNode) + actualDeltaX;
var otherNewTop = Canvas.GetTop(selectItemNode) + actualDeltaY;
DragControl(selectItemNode, otherNewLeft, otherNewTop);
//this.EnvDecorator.MoveNode(nodeControl.ViewModel.NodeModel.Guid, otherNewLeft, otherNewTop); // 移动节点
}
}
// 更新节点之间线的连接位置
foreach (var item in selectNodeControls)
{
item.UpdateLocationConnections();
}
}
startControlDragPoint = currentPosition; // 更新起始点位置
}
}
private void NodeControl_PointerExited(object? sender, PointerEventArgs e)
{
IsPreviewNodeControl = false;
}
private void NodeControl_PointerMoved(object? sender, PointerEventArgs e)
{
IsPreviewNodeControl = true;
}
#endregion
}

View File

@@ -1,27 +0,0 @@
<ResourceDictionary xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="using:Serein.Workbench.Avalonia.Custom.Views">
<!--
Additional resources
Using Control Themes:
https://docs.avaloniaui.net/docs/basics/user-interface/styling/control-themes
Using Theme Variants:
https://docs.avaloniaui.net/docs/guides/styles-and-resources/how-to-use-theme-variants
-->
<Design.PreviewWith>
<StackPanel Width="400" Spacing="10">
<StackPanel Background="{DynamicResource SystemRegionBrush}">
<controls:NodeJunctionView />
</StackPanel>
</StackPanel>
</Design.PreviewWith>
<ControlTheme x:Key="{x:Type controls:NodeJunctionView}" TargetType="controls:NodeJunctionView">
<Setter Property="Template">
<ControlTemplate>
</ControlTemplate>
</Setter>
</ControlTheme>
</ResourceDictionary>

View File

@@ -1,283 +0,0 @@
using Avalonia;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Media;
using Serein.Library;
using Serein.Library.Api;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Extension;
using System;
using System.Diagnostics;
using Color = Avalonia.Media.Color;
using Point = Avalonia.Point;
namespace Serein.Workbench.Avalonia.Custom.Views;
/// <summary>
/// 连接控制点
/// </summary>
public class NodeJunctionView : TemplatedControl
{
public static readonly DirectProperty<NodeJunctionView, JunctionType> JunctionTypeProperty =
AvaloniaProperty.RegisterDirect<NodeJunctionView, JunctionType>(nameof(JunctionType), o => o.JunctionType, (o, v) => o.JunctionType = v);
private JunctionType junctionType;
public JunctionType JunctionType
{
get { return junctionType; }
set { SetAndRaise(JunctionTypeProperty, ref junctionType, value); }
}
public static readonly DirectProperty<NodeJunctionView, NodeModelBase?> MyNodeProperty =
AvaloniaProperty.RegisterDirect<NodeJunctionView, NodeModelBase?>(nameof(MyNode), o => o.MyNode, (o, v) => o.MyNode = v);
private NodeModelBase? myNode;
public NodeModelBase? MyNode
{
get { return myNode; }
set { SetAndRaise(MyNodeProperty, ref myNode, value); }
}
public static readonly DirectProperty<NodeJunctionView, int> ArgIndexProperty =
AvaloniaProperty.RegisterDirect<NodeJunctionView, int>(nameof(ArgIndex), o => o.ArgIndex, (o, v) => o.ArgIndex = v);
private int argIndex;
public int ArgIndex
{
get { return argIndex; }
set { SetAndRaise(ArgIndexProperty, ref argIndex, value); }
}
private readonly INodeOperationService nodeOperationService;
private readonly IFlowEnvironment flowEnvironment;
/// <summary>
/// Render方法中控制自绘内容
/// </summary>
protected readonly StreamGeometry StreamGeometry = new StreamGeometry();
#region
public NodeJunctionView()
{
nodeOperationService = App.GetService<INodeOperationService>();
flowEnvironment = App.GetService<IFlowEnvironment>();
//this.PointerExited += NodeJunctionView_PointerExited;
this.PointerMoved += NodeJunctionView_PointerMoved;
this.PointerPressed += NodeJunctionView_PointerPressed;
this.PointerReleased += NodeJunctionView_PointerReleased;
}
public bool IsPreviewing { get; set; }
private Guid Guid = Guid.NewGuid();
private void NodeJunctionView_PointerMoved(object? sender, PointerEventArgs e)
{
if (!nodeOperationService.ConnectingManage.IsCreateing)
return;
if (nodeOperationService.MainCanvas is not InputElement inputElement)
return;
var currentPoint = e.GetPosition(nodeOperationService.MainCanvas);
if (inputElement.InputHitTest(currentPoint) is NodeJunctionView junctionView)
{
RefreshDisplay(junctionView);
}
else
{
var oldNj = nodeOperationService.ConnectingManage.CurrentJunction;
if (oldNj is not null)
{
oldNj.IsPreviewing = false;
oldNj.InvalidateVisual();
}
}
}
private void RefreshDisplay(NodeJunctionView junctionView)
{
var oldNj = nodeOperationService.ConnectingManage.CurrentJunction;
if (oldNj is not null )
{
if (junctionView.Equals(oldNj))
{
return;
}
oldNj.IsPreviewing = false;
oldNj.InvalidateVisual();
}
nodeOperationService.ConnectingManage.CurrentJunction = junctionView;
if (!this.Equals(junctionView) && nodeOperationService.ConnectingManage.IsCanConnected())
{
Debug.WriteLine("ok");
nodeOperationService.ConnectingManage.TempLine?.ToEnd(junctionView);
}
junctionView.IsPreviewing = true;
junctionView.InvalidateVisual();
}
/// <summary>
/// 尝试开始创建连接线
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void NodeJunctionView_PointerPressed(object? sender, PointerPressedEventArgs e)
{
nodeOperationService.TryCreateConnectionOnJunction(this); // 尝试开始创建
}
private void NodeJunctionView_PointerReleased(object? sender, PointerReleasedEventArgs e)
{
CheckJunvtion();
nodeOperationService.ConnectingManage.Reset();
}
private async void CheckJunvtion()
{
var myData = nodeOperationService.ConnectingManage;
if(myData.StartJunction is null || myData.CurrentJunction is null)
{
return;
}
if(myData.StartJunction.MyNode is null || myData.CurrentJunction.MyNode is null)
{
return;
}
if (!myData.IsCanConnected())
{
return;
}
var canvas = nodeOperationService.MainCanvas;
#region
if (myData.Type == JunctionOfConnectionType.Invoke)
{
await flowEnvironment.ConnectInvokeNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType,
myData.ConnectionInvokeType);
}
#endregion
#region
else if (myData.Type == JunctionOfConnectionType.Arg)
{
var argIndex = 0;
if (myData.StartJunction.JunctionType == JunctionType.ArgData)
{
argIndex = myData.StartJunction.ArgIndex;
}
else if (myData.CurrentJunction.JunctionType == JunctionType.ArgData)
{
argIndex = myData.CurrentJunction.ArgIndex;
}
await flowEnvironment.ConnectArgSourceNodeAsync(myData.StartJunction.MyNode.Guid, myData.CurrentJunction.MyNode.Guid,
myData.StartJunction.JunctionType,
myData.CurrentJunction.JunctionType,
myData.ConnectionArgSourceType,
argIndex);
}
#endregion
}
#endregion
#region UI视觉
/// <summary>
/// 控件重绘事件
/// </summary>
/// <param name="drawingContext"></param>
public override void Render(DrawingContext drawingContext)
{
double width = 44;
double height = 26;
var background = GetBackgrounp();
var pen = new Pen(Brushes.Transparent, 1);
//var pen = nodeOperationService.ConnectingData.IsCreateing ? new Pen(background, 1) : new Pen(Brushes.Black, 1);
// 输入连接器的背景
var connectorRect = new Rect(0, 0, width, height);
drawingContext.DrawRectangle(Brushes.Transparent, new Pen(), connectorRect);
double circleCenterX = width / 2 ; // 中心 X 坐标
double circleCenterY = height / 2 ; // 中心 Y 坐标
//_myCenterPoint = new Point(circleCenterX - Width / 2, circleCenterY); // 中心坐标
// 定义圆形的大小和位置
var diameterCircle = width - 20;
Rect rect = new(4, 2, diameterCircle / 2, diameterCircle / 2);
var ellipse = new EllipseGeometry(rect);
drawingContext.DrawGeometry(background, pen, ellipse);
// 定义三角形的间距
double triangleCenterX = width / 2 - 2; // 三角形中心 X 坐标
double triangleCenterY = height / 2 -5; // 三角形中心 Y 坐标
// 绘制三角形
var pathGeometry = new StreamGeometry();
using (var context = pathGeometry.Open())
{
int t = 6;
context.BeginFigure(new Point(triangleCenterX, triangleCenterY - t), true);
context.LineTo(new Point(triangleCenterX + 8, triangleCenterY), true);
context.LineTo(new Point(triangleCenterX, triangleCenterY + t), true);
context.LineTo(new Point(triangleCenterX, triangleCenterY - t), true);
}
drawingContext.DrawGeometry(background, pen, pathGeometry);
}
/// <summary>
/// 获取背景颜色
/// </summary>
/// <returns></returns>
protected IBrush GetBackgrounp()
{
var myData = nodeOperationService.ConnectingManage;
if (IsPreviewing == false || !myData.IsCreateing )
{
return new SolidColorBrush(Color.Parse("#76ABEE"));
}
if (!myData.IsCanConnected())
{
return new SolidColorBrush(Color.Parse("#FF0000"));
}
if (myData.Type == JunctionOfConnectionType.Invoke)
{
return myData.ConnectionInvokeType.ToLineColor(); // 调用
}
else
{
return myData.ConnectionArgSourceType.ToLineColor(); // 参数
}
}
#endregion
}

View File

@@ -1,69 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="390" d:DesignHeight="40"
x:Class="Serein.Workbench.Avalonia.Custom.Views.ParameterDetailsInfoView"
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.Custom.ViewModels"
xmlns:cv="clr-namespace:Serein.Workbench.Avalonia.Custom.Views"
xmlns:baselibrary="clr-namespace:Serein.Library;assembly=Serein.Library"
xmlns:converter="using:Serein.Workbench.Avalonia.Converters"
x:DataType="vm:ParameterDetailsViewModel"
VerticalAlignment="Center">
<Design.DataContext>
<vm:ParameterDetailsViewModel/>
</Design.DataContext>
<StackPanel >
<StackPanel.Resources>
<converter:IsVisibleOfParameterConverter x:Key="visibleConverter"/>
</StackPanel.Resources>
<Grid ColumnDefinitions="20,40,90,*" Margin="6,0,10,0">
<!--<ToolTip.Tip>
<StackPanel>
</StackPanel>
</ToolTip.Tip>-->
<!--<ToolTip Background="LightYellow" Foreground="#071042" Content="" />-->
<cv:NodeJunctionView Grid.Column="0" JunctionType="ArgData" ArgIndex="{Binding ParameterDetails.Index}" MyNode="{Binding ParameterDetails.NodeModel}" Width="30" Height="15" Margin="2" HorizontalAlignment="Center" VerticalAlignment="Center" />
<!--指定参数-->
<CheckBox Grid.Column="1" IsChecked="{Binding ParameterDetails.IsExplicitData, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center" >
</CheckBox>
<!--参数名称-->
<TextBlock Grid.Column="2" Text="{Binding ParameterDetails.Name}" FontSize="14"
HorizontalAlignment="Left" VerticalAlignment="Center"
ToolTip.Placement="Bottom" ToolTip.VerticalOffset="6">
<ToolTip.Tip>
<StackPanel>
<TextBlock Text="{Binding ParameterDetails}" FontSize="14" TextTrimming="None" TextWrapping="WrapWithOverflow"/>
</StackPanel>
</ToolTip.Tip>
</TextBlock>
<!--参数内容-->
<TextBlock Grid.Column="3" IsVisible="{Binding IsVisibleA}" FontSize="14" Text=" [ 自动取参 ]"
MinWidth="120" MaxWidth="300" HorizontalAlignment="Left" VerticalAlignment="Center" />
<TextBox Grid.Column="3" IsVisible="{Binding IsVisibleB}" FontSize="14" Text="{Binding ParameterDetails.DataValue, Mode=TwoWay}"
MinWidth="120" MaxWidth="300" HorizontalAlignment="Left" VerticalAlignment="Center" />
<ComboBox Grid.Column="3" IsVisible="{Binding IsVisibleC}"
ItemsSource="{Binding ParameterDetails.Items}"
SelectedValue="{Binding ParameterDetails.DataValue,Mode=OneTime}"
MinWidth="120" MaxWidth="300"
HorizontalAlignment="Left" VerticalAlignment="Center">
<ComboBox.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding}" FontFamily="{Binding}" FontSize="14"/>
</DataTemplate>
</ComboBox.ItemTemplate>
</ComboBox>
</Grid>
</StackPanel>
</UserControl>

View File

@@ -1,31 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Markup.Xaml;
using Serein.Library;
using Serein.Workbench.Avalonia.Custom.ViewModels;
namespace Serein.Workbench.Avalonia.Custom.Views;
internal partial class ParameterDetailsInfoView : UserControl
{
private readonly ParameterDetailsViewModel _vm;
public ParameterDetailsInfoView()
{
InitializeComponent();
var pd = new ParameterDetails();
pd.Name = "param name";
pd.IsParams = true;
pd.DataValue = "data value";
pd.Items = ["A","B","C"];
_vm = new (pd);
DataContext = _vm;
}
public ParameterDetailsInfoView(ParameterDetailsViewModel parameterDetailsViewModel)
{
InitializeComponent();
_vm = parameterDetailsViewModel;
DataContext = _vm;
}
}

View File

@@ -1,81 +0,0 @@
using Avalonia.Controls.Templates;
using Avalonia.Controls;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Serein.Library;
using System.Diagnostics;
using MsBox.Avalonia.Enums;
using MsBox.Avalonia;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia;
using Serein.Library.Utils;
namespace Serein.Workbench.Avalonia.DataTemplates
{
/// <summary>
/// 方法信息模板
/// </summary>
internal class LibraryMethodInfoDataTemplate : IDataTemplate
{
public Control Build(object param)
{
if (param is MethodDetailsInfo mdInfo)
{
var textBlock = new TextBlock() { Text = mdInfo.MethodAnotherName };
textBlock.Margin = new Thickness(2d, -6d, 2d, -6d);
textBlock.FontSize = 12;
textBlock.PointerPressed += TextBlock_PointerPressed;
textBlock.Tag = mdInfo;
return textBlock;
}
else
{
var textBlock = new TextBlock() { Text = $"Binding 类型不为预期的[MethodDetailsInfo],而是[{param?.GetType()}]" };
textBlock.Margin = new Thickness(2d, -6d, 2d, -6d);
textBlock.FontSize = 12;
return textBlock;
}
}
public bool Match(object data)
{
return data is MethodDetailsInfo;
}
private static void TextBlock_PointerPressed(object? sender, PointerPressedEventArgs e)
{
if (sender is not TextBlock textBlock || textBlock.Tag is not MethodDetailsInfo mdInfo)
{
return;
}
var dragData = new DataObject(); // 设置需要传递的数据
dragData.Set(DataFormats.Text, mdInfo.ToJsonText());
_ = DragDrop.DoDragDrop(e, dragData, DragDropEffects.Copy);
//var result = await DragDrop.DoDragDrop(e, dragData, DragDropEffects.Copy);
//Debug.WriteLine("DoDrag :" + result);
//switch (result)
//{
// case DragDropEffects.Copy:
// Debug.WriteLine("文本来自 Copy");
// break;
// case DragDropEffects.Link:
// Debug.WriteLine("文本来自 Link");
// break;
// case DragDropEffects.None:
// Debug.WriteLine("拖拽操作被取消");
// break;
//}
}
}
}

View File

@@ -1,39 +0,0 @@
using Avalonia.Controls;
using Avalonia;
using Avalonia.Controls.Templates;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Serein.Library;
using Serein.Workbench.Avalonia.Custom.Views;
using Serein.Workbench.Avalonia.Custom.ViewModels;
namespace Serein.Workbench.Avalonia.DataTemplates
{
internal class NodeMethodParameterInfoDataTemplate : IDataTemplate
{
public Control Build(object param)
{
if (param is ParameterDetails mdInfo)
{
var viewModel = new ParameterDetailsViewModel(mdInfo);
var view = new ParameterDetailsInfoView(viewModel);
return view;
}
else
{
var textBlock = new TextBlock() { Text = $"Binding 类型不为预期的[MethodDetailsInfo],而是[{param?.GetType()}]" };
textBlock.Margin = new Thickness(1d, -4d, 1d, -4d);
return textBlock;
}
}
public bool Match(object data)
{
return data is ParameterDetails;
}
}
}

View File

@@ -1,55 +0,0 @@
using Avalonia.Media;
using Serein.Library;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Extension
{
/// <summary>
/// 线条颜色
/// </summary>
public static class LineExtension
{
/// <summary>
/// 根据连接类型指定颜色
/// </summary>
/// <param name="currentConnectionType"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static SolidColorBrush ToLineColor(this ConnectionInvokeType currentConnectionType)
{
return currentConnectionType switch
{
ConnectionInvokeType.IsSucceed => new SolidColorBrush(Color.Parse("#04FC10")), // 04FC10 & 027E08
ConnectionInvokeType.IsFail => new SolidColorBrush(Color.Parse("#F18905")),
ConnectionInvokeType.IsError => new SolidColorBrush(Color.Parse("#FE1343")),
ConnectionInvokeType.Upstream => new SolidColorBrush(Color.Parse("#4A82E4")),
ConnectionInvokeType.None => new SolidColorBrush(Color.Parse("#56CEF6")),
_ => throw new Exception(),
};
}
/// <summary>
/// 根据连接类型指定颜色
/// </summary>
/// <param name="connection"></param>
/// <returns></returns>
/// <exception cref="Exception"></exception>
public static SolidColorBrush ToLineColor(this ConnectionArgSourceType connection)
{
return connection switch
{
ConnectionArgSourceType.GetPreviousNodeData => new SolidColorBrush(Color.Parse("#56CEF6")), // 04FC10 & 027E08
ConnectionArgSourceType.GetOtherNodeData => new SolidColorBrush(Color.Parse("#56CEF6")),
ConnectionArgSourceType.GetOtherNodeDataOfInvoke => new SolidColorBrush(Color.Parse("#56CEF6")),
_ => throw new Exception(),
};
}
}
}

View File

@@ -1,42 +0,0 @@
using Avalonia;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Extension
{
public static class PointExtension
{
public static Point Add(this Point a, Point b)
{
return new Point(a.X + b.X, a.Y + b.Y);
}
public static Point Sub(this Point a, Point b)
{
return new Point(a.X - b.X, a.Y - b.Y);
}
public static Vector ToVector(this Point me)
{
return new Vector(me.X, me.Y);
}
}
public static class VectorExtension
{
public static double DotProduct(this Vector a, Vector b)
{
return a.X * b.X + a.Y * b.Y;
}
public static Vector NormalizeTo(this Vector v)
{
var temp = v;
temp.Normalize();
return temp;
}
}
}

View File

@@ -1,32 +0,0 @@
using Avalonia.Controls;
using Avalonia.Input;
using Avalonia;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Extension
{
internal static class PointerExtension
{
public static bool JudgePointer(this
PointerEventArgs eventArgs,
object? sender,
PointerType pointerType,
Func<PointerPointProperties,bool> judgePointerFunc)
{
if(sender is not Visual visual)
{
return false;
}
if (eventArgs.Pointer.Type == pointerType) // 是否是否是指定的设备类型
{
var point = eventArgs.GetCurrentPoint(visual); // 获取到点击点
return judgePointerFunc.Invoke(point.Properties); // 判断是否属于某种类型的点击
}
return false;
}
}
}

View File

@@ -1,138 +0,0 @@
using Avalonia;
using Avalonia.Threading;
using Serein.Library;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Custom.Views;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Model
{
/// <summary>
/// 节点之间连接线的相关控制方法
/// </summary>
internal class ConnectingManage
{
/// <summary>
/// 是否正在创建连线
/// </summary>
public bool IsCreateing { get; set; }
/// <summary>
/// 起始控制点
/// </summary>
public NodeJunctionView? StartJunction { get; set; }
/// <summary>
/// 当前的控制点
/// </summary>
public NodeJunctionView? CurrentJunction { get; set; }
/// <summary>
/// 线条样式
/// </summary>
public NodeConnectionLineControl? TempLine { get; set; }
/// <summary>
/// 线条类别(方法调用)
/// </summary>
public ConnectionInvokeType ConnectionInvokeType { get; set; } = ConnectionInvokeType.IsSucceed;
/// <summary>
/// 线条类别(参数传递)
/// </summary>
public ConnectionArgSourceType ConnectionArgSourceType { get; set; } = ConnectionArgSourceType.GetOtherNodeData;
/// <summary>
/// 判断当前连接类型
/// </summary>
public JunctionOfConnectionType? Type => StartJunction?.JunctionType.ToConnectyionType();
private readonly INodeOperationService nodeOperationService1;
/// <summary>
/// 是否允许连接
/// </summary>
public bool IsCanConnected()
{
if (StartJunction is null
|| CurrentJunction is null )
{
return false;
}
if (StartJunction?.MyNode is null
|| StartJunction.MyNode.Equals(CurrentJunction.MyNode))
return false;
if (StartJunction.JunctionType.IsCanConnection(CurrentJunction.JunctionType))
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 更新临时的连接线
/// </summary>
/// <param name="point"></param>
public void UpdatePoint(Point point)
{
if (StartJunction is null || CurrentJunction is null )
{
return;
}
if (IsCanConnected())
{
return;
}
if (StartJunction.JunctionType == Library.JunctionType.Execute
|| StartJunction.JunctionType == Library.JunctionType.ArgData)
{
TempLine?.RefreshLeftPointOfTempLineDsiplay(point);
}
else
{
TempLine?.RefreshRightPointOfTempLineDsiplay(point);
}
}
/// <summary>
/// 重置
/// </summary>
public void Reset()
{
if(CurrentJunction is not null)
{
CurrentJunction.IsPreviewing = false;
Dispatcher.UIThread.InvokeAsync(CurrentJunction.InvalidateVisual, DispatcherPriority.Background);
}
if(StartJunction is not null)
{
StartJunction.IsPreviewing = false;
Dispatcher.UIThread.InvokeAsync(StartJunction.InvalidateVisual, DispatcherPriority.Background);
}
IsCreateing = false;
TempLine?.Remove();
ConnectionInvokeType = ConnectionInvokeType.IsSucceed;
ConnectionArgSourceType = ConnectionArgSourceType.GetOtherNodeData;
}
}
}

View File

@@ -1,14 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Model
{
internal class FlowLibraryInfo
{
}
}

View File

@@ -1,41 +0,0 @@
using Avalonia.Controls;
using Serein.Workbench.Avalonia.Custom.Views;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Model
{
/// <summary>
/// 绘制的线
/// </summary>
public class NodeConnectionLine
{
/// <summary>
/// 将线条绘制出来(临时线)
/// </summary>
/// <param name="canvas">放置画布</param>
/// <param name="line">线的实体</param>
public NodeConnectionLine(Canvas canvas, ConnectionLineShape line)
{
Canvas = canvas;
Line = line;
canvas?.Children.Add(line);
}
public Canvas Canvas { get; }
public ConnectionLineShape Line { get; }
/// <summary>
/// 移除线
/// </summary>
public void Remove()
{
Canvas?.Children.Remove(Line);
}
}
}

View File

@@ -1,339 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Controls.Shapes;
using Avalonia.Media;
using Avalonia.VisualTree;
using Serein.Library;
using Serein.Script.Node;
using Serein.Workbench.Avalonia.Custom.Views;
using Serein.Workbench.Avalonia.Extension;
using Serein.Workbench.Avalonia.Services;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;
using Color = Avalonia.Media.Color;
using Point = Avalonia.Point;
namespace Serein.Workbench.Avalonia.Model
{
public class NodeConnectionLineControl
{
/// <summary>
/// 线条类别(方法调用)
/// </summary>
public ConnectionInvokeType ConnectionInvokeType { get; set; } = ConnectionInvokeType.IsSucceed;
/// <summary>
/// 线条类别(参数传递)
/// </summary>
public ConnectionArgSourceType ConnectionArgSourceType { get; set; } = ConnectionArgSourceType.GetOtherNodeData;
/// <summary>
/// 画布
/// </summary>
private Canvas Canvas;
/// <summary>
/// 连接线的起点
/// </summary>
private NodeJunctionView? LeftNodeJunctionView;
/// <summary>
/// 连接线的终点
/// </summary>
private NodeJunctionView? RightNodeJunctionView;
/// <summary>
/// 连接时显示的线
/// </summary>
public ConnectionLineShape? ConnectionLineShape { get; private set; }
private NodeJunctionView StartNodeJunctionView;
public NodeConnectionLineControl(Canvas canvas,
NodeJunctionView? leftNodeJunctionView,
NodeJunctionView? rightNodeJunctionView)
{
if (leftNodeJunctionView is null && rightNodeJunctionView is null)
{
throw new Exception("不能都为空");
}
Canvas = canvas;
LeftNodeJunctionView = leftNodeJunctionView;
RightNodeJunctionView = rightNodeJunctionView;
if (leftNodeJunctionView is null && rightNodeJunctionView is not null)
{
StartNodeJunctionView = rightNodeJunctionView;
}
else if(leftNodeJunctionView is not null && rightNodeJunctionView is null)
{
StartNodeJunctionView = leftNodeJunctionView;
}
else if (leftNodeJunctionView is not null && rightNodeJunctionView is not null)
{
LeftNodeJunctionView = leftNodeJunctionView;
RightNodeJunctionView = rightNodeJunctionView;
RefreshLineDsiplay();
}
}
/// <summary>
/// 连接到终点
/// </summary>
/// <param name="endNodeJunctionView"></param>
public void ToEnd(NodeJunctionView endNodeJunctionView)
{
var @bool = endNodeJunctionView.JunctionType == JunctionType.Execute || endNodeJunctionView.JunctionType == JunctionType.ArgData;
(LeftNodeJunctionView, RightNodeJunctionView) = @bool? (StartNodeJunctionView, endNodeJunctionView) : (endNodeJunctionView, StartNodeJunctionView);
RefreshLineDsiplay();
return;
/*if(StartNodeJunctionView.JunctionType == JunctionType.NextStep
&& endNodeJunctionView.JunctionType == JunctionType.Execute
&& StartNodeJunctionView.MyNode?.Equals(endNodeJunctionView.MyNode) == false)
{
LeftNodeJunctionView = StartNodeJunctionView;
RightNodeJunctionView = endNodeJunctionView;
RefreshLineDsiplay();
return;
}
if (StartNodeJunctionView.JunctionType == JunctionType.ReturnData
&& endNodeJunctionView.JunctionType == JunctionType.ArgData
&& StartNodeJunctionView.MyNode?.Equals(endNodeJunctionView.MyNode) == false)
{
LeftNodeJunctionView = StartNodeJunctionView;
RightNodeJunctionView = endNodeJunctionView;
RefreshLineDsiplay();
return;
}
if (StartNodeJunctionView.JunctionType == JunctionType.Execute
&& endNodeJunctionView.JunctionType == JunctionType.NextStep
&& StartNodeJunctionView.MyNode?.Equals(endNodeJunctionView.MyNode) == false)
{
LeftNodeJunctionView = endNodeJunctionView;
RightNodeJunctionView = StartNodeJunctionView;
RefreshLineDsiplay();
return;
}
if (StartNodeJunctionView.JunctionType == JunctionType.ArgData
&& endNodeJunctionView.JunctionType == JunctionType.ReturnData
&& StartNodeJunctionView.MyNode?.Equals(endNodeJunctionView.MyNode) == false)
{
LeftNodeJunctionView = endNodeJunctionView;
RightNodeJunctionView = StartNodeJunctionView;
RefreshLineDsiplay();
return;
}*/
}
/// <summary>
/// 刷新线的显示
/// </summary>
public void RefreshLineDsiplay()
{
if (LeftNodeJunctionView is null || RightNodeJunctionView is null)
{
return;
}
var leftPoint = GetPoint(LeftNodeJunctionView);
var rightPoint = GetPoint(RightNodeJunctionView);
if (ConnectionLineShape is null)
{
CreateLineShape(leftPoint, rightPoint, GetBackgrounp());
}
else
{
var brush = GetBackgrounp();
ConnectionLineShape.UpdatePoint(leftPoint, rightPoint, brush);
}
}
//public void UpdateColor()
//{
// var brush = GetBackgrounp();
// ConnectionLineShape?.UpdateColor(brush);
//}
/// <summary>
/// 刷新临时线的显示
/// </summary>
public void RefreshRightPointOfTempLineDsiplay(Point rightPoint)
{
if (ConnectionLineShape is not null)
{
RightNodeJunctionView = null;
var brush = GetBackgrounp();
ConnectionLineShape.UpdateRightPoint(rightPoint, brush);
return;
}
if (LeftNodeJunctionView is not null)
{
var leftPoint = GetPoint(LeftNodeJunctionView);
var brush = GetBackgrounp();
CreateLineShape(leftPoint, rightPoint, brush);
}
}
/// <summary>
/// 刷新临时线的显示
/// </summary>
public void RefreshLeftPointOfTempLineDsiplay(Point leftPoint)
{
if (ConnectionLineShape is not null)
{
var brush = GetBackgrounp();
LeftNodeJunctionView = null;
ConnectionLineShape.UpdateLeftPoints(leftPoint, brush);
return;
}
if (RightNodeJunctionView is not null)
{
var rightPoint = GetPoint(RightNodeJunctionView);
var brush = GetBackgrounp();
CreateLineShape(leftPoint, rightPoint, brush);
}
}
/// <summary>
/// 获取背景颜色
/// </summary>
/// <returns></returns>
public Brush GetBackgrounp()
{
if (LeftNodeJunctionView is null || RightNodeJunctionView is null)
{
return new SolidColorBrush(Color.Parse("#FF0000")); // 没有终点
}
// 判断连接控制点是否匹配
if (!IsCanConnected())
{
return new SolidColorBrush(Color.Parse("#FF0000"));
}
if (GetConnectionType() == JunctionOfConnectionType.Invoke)
{
return ConnectionInvokeType.ToLineColor(); // 调用
}
else if (GetConnectionType() == JunctionOfConnectionType.Arg)
{
return ConnectionArgSourceType.ToLineColor(); // 参数
}
else
{
return new SolidColorBrush(Color.Parse("#FF0000"));
}
}
public bool IsCanConnected()
{
if (LeftNodeJunctionView is null
|| RightNodeJunctionView is null)
{
return false;
}
if (LeftNodeJunctionView?.MyNode is null
|| LeftNodeJunctionView.MyNode.Equals(RightNodeJunctionView.MyNode))
return false;
if (LeftNodeJunctionView.JunctionType.IsCanConnection(RightNodeJunctionView.JunctionType))
{
return true;
}
else
{
return false;
}
}
/// <summary>
/// 移除线
/// </summary>
public void Remove()
{
if (ConnectionLineShape is null)
{
return;
}
Canvas.Children.Remove(ConnectionLineShape);
}
private static Point defaultPoint = new Point(0, 0);
int count;
private Point GetPoint(NodeJunctionView nodeJunctionView)
{
var junctionSize = nodeJunctionView.GetTransformedBounds()!.Value.Bounds.Size;
Point junctionPoint;
if (nodeJunctionView.JunctionType == JunctionType.ArgData || nodeJunctionView.JunctionType == JunctionType.Execute)
{
junctionPoint = new Point(junctionSize.Width / 2 - 11, junctionSize.Height / 2); // 选择左侧
}
else
{
junctionPoint = new Point(junctionSize.Width / 2 + 11, junctionSize.Height / 2); // 选择右侧
}
if (nodeJunctionView.TranslatePoint(junctionPoint, Canvas) is Point point)
{
//myData.StartPoint = point;
return point;
}
else
{
return defaultPoint;
}
}
private void CreateLineShape(Point leftPoint, Point rightPoint, Brush brush)
{
ConnectionLineShape = new ConnectionLineShape(leftPoint, rightPoint, brush);
Canvas.Children.Add(ConnectionLineShape);
}
private JunctionOfConnectionType GetConnectionType()
{
if(LeftNodeJunctionView is null)
{
if(RightNodeJunctionView is null)
{
return JunctionOfConnectionType.None;
}
else
{
return RightNodeJunctionView.JunctionType.ToConnectyionType();
}
}
else
{
return LeftNodeJunctionView.JunctionType.ToConnectyionType();
}
}
}
}

View File

@@ -1,61 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<LangVersion>latest</LangVersion>
<AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
</PropertyGroup>
<ItemGroup>
<AvaloniaResource Include="Assets\**" />
</ItemGroup>
<ItemGroup>
<AvaloniaXaml Remove="Commands\**" />
<Compile Remove="Commands\**" />
<EmbeddedResource Remove="Commands\**" />
<None Remove="Commands\**" />
</ItemGroup>
<ItemGroup>
<Compile Remove="Custom\ViewModels\FlowLibraryInfoViewModel.cs" />
<Compile Remove="Model\NodeConnectionLine.cs" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Avalonia" Version="11.2.3" />
<PackageReference Include="Avalonia.Skia" Version="11.2.3" />
<PackageReference Include="Avalonia.Themes.Fluent" Version="11.2.3" />
<PackageReference Include="Avalonia.Fonts.Inter" Version="11.2.3" />
<PackageReference Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
<PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.2.3" />
<PackageReference Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.0" />
</ItemGroup>
<ItemGroup>
<Folder Include="Resources\" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\DynamicControl\SereinFlow\Library\Serein.Library.csproj" />
<ProjectReference Include="..\..\..\DynamicControl\SereinFlow\NodeFlow\Serein.NodeFlow.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Custom\Views\FlowLibraryMethodInfoView.axaml.cs">
<DependentUpon>FlowLibraryMethodInfoView1. axaml</DependentUpon>
</Compile>
<Compile Update="Custom\Views\MainMenuBarView.axaml.cs">
<DependentUpon>MainMenuBarView.axaml</DependentUpon>
</Compile>
<Compile Update="Custom\Views\FlowLibraryInfoView.axaml.cs">
<DependentUpon>FlowLibraryInfoView. axaml</DependentUpon>
</Compile>
<Compile Update="Custom\Views\NodeContainerView.axaml.cs">
<DependentUpon>NodeContainerView.axaml</DependentUpon>
</Compile>
</ItemGroup>
</Project>

View File

@@ -1,345 +0,0 @@
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia;
using Newtonsoft.Json.Linq;
using Serein.Library.Api;
using Serein.NodeFlow;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Serein.Library;
using Serein.Library.Utils;
using Serein.Workbench.Avalonia.Api;
namespace Serein.Workbench.Avalonia.Services
{
internal class FlowEEForwardingService : IFlowEEForwardingService
{
/// <summary>
/// 流程运行环境
/// </summary>
private readonly IFlowEnvironment flowEnvironment;
private readonly IFlowEnvironmentEvent flowEnvironmentEvent;
/// <summary>
/// 转发流程运行环境各个事件的实现类
/// </summary>
/// <param name="flowEnvironment"></param>
/// <param name="flowNodeControlService"></param>
public FlowEEForwardingService(IFlowEnvironment flowEnvironment,
IFlowEnvironmentEvent flowEnvironmentEvent)
{
this.flowEnvironment = flowEnvironment;
this.flowEnvironmentEvent = flowEnvironmentEvent;
InitFlowEnvironmentEvent();
}
#region
/// <summary>
/// 加载了依赖文件事件
/// </summary>
public event LoadDllHandler? OnDllLoad;
/// <summary>
/// 项目加载完成事件
/// </summary>
public event ProjectLoadedHandler? OnProjectLoaded;
/// <summary>
/// 项目保存中事件
/// </summary>
public event ProjectSavingHandler? OnProjectSaving;
/// <summary>
/// 节点连接改变事件
/// </summary>
public event NodeConnectChangeHandler? OnNodeConnectChange;
/// <summary>
/// 节点创建事件
/// </summary>
public event NodeCreateHandler? OnNodeCreate;
/// <summary>
/// 节点移除事件
/// </summary>
public event NodeRemoveHandler? OnNodeRemove;
/// <summary>
/// 节点放置容器事件
/// </summary>
public event NodePlaceHandler? OnNodePlace;
/// <summary>
/// 节点取出事件
/// </summary>
public event NodeTakeOutHandler? OnNodeTakeOut;
/// <summary>
/// 流程起始节点改变事件
/// </summary>
public event StartNodeChangeHandler? OnStartNodeChange;
/// <summary>
/// 流程运行完毕事件
/// </summary>
public event FlowRunCompleteHandler? OnFlowRunComplete;
/// <summary>
/// 被监视的对象数据改变事件
/// </summary>
public event MonitorObjectChangeHandler? OnMonitorObjectChange;
/// <summary>
/// 节点中断状态改变事件
/// </summary>
public event NodeInterruptStateChangeHandler? OnNodeInterruptStateChange;
/// <summary>
/// 表达式中断触发事件
/// </summary>
public event ExpInterruptTriggerHandler? OnInterruptTrigger;
/// <summary>
/// 容器对象改变事件
/// </summary>
public event IOCMembersChangedHandler? OnIOCMembersChanged;
/// <summary>
/// 节点定位事件
/// </summary>
public event NodeLocatedHandler? OnNodeLocated;
/// <summary>
/// 节点移动事件
/// </summary>
public event NodeMovedHandler? OnNodeMoved;
/// <summary>
/// 运行环境输出事件
/// </summary>
public event EnvOutHandler? OnEnvOut;
#endregion
#region
private void InitFlowEnvironmentEvent()
{
flowEnvironmentEvent.OnDllLoad += FlowEnvironment_DllLoadEvent;
flowEnvironmentEvent.OnProjectSaving += EnvDecorator_OnProjectSaving;
flowEnvironmentEvent.OnProjectLoaded += FlowEnvironment_OnProjectLoaded;
flowEnvironmentEvent.OnStartNodeChange += FlowEnvironment_StartNodeChangeEvent;
flowEnvironmentEvent.OnNodeConnectChange += FlowEnvironment_NodeConnectChangeEvemt;
flowEnvironmentEvent.OnNodeCreate += FlowEnvironment_NodeCreateEvent;
flowEnvironmentEvent.OnNodeRemove += FlowEnvironment_NodeRemoveEvent;
flowEnvironmentEvent.OnNodePlace += EnvDecorator_OnNodePlaceEvent;
flowEnvironmentEvent.OnNodeTakeOut += EnvDecorator_OnNodeTakeOutEvent;
flowEnvironmentEvent.OnFlowRunComplete += FlowEnvironment_OnFlowRunCompleteEvent;
flowEnvironmentEvent.OnMonitorObjectChange += FlowEnvironment_OnMonitorObjectChangeEvent;
flowEnvironmentEvent.OnNodeInterruptStateChange += FlowEnvironment_OnNodeInterruptStateChangeEvent;
flowEnvironmentEvent.OnInterruptTrigger += FlowEnvironment_OnInterruptTriggerEvent;
flowEnvironmentEvent.OnIOCMembersChanged += FlowEnvironment_OnIOCMembersChangedEvent;
flowEnvironmentEvent.OnNodeLocated += FlowEnvironment_OnNodeLocateEvent;
flowEnvironmentEvent.OnNodeMoved += FlowEnvironment_OnNodeMovedEvent;
flowEnvironmentEvent.OnEnvOut += FlowEnvironment_OnEnvOutEvent;
}
private void ResetFlowEnvironmentEvent()
{
flowEnvironmentEvent.OnDllLoad -= FlowEnvironment_DllLoadEvent;
flowEnvironmentEvent.OnProjectSaving -= EnvDecorator_OnProjectSaving;
flowEnvironmentEvent.OnProjectLoaded -= FlowEnvironment_OnProjectLoaded;
flowEnvironmentEvent.OnStartNodeChange -= FlowEnvironment_StartNodeChangeEvent;
flowEnvironmentEvent.OnNodeConnectChange -= FlowEnvironment_NodeConnectChangeEvemt;
flowEnvironmentEvent.OnNodeCreate -= FlowEnvironment_NodeCreateEvent;
flowEnvironmentEvent.OnNodeRemove -= FlowEnvironment_NodeRemoveEvent;
flowEnvironmentEvent.OnNodePlace -= EnvDecorator_OnNodePlaceEvent;
flowEnvironmentEvent.OnNodeTakeOut -= EnvDecorator_OnNodeTakeOutEvent;
flowEnvironmentEvent.OnFlowRunComplete -= FlowEnvironment_OnFlowRunCompleteEvent;
flowEnvironmentEvent.OnMonitorObjectChange -= FlowEnvironment_OnMonitorObjectChangeEvent;
flowEnvironmentEvent.OnNodeInterruptStateChange -= FlowEnvironment_OnNodeInterruptStateChangeEvent;
flowEnvironmentEvent.OnInterruptTrigger -= FlowEnvironment_OnInterruptTriggerEvent;
flowEnvironmentEvent.OnIOCMembersChanged -= FlowEnvironment_OnIOCMembersChangedEvent;
flowEnvironmentEvent.OnNodeLocated -= FlowEnvironment_OnNodeLocateEvent;
flowEnvironmentEvent.OnNodeMoved -= FlowEnvironment_OnNodeMovedEvent;
flowEnvironmentEvent.OnEnvOut -= FlowEnvironment_OnEnvOutEvent;
}
#region
/// <summary>
/// 环境内容输出
/// </summary>
/// <param name="type"></param>
/// <param name="value"></param>
private void FlowEnvironment_OnEnvOutEvent(InfoType type, string value)
{
//LogOutWindow.AppendText($"{DateTime.Now} [{type}] : {value}{Environment.NewLine}");
}
/// <summary>
/// 需要保存项目
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
private void EnvDecorator_OnProjectSaving(ProjectSavingEventArgs eventArgs)
{
OnProjectSaving?.Invoke(eventArgs);
}
/// <summary>
/// 加载完成
/// </summary>
/// <param name="eventArgs"></param>
private void FlowEnvironment_OnProjectLoaded(ProjectLoadedEventArgs eventArgs)
{
OnProjectLoaded?.Invoke(eventArgs);
}
/// <summary>
/// 运行完成
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
private void FlowEnvironment_OnFlowRunCompleteEvent(FlowEventArgs eventArgs)
{
SereinEnv.WriteLine(InfoType.INFO, "-------运行完成---------\r\n");
OnFlowRunComplete?.Invoke(eventArgs);
}
/// <summary>
/// 加载了DLL文件dll内容
/// </summary>
private void FlowEnvironment_DllLoadEvent(LoadDllEventArgs eventArgs)
{
OnDllLoad?.Invoke(eventArgs);
}
/// <summary>
/// 节点连接关系变更
/// </summary>
/// <param name="eventArgs"></param>
private void FlowEnvironment_NodeConnectChangeEvemt(NodeConnectChangeEventArgs eventArgs)
{
OnNodeConnectChange?.Invoke(eventArgs);
}
/// <summary>
/// 节点移除事件
/// </summary>
/// <param name="eventArgs"></param>
private void FlowEnvironment_NodeRemoveEvent(NodeRemoveEventArgs eventArgs)
{
OnNodeRemove?.Invoke(eventArgs);
}
/// <summary>
/// 添加节点事件
/// </summary>
/// <param name="eventArgs">添加节点事件参数</param>
/// <exception cref="NotImplementedException"></exception>
private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs)
{
OnNodeCreate?.Invoke(eventArgs);
}
/// <summary>
/// 放置一个节点
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
private void EnvDecorator_OnNodePlaceEvent(NodePlaceEventArgs eventArgs)
{
OnNodePlace?.Invoke(eventArgs);
}
/// <summary>
/// 取出一个节点
/// </summary>
/// <param name="eventArgs"></param>
private void EnvDecorator_OnNodeTakeOutEvent(NodeTakeOutEventArgs eventArgs)
{
OnNodeTakeOut?.Invoke(eventArgs);
}
/// <summary>
/// 设置了流程起始控件
/// </summary>
/// <param name="oldNodeGuid"></param>
/// <param name="newNodeGuid"></param>
private void FlowEnvironment_StartNodeChangeEvent(StartNodeChangeEventArgs eventArgs)
{
OnStartNodeChange?.Invoke(eventArgs);
}
/// <summary>
/// 被监视的对象发生改变
/// </summary>
/// <param name="eventArgs"></param>
private void FlowEnvironment_OnMonitorObjectChangeEvent(MonitorObjectEventArgs eventArgs)
{
OnMonitorObjectChange?.Invoke(eventArgs);
}
/// <summary>
/// 节点中断状态改变。
/// </summary>
/// <param name="eventArgs"></param>
private void FlowEnvironment_OnNodeInterruptStateChangeEvent(NodeInterruptStateChangeEventArgs eventArgs)
{
OnNodeInterruptStateChange?.Invoke(eventArgs);
}
/// <summary>
/// 节点触发了中断
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
private void FlowEnvironment_OnInterruptTriggerEvent(InterruptTriggerEventArgs eventArgs)
{
OnInterruptTrigger?.Invoke(eventArgs);
}
/// <summary>
/// IOC变更
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
private void FlowEnvironment_OnIOCMembersChangedEvent(IOCMembersChangedEventArgs eventArgs)
{
OnIOCMembersChanged?.Invoke(eventArgs);
}
/// <summary>
/// 节点需要定位
/// </summary>
/// <param name="eventArgs"></param>
/// <exception cref="NotImplementedException"></exception>
private void FlowEnvironment_OnNodeLocateEvent(NodeLocatedEventArgs eventArgs)
{
OnNodeLocated?.Invoke(eventArgs);
}
/// <summary>
/// 节点移动
/// </summary>
/// <param name="eventArgs"></param>
private void FlowEnvironment_OnNodeMovedEvent(NodeMovedEventArgs eventArgs)
{
OnNodeMoved?.Invoke(eventArgs);
}
#endregion
#endregion
}
}

View File

@@ -1,80 +0,0 @@
using Avalonia.Controls;
using Avalonia.Input;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Services
{
delegate void KeyDownEventHandler(Key key);
delegate void KeyUpEventHandler(Key key);
/// <summary>
/// 全局事件服务
/// </summary>
internal interface IKeyEventService
{
event KeyDownEventHandler KeyDown;
event KeyUpEventHandler KeyUp;
/// <summary>
/// 获取某个按键状态
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
bool GetKeyState(Key key);
/// <summary>
/// 设置某个按键的状态
/// </summary>
/// <param name="key"></param>
/// <param name="state"></param>
void SetKeyState(Key key, bool statestate);
}
/// <summary>
/// 管理按键状态
/// </summary>
internal class KeyEventService : IKeyEventService
{
/// <summary>
/// 按键按下
/// </summary>
public event KeyDownEventHandler KeyDown;
/// <summary>
/// 按键松开
/// </summary>
public event KeyUpEventHandler KeyUp;
public KeyEventService()
{
var arr = Enum.GetValues<Key>();
KeysState = new bool[arr.Length];
// 绑定快捷键
//HotKeyManager.SetHotKey(saveMenuItem, new KeyGesture(Key.S, KeyModifiers.Control));
}
private readonly bool[] KeysState;
public bool GetKeyState(Key key)
{
return KeysState[(int)key];
}
public void SetKeyState(Key key, bool state)
{
if (state)
{
KeyDown?.Invoke(key);
}
else
{
KeyUp?.Invoke(key);
}
//Debug.WriteLine($"按键事件:{key} - {state}");
KeysState[(int)key] = state;
}
}
}

View File

@@ -1,537 +0,0 @@
using Avalonia;
using Avalonia.Controls;
using Avalonia.Media;
using Avalonia.VisualTree;
using Newtonsoft.Json;
using Serein.Library;
using Serein.Library.Api;
using Serein.Library.Utils;
using Serein.NodeFlow;
using Serein.Workbench.Avalonia.Api;
using Serein.Workbench.Avalonia.Custom.Node.ViewModels;
using Serein.Workbench.Avalonia.Custom.Node.Views;
using Serein.Workbench.Avalonia.Custom.Views;
using Serein.Workbench.Avalonia.Extension;
using Serein.Workbench.Avalonia.Model;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.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.Avalonia.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; // 订阅运行环境连接了节点事件
flowEnvironment.NodeMVVMManagement.RegisterUI(NodeControlType.Action, typeof(ActionNodeView), typeof(ActionNodeViewModel)); // 注册动作节点
// 手动加载项目
_ = Task.Run(async delegate
{
await Task.Delay(1000);
var 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
}
/*else
{
ConnectionArgSourceType connectionArgSourceType = eventArgs.ConnectionArgSourceType;
#region 创建/删除节点之间的参数传递关系
#region 创建连接
if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接
{
if (fromNodeControl is not INodeJunction IFormJunction || toNodeControl is not INodeJunction IToJunction)
{
SereinEnv.WriteLine(InfoType.INFO, "非预期的情况");
return;
}
JunctionControlBase startJunction = eventArgs.ConnectionArgSourceType switch
{
ConnectionArgSourceType.GetPreviousNodeData => IFormJunction.ReturnDataJunction, // 自身节点
ConnectionArgSourceType.GetOtherNodeData => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
ConnectionArgSourceType.GetOtherNodeDataOfInvoke => IFormJunction.ReturnDataJunction, // 其它节点的返回值控制点
_ => throw new Exception("窗体事件 FlowEnvironment_NodeConnectChangeEvemt 创建/删除节点之间的参数传递关系 JunctionControlBase 枚举值错误 。非预期的枚举值。") // 应该不会触发
};
if (IToJunction.ArgDataJunction.Length <= eventArgs.ArgIndex)
{
_ = Task.Run(async () =>
{
await Task.Delay(500);
FlowEnvironment_NodeConnectChangeEvemt(eventArgs);
});
return;
}
JunctionControlBase endJunction = IToJunction.ArgDataJunction[eventArgs.ArgIndex];
LineType lineType = LineType.Bezier;
// 添加连接
var connection = new ConnectionControl(
lineType,
FlowChartCanvas,
eventArgs.ArgIndex,
eventArgs.ConnectionArgSourceType,
startJunction,
endJunction,
IToJunction
);
Connections.Add(connection);
fromNodeControl.AddCnnection(connection);
toNodeControl.AddCnnection(connection);
EndConnection(); // 环境触发了创建节点连接事件
}
#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))
.ToList(); // 获取这两个节点之间的所有连接关系
foreach (var connection in removeConnections)
{
if (connection.End is ArgJunctionControl junctionControl && junctionControl.ArgIndex == eventArgs.ArgIndex)
{
// 找到符合删除条件的连接线
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);
if (t_ViewModel is not NodeViewModelBase viewModelBase)
{
nodeView = null;
return false;
}
viewModelBase.NodeModelBase = nodeModel; // 设置节点对象
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);
}
});
}
/// <summary>
/// 尝试在连接控制点之间创建连接线
/// </summary>
public void TryCreateConnectionOnJunction(NodeJunctionView startJunction)
{
if (MainCanvas is not null)
{
ConnectingManage.Reset();
ConnectingManage.IsCreateing = true; // 表示开始连接
ConnectingManage.StartJunction = startJunction;
ConnectingManage.CurrentJunction = startJunction;
if(startJunction.JunctionType == JunctionType.NextStep || startJunction.JunctionType == JunctionType.ReturnData)
{
ConnectingManage.TempLine = new NodeConnectionLineControl(MainCanvas, startJunction, null);
}
else
{
ConnectingManage.TempLine = new NodeConnectionLineControl(MainCanvas,null ,startJunction);
}
/*var junctionOfConnectionType = startJunction.JunctionType.ToConnectyionType();
ConnectionLineShape bezierLine;
Brush brushColor; // 临时线的颜色
if (junctionOfConnectionType == JunctionOfConnectionType.Invoke)
{
brushColor = ConnectionInvokeType.IsSucceed.ToLineColor();
}
else if (junctionOfConnectionType == JunctionOfConnectionType.Arg)
{
brushColor = ConnectionArgSourceType.GetOtherNodeData.ToLineColor();
}
else
{
return;
}
bezierLine = new ConnectionLineShape(myData.StartPoint,
myData.StartPoint,
brushColor,
isTop: true); // 绘制临时的线
*/
//Mouse.OverrideCursor = Cursors.Cross; // 设置鼠标为正在创建连线
}
}
#endregion
}
}

View File

@@ -1,90 +0,0 @@
using Serein.Library;
using Serein.Library.Api;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Services
{
#region
public delegate void PreviewlMethodInfoHandler(PreviewlMethodInfoEventArgs eventArgs);
#endregion
#region
public class PreviewlMethodInfoEventArgs(MethodDetailsInfo mdInfo) : EventArgs
{
/// <summary>
/// 方法信息
/// </summary>
public MethodDetailsInfo MethodDetailsInfo { get; } = mdInfo;
}
#endregion
/// <summary>
/// 工作台事件管理
/// </summary>
internal interface IWorkbenchEventService
{
/// <summary>
/// 预览了某个方法信息(待创建)
/// </summary>
event PreviewlMethodInfoHandler OnPreviewlMethodInfo;
/// <summary>
/// 预览依赖方法信息
/// </summary>
void PreviewLibraryMethodInfo(MethodDetailsInfo mdInfo);
}
/// <summary>
/// 工作台事件的实现类
/// </summary>
internal class WorkbenchEventService : IWorkbenchEventService
{
private readonly IFlowEnvironment flowEnvironment;
/// <summary>
/// 管理工作台的事件
/// </summary>
/// <param name="flowEnvironment"></param>
public WorkbenchEventService(IFlowEnvironment flowEnvironment)
{
this.flowEnvironment = flowEnvironment;
}
private void SubscribeEvents()
{
}
/// <summary>
/// 预览了某个方法信息(待创建)
/// </summary>
public event PreviewlMethodInfoHandler? OnPreviewlMethodInfo;
/// <summary>
/// 预览依赖方法信息
/// </summary>
public void PreviewLibraryMethodInfo(MethodDetailsInfo mdInfo)
{
OnPreviewlMethodInfo?.Invoke(new PreviewlMethodInfoEventArgs(mdInfo));
}
/// <summary>
/// 需要放置节点控件
/// </summary>
public void PlateNodeControl()
{
}
}
}

View File

@@ -1,7 +0,0 @@
namespace Serein.Workbench.Avalonia.ViewModels;
public partial class MainViewModel : ViewModelBase
{
public string Greeting => "Welcome to Avalonia!";
}

View File

@@ -1,7 +0,0 @@
using CommunityToolkit.Mvvm.ComponentModel;
namespace Serein.Workbench.Avalonia.ViewModels;
public class ViewModelBase : ObservableObject
{
}

View File

@@ -1,34 +0,0 @@
<UserControl xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="clr-namespace:Serein.Workbench.Avalonia.ViewModels"
xmlns:cv="clr-namespace:Serein.Workbench.Avalonia.Custom.Views"
mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="640"
x:Class="Serein.Workbench.Avalonia.Views.MainView"
x:DataType="vm:MainViewModel">
<Design.DataContext>
<vm:MainViewModel />
</Design.DataContext>
<UserControl.Styles>
</UserControl.Styles>
<Grid RowDefinitions="auto,0,*" ColumnDefinitions="*">
<cv:MainMenuBarView Grid.Row="0" Grid.Column="0" Background="#E2E9EE"/>
<!--这里留空,以后放置功能区-->
<Grid Grid.Row="2" Grid.Column="0"
RowDefinitions="*" ColumnDefinitions="auto,*">
<!--依赖信息-->
<Grid IsVisible="False" RowDefinitions="*,auto" HorizontalAlignment="Left">
<!--已加载的依赖-->
<cv:FlowLibrarysView Grid.Row="0"/>
<!--<cv:FlowLibraryMethodInfoView Grid.Row="1" HorizontalAlignment="Left"/>-->
<!--当前预览的节点方法信息-->
</Grid>
<!--画布-->
<cv:NodeContainerView Grid.Column="1"/>
<!--其他视图-->
</Grid>
</Grid>
</UserControl>

View File

@@ -1,23 +0,0 @@
using Avalonia.Controls;
using Avalonia.Controls.Primitives;
using Avalonia.Input;
using Avalonia.Markup.Xaml;
using Newtonsoft.Json;
using Serein.Library;
using Serein.NodeFlow.Env;
using System;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
namespace Serein.Workbench.Avalonia.Views;
public partial class MainView : UserControl
{
public MainView()
{
InitializeComponent();
}
}

View File

@@ -1,14 +0,0 @@
<Window xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:vm="using:Serein.Workbench.Avalonia.ViewModels"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:views="clr-namespace:Serein.Workbench.Avalonia.Views"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="Serein.Workbench.Avalonia.Views.MainWindow"
Icon="/Assets/avalonia-logo.ico"
Title="Serein Flow Edit">
<views:MainView />
</Window>

View File

@@ -1,36 +0,0 @@
using Avalonia.Controls;
using Avalonia.Input;
using System.Diagnostics;
using System;
using Avalonia.Markup.Xaml;
using Newtonsoft.Json;
using Serein.NodeFlow.Env;
using System.Threading.Tasks;
using Serein.Library;
using Serein.Workbench.Avalonia.Services;
namespace Serein.Workbench.Avalonia.Views;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
IKeyEventService keyEventService = App.GetService<IKeyEventService>();
this.Loaded += MainWindow_Loaded;
this.KeyDown += (o, e) =>
{
keyEventService.SetKeyState(e.Key, true);
};
this.KeyUp += (o, e) =>
{
keyEventService.SetKeyState(e.Key, false);
};
}
private void MainWindow_Loaded(object? sender, global::Avalonia.Interactivity.RoutedEventArgs e)
{
}
}