mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
238 lines
8.7 KiB
C#
238 lines
8.7 KiB
C#
|
|
using System;
|
|||
|
|
using System.Collections.Generic;
|
|||
|
|
using System.Linq;
|
|||
|
|
using System.Text;
|
|||
|
|
using System.Threading.Tasks;
|
|||
|
|
using System.Windows.Media;
|
|||
|
|
using System.Windows;
|
|||
|
|
using Serein.Workbench.Node.View;
|
|||
|
|
using System.Windows.Controls;
|
|||
|
|
using Serein.Library;
|
|||
|
|
using System.Windows.Data;
|
|||
|
|
|
|||
|
|
namespace Serein.Workbench.Node.View
|
|||
|
|
{
|
|||
|
|
|
|||
|
|
|
|||
|
|
public abstract class NodeJunctionViewBase : ContentControl, IDisposable
|
|||
|
|
{
|
|||
|
|
public NodeJunctionViewBase()
|
|||
|
|
{
|
|||
|
|
var transfromGroup = new TransformGroup();
|
|||
|
|
transfromGroup.Children.Add(_Translate);
|
|||
|
|
RenderTransform = transfromGroup;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 每个连接器都有一个唯一标识符(Guid),用于标识连接器。
|
|||
|
|
/// </summary>
|
|||
|
|
public Guid Guid
|
|||
|
|
{
|
|||
|
|
get => (Guid)GetValue(GuidProperty);
|
|||
|
|
set => SetValue(GuidProperty, value);
|
|||
|
|
}
|
|||
|
|
public static readonly DependencyProperty GuidProperty = DependencyProperty.Register(
|
|||
|
|
nameof(Guid),
|
|||
|
|
typeof(Guid),
|
|||
|
|
typeof(NodeJunctionViewBase), // NodeConnectorContent
|
|||
|
|
new PropertyMetadata(Guid.Empty));
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 连接器当前的连接数,表示有多少条 NodeLink 连接到此连接器。该属性为只读。
|
|||
|
|
/// </summary>
|
|||
|
|
public int ConnectedCount
|
|||
|
|
{
|
|||
|
|
get => (int)GetValue(ConnectedCountProperty);
|
|||
|
|
private set => SetValue(ConnectedCountPropertyKey, value);
|
|||
|
|
}
|
|||
|
|
public static readonly DependencyPropertyKey ConnectedCountPropertyKey = DependencyProperty.RegisterReadOnly(
|
|||
|
|
nameof(ConnectedCount),
|
|||
|
|
typeof(int),
|
|||
|
|
typeof(NodeJunctionViewBase), // NodeConnectorContent
|
|||
|
|
new PropertyMetadata(0));
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty ConnectedCountProperty = ConnectedCountPropertyKey.DependencyProperty;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 布尔值,指示此连接器是否有任何连接。
|
|||
|
|
/// </summary>
|
|||
|
|
public bool IsConnected
|
|||
|
|
{
|
|||
|
|
get => (bool)GetValue(IsConnectedProperty);
|
|||
|
|
private set => SetValue(IsConnectedPropertyKey, value);
|
|||
|
|
}
|
|||
|
|
public static readonly DependencyPropertyKey IsConnectedPropertyKey = DependencyProperty.RegisterReadOnly(
|
|||
|
|
nameof(IsConnected),
|
|||
|
|
typeof(bool),
|
|||
|
|
typeof(NodeJunctionViewBase), // NodeConnectorContent
|
|||
|
|
new PropertyMetadata(false));
|
|||
|
|
|
|||
|
|
public static readonly DependencyProperty IsConnectedProperty = IsConnectedPropertyKey.DependencyProperty;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 这些属性控制连接器的外观(颜色、边框厚度、填充颜色)。
|
|||
|
|
/// </summary>
|
|||
|
|
public Brush Stroke
|
|||
|
|
{
|
|||
|
|
get => (Brush)GetValue(StrokeProperty);
|
|||
|
|
set => SetValue(StrokeProperty, value);
|
|||
|
|
}
|
|||
|
|
public static readonly DependencyProperty StrokeProperty = DependencyProperty.Register(
|
|||
|
|
nameof(Stroke),
|
|||
|
|
typeof(Brush),
|
|||
|
|
typeof(NodeJunctionViewBase), // NodeConnectorContent
|
|||
|
|
new FrameworkPropertyMetadata(Brushes.Blue));
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 这些属性控制连接器的外观(颜色、边框厚度、填充颜色)。
|
|||
|
|
/// </summary>
|
|||
|
|
public double StrokeThickness
|
|||
|
|
{
|
|||
|
|
get => (double)GetValue(StrokeThicknessProperty);
|
|||
|
|
set => SetValue(StrokeThicknessProperty, value);
|
|||
|
|
}
|
|||
|
|
public static readonly DependencyProperty StrokeThicknessProperty = DependencyProperty.Register(
|
|||
|
|
nameof(StrokeThickness),
|
|||
|
|
typeof(double),
|
|||
|
|
typeof(NodeJunctionViewBase), // NodeConnectorContent
|
|||
|
|
new FrameworkPropertyMetadata(1.0));
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 这些属性控制连接器的外观(颜色、边框厚度、填充颜色)。
|
|||
|
|
/// </summary>
|
|||
|
|
public Brush Fill
|
|||
|
|
{
|
|||
|
|
get => (Brush)GetValue(FillProperty);
|
|||
|
|
set => SetValue(FillProperty, value);
|
|||
|
|
}
|
|||
|
|
public static readonly DependencyProperty FillProperty = DependencyProperty.Register(
|
|||
|
|
nameof(Fill),
|
|||
|
|
typeof(Brush),
|
|||
|
|
typeof(NodeJunctionViewBase),// NodeConnectorContent
|
|||
|
|
new FrameworkPropertyMetadata(Brushes.Gray));
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 指示该连接器是否可以与其他连接器进行连接。
|
|||
|
|
/// </summary>
|
|||
|
|
public bool CanConnect
|
|||
|
|
{
|
|||
|
|
get => (bool)GetValue(CanConnectProperty);
|
|||
|
|
set => SetValue(CanConnectProperty, value);
|
|||
|
|
}
|
|||
|
|
public static readonly DependencyProperty CanConnectProperty = DependencyProperty.Register(
|
|||
|
|
nameof(CanConnect),
|
|||
|
|
typeof(bool),
|
|||
|
|
typeof(NodeJunctionViewBase),// NodeConnectorContent
|
|||
|
|
new FrameworkPropertyMetadata(true, FrameworkPropertyMetadataOptions.AffectsRender));
|
|||
|
|
|
|||
|
|
|
|||
|
|
private Point _Position = new Point();
|
|||
|
|
/// <summary>
|
|||
|
|
/// 该连接器的当前坐标(位置)。
|
|||
|
|
/// </summary>
|
|||
|
|
public Point Position
|
|||
|
|
{
|
|||
|
|
get => _Position;
|
|||
|
|
set => UpdatePosition(value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// (重要数据)表示连接器所属的节点。
|
|||
|
|
/// </summary>
|
|||
|
|
public NodeModelBase NodeModel { get; private set; } = null;
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 该连接器所连接的所有 NodeLink 的集合。
|
|||
|
|
/// </summary>
|
|||
|
|
public IEnumerable<ConnectionControl> NodeLinks => _NodeLinks;
|
|||
|
|
List<ConnectionControl> _NodeLinks = new List<ConnectionControl>();
|
|||
|
|
|
|||
|
|
protected abstract FrameworkElement ConnectorControl { get; }
|
|||
|
|
TranslateTransform _Translate = new TranslateTransform();
|
|||
|
|
void UpdatePosition(Point pos)
|
|||
|
|
{
|
|||
|
|
_Position = pos;
|
|||
|
|
_Translate.X = _Position.X;
|
|||
|
|
_Translate.Y = _Position.Y;
|
|||
|
|
|
|||
|
|
InvalidateVisual();
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 将 NodeLink 添加到连接器,并更新 ConnectedCount 和 IsConnected。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="nodeLink"></param>
|
|||
|
|
public void Connect(ConnectionControl nodeLink)
|
|||
|
|
{
|
|||
|
|
_NodeLinks.Add(nodeLink);
|
|||
|
|
ConnectedCount = _NodeLinks.Count;
|
|||
|
|
IsConnected = ConnectedCount > 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 断开与某个 NodeLink 的连接,更新连接状态。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="nodeLink"></param>
|
|||
|
|
public void Disconnect(ConnectionControl nodeLink)
|
|||
|
|
{
|
|||
|
|
_NodeLinks.Remove(nodeLink);
|
|||
|
|
ConnectedCount = _NodeLinks.Count;
|
|||
|
|
IsConnected = ConnectedCount > 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 获取连接器相对于指定 Canvas 的位置。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="canvas"></param>
|
|||
|
|
/// <param name="xScaleOffset"></param>
|
|||
|
|
/// <param name="yScaleOffset"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public Point GetContentPosition(Canvas canvas, double xScaleOffset = 0.5, double yScaleOffset = 0.5)
|
|||
|
|
{
|
|||
|
|
// it will be shifted Control position if not called UpdateLayout().
|
|||
|
|
ConnectorControl.UpdateLayout();
|
|||
|
|
var transformer = ConnectorControl.TransformToVisual(canvas);
|
|||
|
|
|
|||
|
|
var x = ConnectorControl.ActualWidth * xScaleOffset;
|
|||
|
|
var y = ConnectorControl.ActualHeight * yScaleOffset;
|
|||
|
|
return transformer.Transform(new Point(x, y));
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 更新与此连接器相连的所有 NodeLink 的位置。这个方法是抽象的,要求子类实现。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="canvas"></param>
|
|||
|
|
public abstract void UpdateLinkPosition(Canvas canvas);
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 用于检查此连接器是否可以与另一个连接器相连接,要求子类实现。
|
|||
|
|
/// </summary>
|
|||
|
|
/// <param name="connector"></param>
|
|||
|
|
/// <returns></returns>
|
|||
|
|
public abstract bool CanConnectTo(NodeJunctionViewBase connector);
|
|||
|
|
|
|||
|
|
/// <summary>
|
|||
|
|
/// 释放连接器相关的资源,包括样式、绑定和已连接的 NodeLink
|
|||
|
|
/// </summary>
|
|||
|
|
public void Dispose()
|
|||
|
|
{
|
|||
|
|
// You need to clear Style.
|
|||
|
|
// Because implemented on style for binding.
|
|||
|
|
Style = null;
|
|||
|
|
|
|||
|
|
// Clear binding for subscribing source changed event from old control.
|
|||
|
|
// throw exception about visual tree ancestor different if you not clear binding.
|
|||
|
|
BindingOperations.ClearAllBindings(this);
|
|||
|
|
|
|||
|
|
var nodeLinks = _NodeLinks.ToArray();
|
|||
|
|
|
|||
|
|
// it must instance to nodeLinks because change node link collection in NodeLink Dispose.
|
|||
|
|
foreach (var nodeLink in nodeLinks)
|
|||
|
|
{
|
|||
|
|
// nodeLink.Dispose();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
}
|