mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-25 01:16:35 +08:00
准备区分节点、参数、返回值的连接,做个备份
This commit is contained in:
@@ -664,7 +664,7 @@ namespace Serein.Library.Api
|
|||||||
/// <param name="fromNodeGuid">起始节点Guid</param>
|
/// <param name="fromNodeGuid">起始节点Guid</param>
|
||||||
/// <param name="toNodeGuid">目标节点Guid</param>
|
/// <param name="toNodeGuid">目标节点Guid</param>
|
||||||
/// <param name="connectionType">连接类型</param>
|
/// <param name="connectionType">连接类型</param>
|
||||||
Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType);
|
Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionType connectionType);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建节点/区域/基础控件
|
/// 创建节点/区域/基础控件
|
||||||
|
|||||||
@@ -30,7 +30,10 @@ namespace Serein.Library
|
|||||||
/// 异常发生分支(当前节点对应的方法执行时出现非预期的异常)
|
/// 异常发生分支(当前节点对应的方法执行时出现非预期的异常)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
IsError,
|
IsError,
|
||||||
|
/// <summary>
|
||||||
|
/// 无视
|
||||||
|
/// </summary>
|
||||||
|
// IsIgnore,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
31
Library/Enums/JunctionType.cs
Normal file
31
Library/Enums/JunctionType.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.Library
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 连接点类型
|
||||||
|
/// </summary>
|
||||||
|
public enum JunctionType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 当前执行
|
||||||
|
/// </summary>
|
||||||
|
Execute,
|
||||||
|
/// <summary>
|
||||||
|
/// 入参
|
||||||
|
/// </summary>
|
||||||
|
ArgData,
|
||||||
|
/// <summary>
|
||||||
|
/// 返回值
|
||||||
|
/// </summary>
|
||||||
|
ReturnData,
|
||||||
|
/// <summary>
|
||||||
|
/// 下一步要执行的节点
|
||||||
|
/// </summary>
|
||||||
|
NextStep,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,13 @@ namespace Serein.Library
|
|||||||
public partial class MethodDetails
|
public partial class MethodDetails
|
||||||
{
|
{
|
||||||
private readonly IFlowEnvironment env;
|
private readonly IFlowEnvironment env;
|
||||||
private readonly NodeModelBase nodeModel;
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对应的节点
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo(IsProtection = true)]
|
||||||
|
private NodeModelBase _nodeModel;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否保护参数(目前仅视觉效果参数,不影响运行实现,后续将设置作用在运行逻辑中)
|
/// 是否保护参数(目前仅视觉效果参数,不影响运行实现,后续将设置作用在运行逻辑中)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -88,7 +94,7 @@ namespace Serein.Library
|
|||||||
/// <param name="nodeModel">标识属于哪个节点</param>
|
/// <param name="nodeModel">标识属于哪个节点</param>
|
||||||
public MethodDetails(IFlowEnvironment env, NodeModelBase nodeModel)
|
public MethodDetails(IFlowEnvironment env, NodeModelBase nodeModel)
|
||||||
{
|
{
|
||||||
this.nodeModel = nodeModel;
|
NodeModel = nodeModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -12,14 +12,19 @@ namespace Serein.Library
|
|||||||
[NodeProperty(ValuePath = NodeValuePath.DebugSetting)]
|
[NodeProperty(ValuePath = NodeValuePath.DebugSetting)]
|
||||||
public partial class NodeDebugSetting
|
public partial class NodeDebugSetting
|
||||||
{
|
{
|
||||||
private readonly NodeModelBase nodeModel;
|
/// <summary>
|
||||||
|
/// 对应的节点
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo(IsProtection = true)]
|
||||||
|
private NodeModelBase _nodeModel;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 创建属于某个节点的调试设置
|
/// 创建属于某个节点的调试设置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeModel"></param>
|
/// <param name="nodeModel"></param>
|
||||||
public NodeDebugSetting(NodeModelBase nodeModel)
|
public NodeDebugSetting(NodeModelBase nodeModel)
|
||||||
{
|
{
|
||||||
this.nodeModel = nodeModel;
|
NodeModel = nodeModel;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 是否使能
|
/// 是否使能
|
||||||
|
|||||||
@@ -14,61 +14,60 @@ namespace Serein.Library
|
|||||||
[NodeProperty(ValuePath = NodeValuePath.None)]
|
[NodeProperty(ValuePath = NodeValuePath.None)]
|
||||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 节点运行环境
|
||||||
|
/// </summary>
|
||||||
[PropertyInfo(IsProtection = true)]
|
[PropertyInfo(IsProtection = true)]
|
||||||
private IFlowEnvironment _env;
|
private IFlowEnvironment _env;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 标识节点对象全局唯一
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo(IsProtection = true)]
|
||||||
|
private string _guid;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 描述节点对应的控件类型
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo(IsProtection = true)]
|
||||||
|
private NodeControlType _controlType;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 在画布中的位置
|
/// 在画布中的位置
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo(IsProtection = true)]
|
[PropertyInfo(IsProtection = true)]
|
||||||
private PositionOfUI _position ;
|
private PositionOfUI _position ;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 显示名称
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo]
|
||||||
|
private string _displayName;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 是否为起点控件
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo]
|
||||||
|
private bool _isStart;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 附加的调试功能
|
/// 附加的调试功能
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo(IsProtection = true)]
|
[PropertyInfo(IsProtection = true)]
|
||||||
private NodeDebugSetting _debugSetting ;
|
private NodeDebugSetting _debugSetting ;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 描述节点对应的控件类型
|
|
||||||
/// </summary>
|
|
||||||
[PropertyInfo(IsProtection = true)]
|
|
||||||
private NodeControlType _controlType ;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 方法描述。不包含Method与委托,需要通过MethodName从环境中获取委托进行调用。
|
/// 方法描述。不包含Method与委托,需要通过MethodName从环境中获取委托进行调用。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo(IsProtection = true)]
|
[PropertyInfo(IsProtection = true)]
|
||||||
private MethodDetails _methodDetails ;
|
private MethodDetails _methodDetails ;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 标识节点对象全局唯一
|
|
||||||
/// </summary>
|
|
||||||
[PropertyInfo(IsProtection = true)]
|
|
||||||
private string _guid ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 显示名称
|
|
||||||
/// </summary>
|
|
||||||
[PropertyInfo]
|
|
||||||
private string _displayName ;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为起点控件
|
|
||||||
/// </summary>
|
|
||||||
[PropertyInfo]
|
|
||||||
private bool _isStart ;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 运行时的上一节点
|
/// 运行时的上一节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[PropertyInfo]
|
[PropertyInfo]
|
||||||
private NodeModelBase _previousNode ;
|
private NodeModelBase _previousNode ;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 当前节点执行完毕后需要执行的下一个分支的类别
|
/// 当前节点执行完毕后需要执行的下一个分支的类别
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -81,10 +80,11 @@ namespace Serein.Library
|
|||||||
[PropertyInfo]
|
[PropertyInfo]
|
||||||
private Exception _runingException ;
|
private Exception _runingException ;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||||
@@ -112,6 +112,7 @@ namespace Serein.Library
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Dictionary<ConnectionType, List<NodeModelBase>> SuccessorNodes { get; }
|
public Dictionary<ConnectionType, List<NodeModelBase>> SuccessorNodes { get; }
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 控制FlowData在同一时间只会被同一个线程更改。
|
/// 控制FlowData在同一时间只会被同一个线程更改。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -15,7 +15,13 @@ namespace Serein.Library
|
|||||||
public partial class ParameterDetails
|
public partial class ParameterDetails
|
||||||
{
|
{
|
||||||
private readonly IFlowEnvironment env;
|
private readonly IFlowEnvironment env;
|
||||||
private readonly NodeModelBase nodeModel;
|
|
||||||
|
/// <summary>
|
||||||
|
/// 对应的节点
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo(IsProtection = true)]
|
||||||
|
private NodeModelBase _nodeModel;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 参数索引
|
/// 参数索引
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -83,7 +89,7 @@ namespace Serein.Library
|
|||||||
public ParameterDetails(IFlowEnvironment env, NodeModelBase nodeModel)
|
public ParameterDetails(IFlowEnvironment env, NodeModelBase nodeModel)
|
||||||
{
|
{
|
||||||
this.env = env;
|
this.env = env;
|
||||||
this.nodeModel = nodeModel;
|
this.NodeModel = nodeModel;
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 通过参数信息加载实体,用于加载项目文件、远程连接的场景
|
/// 通过参数信息加载实体,用于加载项目文件、远程连接的场景
|
||||||
|
|||||||
@@ -244,26 +244,30 @@ namespace Serein.Library
|
|||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 节点于画布中的位置
|
/// 节点于画布中的位置(通用类)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PositionOfUI
|
[NodeProperty]
|
||||||
{ /// <summary>
|
public partial class PositionOfUI
|
||||||
/// 构造一个坐标
|
{
|
||||||
/// </summary>
|
|
||||||
public PositionOfUI()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 构造一个坐标
|
/// 构造一个坐标
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PositionOfUI(double x, double y)
|
public PositionOfUI(double x, double y)
|
||||||
{
|
{
|
||||||
X = x; Y = y;
|
_x = x; _y = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
public double X { get; set; } = 0;
|
/// <summary>
|
||||||
public double Y { get; set; } = 0;
|
/// 指示控件在画布的横向向方向上的位置
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo]
|
||||||
|
private double _x = 0;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 指示控件在画布的纵向方向上的位置
|
||||||
|
/// </summary>
|
||||||
|
[PropertyInfo]
|
||||||
|
private double _y = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -26,8 +26,21 @@ namespace Serein.Library.Utils
|
|||||||
result = default;
|
result = default;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 将字符串的字面量枚举值,转为对应的枚举值
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="TEnum">枚举</typeparam>
|
||||||
|
/// <param name="value">枚举字面量</param>
|
||||||
|
/// <returns>转换后的枚举值</returns>
|
||||||
|
public static TEnum ConvertEnum<TEnum>(this string value) where TEnum : struct, Enum
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(value) && Enum.TryParse(value, true, out TEnum tempResult) && Enum.IsDefined(typeof(TEnum), tempResult))
|
||||||
|
{
|
||||||
|
return tempResult;
|
||||||
|
}
|
||||||
|
throw new NotImplementedException($"枚举值转换失败:value({value})to enum ( {typeof(TEnum).FullName})");
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 从枚举值的 BindValueAttribute 特性中 获取绑定的参数(用于绑定了某些内容的枚举值)
|
/// 从枚举值的 BindValueAttribute 特性中 获取绑定的参数(用于绑定了某些内容的枚举值)
|
||||||
|
|||||||
@@ -249,7 +249,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// 环境加载的节点集合
|
/// 环境加载的节点集合
|
||||||
/// Node Guid - Node Model
|
/// Node Guid - Node Model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Dictionary<string, NodeModelBase> Nodes { get; } = [];
|
private Dictionary<string, NodeModelBase> NodeModels { get; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 存放触发器节点(运行时全部调用)
|
/// 存放触发器节点(运行时全部调用)
|
||||||
@@ -340,7 +340,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
ChannelFlowInterrupt?.CancelAllTasks();
|
ChannelFlowInterrupt?.CancelAllTasks();
|
||||||
flowStarter = new FlowStarter();
|
flowStarter = new FlowStarter();
|
||||||
var nodes = Nodes.Values.ToList();
|
var nodes = NodeModels.Values.ToList();
|
||||||
|
|
||||||
List<MethodDetails> initMethods = [];
|
List<MethodDetails> initMethods = [];
|
||||||
List<MethodDetails> loadMethods = [];
|
List<MethodDetails> loadMethods = [];
|
||||||
@@ -417,7 +417,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
ChannelFlowInterrupt?.CancelAllTasks();
|
ChannelFlowInterrupt?.CancelAllTasks();
|
||||||
flowStarter?.Exit();
|
flowStarter?.Exit();
|
||||||
|
|
||||||
foreach (var node in Nodes.Values)
|
foreach (var node in NodeModels.Values)
|
||||||
{
|
{
|
||||||
if (node is not null)
|
if (node is not null)
|
||||||
{
|
{
|
||||||
@@ -588,7 +588,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
foreach (var childNodeGuid in item.childNodeGuids)
|
foreach (var childNodeGuid in item.childNodeGuids)
|
||||||
{
|
{
|
||||||
Nodes.TryGetValue(childNodeGuid, out NodeModelBase? childNode);
|
NodeModels.TryGetValue(childNodeGuid, out NodeModelBase? childNode);
|
||||||
if (childNode is null)
|
if (childNode is null)
|
||||||
{
|
{
|
||||||
// 节点尚未加载
|
// 节点尚未加载
|
||||||
@@ -621,35 +621,39 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
|
|
||||||
// 确定节点之间的连接关系
|
// 确定节点之间的连接关系
|
||||||
foreach (var nodeInfo in projectData.Nodes)
|
Task.Run(async () =>
|
||||||
{
|
{
|
||||||
if (!Nodes.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
|
await Task.Delay(777);
|
||||||
|
foreach (var nodeInfo in projectData.Nodes)
|
||||||
{
|
{
|
||||||
// 不存在对应的起始节点
|
if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
|
||||||
continue;
|
{
|
||||||
}
|
// 不存在对应的起始节点
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
List<(ConnectionType connectionType, string[] guids)> allToNodes = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
|
List<(ConnectionType connectionType, string[] guids)> allToNodes = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
|
||||||
(ConnectionType.IsFail, nodeInfo.FalseNodes),
|
(ConnectionType.IsFail, nodeInfo.FalseNodes),
|
||||||
(ConnectionType.IsError, nodeInfo.ErrorNodes),
|
(ConnectionType.IsError, nodeInfo.ErrorNodes),
|
||||||
(ConnectionType.Upstream, nodeInfo.UpstreamNodes)];
|
(ConnectionType.Upstream, nodeInfo.UpstreamNodes)];
|
||||||
|
|
||||||
List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
||||||
.Select(info => (info.connectionType,
|
.Select(info => (info.connectionType,
|
||||||
info.guids.Where(guid => Nodes.ContainsKey(guid)).Select(guid => Nodes[guid])
|
info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid])
|
||||||
.ToArray()))
|
.ToArray()))
|
||||||
.ToList();
|
.ToList();
|
||||||
// 遍历每种类型的节点分支(四种)
|
// 遍历每种类型的节点分支(四种)
|
||||||
foreach ((ConnectionType connectionType, NodeModelBase[] toNodes) item in fromNodes)
|
foreach ((ConnectionType connectionType, NodeModelBase[] toNodes) item in fromNodes)
|
||||||
{
|
|
||||||
// 遍历当前类型分支的节点(确认连接关系)
|
|
||||||
foreach (var toNode in item.toNodes)
|
|
||||||
{
|
{
|
||||||
ConnectNodeAsync(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
|
// 遍历当前类型分支的节点(确认连接关系)
|
||||||
|
foreach (var toNode in item.toNodes)
|
||||||
|
{
|
||||||
|
ConnectNodeAsync(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
SetStartNode(projectData.StartNode);
|
SetStartNode(projectData.StartNode);
|
||||||
UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()));
|
UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs()));
|
||||||
@@ -710,8 +714,8 @@ namespace Serein.NodeFlow.Env
|
|||||||
var projectData = new SereinProjectData()
|
var projectData = new SereinProjectData()
|
||||||
{
|
{
|
||||||
Librarys = Librarys.Values.Select(lib => lib.ToLibrary()).ToArray(),
|
Librarys = Librarys.Values.Select(lib => lib.ToLibrary()).ToArray(),
|
||||||
Nodes = Nodes.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
|
Nodes = NodeModels.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
|
||||||
StartNode = Nodes.Values.FirstOrDefault(it => it.IsStart)?.Guid,
|
StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid,
|
||||||
};
|
};
|
||||||
return Task.FromResult(projectData);
|
return Task.FromResult(projectData);
|
||||||
}
|
}
|
||||||
@@ -740,7 +744,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var groupedNodes = Nodes.Values
|
var groupedNodes = NodeModels.Values
|
||||||
.Where(node => node.MethodDetails is not null)
|
.Where(node => node.MethodDetails is not null)
|
||||||
.ToArray()
|
.ToArray()
|
||||||
.GroupBy(node => node.MethodDetails!.MethodName)
|
.GroupBy(node => node.MethodDetails!.MethodName)
|
||||||
@@ -749,7 +753,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
group => group.Count());
|
group => group.Count());
|
||||||
|
|
||||||
|
|
||||||
if (Nodes.Count == 0)
|
if (NodeModels.Count == 0)
|
||||||
{
|
{
|
||||||
return true; // 当前无节点,可以直接删除
|
return true; // 当前无节点,可以直接删除
|
||||||
}
|
}
|
||||||
@@ -877,7 +881,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 从集合中移除节点
|
// 从集合中移除节点
|
||||||
Nodes.Remove(nodeGuid);
|
NodeModels.Remove(nodeGuid);
|
||||||
UIContextOperation?.Invoke(() => OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid)));
|
UIContextOperation?.Invoke(() => OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid)));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -887,8 +891,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="fromNodeGuid">起始节点</param>
|
/// <param name="fromNodeGuid">起始节点</param>
|
||||||
/// <param name="toNodeGuid">目标节点</param>
|
/// <param name="toNodeGuid">目标节点</param>
|
||||||
|
/// <param name="fromNodeJunctionType">起始节点控制点</param>
|
||||||
|
/// <param name="toNodeJunctionType">目标节点控制点</param>
|
||||||
/// <param name="connectionType">连接关系</param>
|
/// <param name="connectionType">连接关系</param>
|
||||||
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionType connectionType)
|
||||||
{
|
{
|
||||||
// 获取起始节点与目标节点
|
// 获取起始节点与目标节点
|
||||||
var fromNode = GuidToModel(fromNodeGuid);
|
var fromNode = GuidToModel(fromNodeGuid);
|
||||||
@@ -978,9 +984,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// <param name="y"></param>
|
/// <param name="y"></param>
|
||||||
public void MoveNode(string nodeGuid, double x, double y)
|
public void MoveNode(string nodeGuid, double x, double y)
|
||||||
{
|
{
|
||||||
var nodeModel = GuidToModel(nodeGuid);
|
NodeModelBase? nodeModel = GuidToModel(nodeGuid);
|
||||||
if (nodeModel is null) return;
|
if (nodeModel is null) return;
|
||||||
nodeModel.Position.X = x;
|
nodeModel.Position.X = x;
|
||||||
|
|
||||||
nodeModel.Position.Y = y;
|
nodeModel.Position.Y = y;
|
||||||
UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y)));
|
UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y)));
|
||||||
|
|
||||||
@@ -1225,7 +1232,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
//throw new ArgumentNullException("not contains - Guid没有对应节点:" + (nodeGuid));
|
//throw new ArgumentNullException("not contains - Guid没有对应节点:" + (nodeGuid));
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (!Nodes.TryGetValue(nodeGuid, out NodeModelBase? nodeModel) || nodeModel is null)
|
if (!NodeModels.TryGetValue(nodeGuid, out NodeModelBase? nodeModel) || nodeModel is null)
|
||||||
{
|
{
|
||||||
//throw new ArgumentNullException("null - Guid存在对应节点,但节点为null:" + (nodeGuid));
|
//throw new ArgumentNullException("null - Guid存在对应节点,但节点为null:" + (nodeGuid));
|
||||||
return null;
|
return null;
|
||||||
@@ -1410,7 +1417,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
private bool TryAddNode(NodeModelBase nodeModel)
|
private bool TryAddNode(NodeModelBase nodeModel)
|
||||||
{
|
{
|
||||||
nodeModel.Guid ??= Guid.NewGuid().ToString();
|
nodeModel.Guid ??= Guid.NewGuid().ToString();
|
||||||
Nodes[nodeModel.Guid] = nodeModel;
|
NodeModels[nodeModel.Guid] = nodeModel;
|
||||||
|
|
||||||
// 如果是触发器,则需要添加到专属集合中
|
// 如果是触发器,则需要添加到专属集合中
|
||||||
if (nodeModel is SingleFlipflopNode flipflopNode)
|
if (nodeModel is SingleFlipflopNode flipflopNode)
|
||||||
|
|||||||
@@ -180,9 +180,9 @@ namespace Serein.NodeFlow.Env
|
|||||||
currentFlowEnvironment.ClearAll();
|
currentFlowEnvironment.ClearAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionType connectionType)
|
||||||
{
|
{
|
||||||
return await currentFlowEnvironment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, connectionType);
|
return await currentFlowEnvironment.ConnectNodeAsync(fromNodeGuid, toNodeGuid, fromNodeJunctionType, toNodeJunctionType, connectionType);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
|
public async Task<(bool, RemoteEnvControl)> ConnectRemoteEnv(string addres, int port, string token)
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
/// 环境加载的节点集合
|
/// 环境加载的节点集合
|
||||||
/// Node Guid - Node Model
|
/// Node Guid - Node Model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private Dictionary<string, NodeModelBase> Nodes { get; } = [];
|
private Dictionary<string, NodeModelBase> NodeModels { get; } = [];
|
||||||
|
|
||||||
public event LoadDllHandler OnDllLoad;
|
public event LoadDllHandler OnDllLoad;
|
||||||
public event ProjectLoadedHandler OnProjectLoaded;
|
public event ProjectLoadedHandler OnProjectLoaded;
|
||||||
@@ -173,7 +173,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
{
|
{
|
||||||
foreach (var childNodeGuid in item.childNodeGuids)
|
foreach (var childNodeGuid in item.childNodeGuids)
|
||||||
{
|
{
|
||||||
Nodes.TryGetValue(childNodeGuid, out NodeModelBase? childNode);
|
NodeModels.TryGetValue(childNodeGuid, out NodeModelBase? childNode);
|
||||||
if (childNode is null)
|
if (childNode is null)
|
||||||
{
|
{
|
||||||
// 节点尚未加载
|
// 节点尚未加载
|
||||||
@@ -211,7 +211,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
await Task.Delay(250);
|
await Task.Delay(250);
|
||||||
foreach (var nodeInfo in projectData.Nodes)
|
foreach (var nodeInfo in projectData.Nodes)
|
||||||
{
|
{
|
||||||
if (!Nodes.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
|
if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode))
|
||||||
{
|
{
|
||||||
// 不存在对应的起始节点
|
// 不存在对应的起始节点
|
||||||
continue;
|
continue;
|
||||||
@@ -225,7 +225,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
List<(ConnectionType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0)
|
||||||
.Select(info => (info.connectionType,
|
.Select(info => (info.connectionType,
|
||||||
info.guids.Where(guid => Nodes.ContainsKey(guid)).Select(guid => Nodes[guid])
|
info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid])
|
||||||
.ToArray()))
|
.ToArray()))
|
||||||
.ToList();
|
.ToList();
|
||||||
// 遍历每种类型的节点分支(四种)
|
// 遍历每种类型的节点分支(四种)
|
||||||
@@ -259,7 +259,7 @@ namespace Serein.NodeFlow.Env
|
|||||||
private bool TryAddNode(NodeModelBase nodeModel)
|
private bool TryAddNode(NodeModelBase nodeModel)
|
||||||
{
|
{
|
||||||
//nodeModel.Guid ??= Guid.NewGuid().ToString();
|
//nodeModel.Guid ??= Guid.NewGuid().ToString();
|
||||||
Nodes[nodeModel.Guid] = nodeModel;
|
NodeModels[nodeModel.Guid] = nodeModel;
|
||||||
|
|
||||||
// 如果是触发器,则需要添加到专属集合中
|
// 如果是触发器,则需要添加到专属集合中
|
||||||
//if (nodeModel is SingleFlipflopNode flipflopNode)
|
//if (nodeModel is SingleFlipflopNode flipflopNode)
|
||||||
@@ -395,10 +395,10 @@ namespace Serein.NodeFlow.Env
|
|||||||
|
|
||||||
public void MoveNode(string nodeGuid, double x, double y)
|
public void MoveNode(string nodeGuid, double x, double y)
|
||||||
{
|
{
|
||||||
UIContextOperation.Invoke(() =>
|
//UIContextOperation?.Invoke(() =>
|
||||||
{
|
//{
|
||||||
OnNodeMoved.Invoke(new NodeMovedEventArgs(nodeGuid, x, y));
|
// OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y));
|
||||||
});
|
//});
|
||||||
_ = msgClient.SendAsync(EnvMsgTheme.MoveNode,
|
_ = msgClient.SendAsync(EnvMsgTheme.MoveNode,
|
||||||
new
|
new
|
||||||
{
|
{
|
||||||
@@ -406,6 +406,12 @@ namespace Serein.NodeFlow.Env
|
|||||||
x,
|
x,
|
||||||
y
|
y
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if(NodeModels.TryGetValue(nodeGuid, out var nodeModel))
|
||||||
|
{
|
||||||
|
nodeModel.Position.X = x;
|
||||||
|
nodeModel.Position.Y = y;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -418,12 +424,14 @@ namespace Serein.NodeFlow.Env
|
|||||||
//UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid,nodeGuid)));
|
//UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(nodeGuid,nodeGuid)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType)
|
public async Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionType connectionType)
|
||||||
{
|
{
|
||||||
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ConnectNode, new
|
var result = await msgClient.SendAndWaitDataAsync<bool>(EnvMsgTheme.ConnectNode, new
|
||||||
{
|
{
|
||||||
fromNodeGuid,
|
fromNodeGuid,
|
||||||
toNodeGuid,
|
toNodeGuid,
|
||||||
|
fromNodeJunctionType = fromNodeJunctionType.ToString(),
|
||||||
|
toNodeJunctionType = toNodeJunctionType.ToString(),
|
||||||
connectionType = connectionType.ToString(),
|
connectionType = connectionType.ToString(),
|
||||||
});
|
});
|
||||||
if (result)
|
if (result)
|
||||||
|
|||||||
@@ -335,6 +335,7 @@ namespace Serein.NodeFlow
|
|||||||
}
|
}
|
||||||
dictGlobalFlipflop.Clear();
|
dictGlobalFlipflop.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 启动全局触发器
|
/// 启动全局触发器
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -130,9 +130,12 @@ namespace Serein.Library.NodeGenerator
|
|||||||
sb.AppendLine($" public partial class {className} : System.ComponentModel.INotifyPropertyChanged");
|
sb.AppendLine($" public partial class {className} : System.ComponentModel.INotifyPropertyChanged");
|
||||||
sb.AppendLine(" {");
|
sb.AppendLine(" {");
|
||||||
|
|
||||||
var path = classInfo[nameof(NodePropertyAttribute)]["ValuePath"];
|
//object path = null ;
|
||||||
|
//if(classInfo.TryGetValue(nameof(NodePropertyAttribute), out var values))
|
||||||
|
//{
|
||||||
|
// values.TryGetValue(nameof(NodePropertyAttribute.ValuePath), out path); // 获取路径
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
|
||||||
@@ -161,8 +164,6 @@ namespace Serein.Library.NodeGenerator
|
|||||||
|
|
||||||
var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), "true"); // 是否为保护字段
|
var isProtection = attributeInfo.Search(nameof(PropertyInfo), nameof(PropertyInfo.IsProtection), "true"); // 是否为保护字段
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 生成 getter / setter
|
// 生成 getter / setter
|
||||||
sb.AppendLine(leadingTrivia);
|
sb.AppendLine(leadingTrivia);
|
||||||
sb.AppendLine($" public {fieldType} {propertyName}");
|
sb.AppendLine($" public {fieldType} {propertyName}");
|
||||||
@@ -184,15 +185,15 @@ namespace Serein.Library.NodeGenerator
|
|||||||
}
|
}
|
||||||
else if (classInfo.ExitsPath(nameof(NodeValuePath.Method))) // 节点方法详情
|
else if (classInfo.ExitsPath(nameof(NodeValuePath.Method))) // 节点方法详情
|
||||||
{
|
{
|
||||||
sb.AddCode(5, $"nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"MethodDetails.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了");
|
sb.AddCode(5, $"NodeModel?.Env?.NotificationNodeValueChangeAsync(NodeModel.Guid, \"MethodDetails.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了");
|
||||||
}
|
}
|
||||||
else if (classInfo.ExitsPath(nameof(NodeValuePath.Parameter))) // 节点方法入参参数描述
|
else if (classInfo.ExitsPath(nameof(NodeValuePath.Parameter))) // 节点方法入参参数描述
|
||||||
{
|
{
|
||||||
sb.AddCode(5, "nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"MethodDetails.ParameterDetailss[\"+$\"{Index}\"+\"]." + $"\"+nameof({propertyName}),value); // 通知远程环境属性发生改变了");
|
sb.AddCode(5, "NodeModel?.Env?.NotificationNodeValueChangeAsync(NodeModel.Guid, \"MethodDetails.ParameterDetailss[\"+$\"{Index}\"+\"]." + $"\"+nameof({propertyName}),value); // 通知远程环境属性发生改变了");
|
||||||
}
|
}
|
||||||
else if (classInfo.ExitsPath(nameof(NodeValuePath.DebugSetting))) // 节点的调试信息
|
else if (classInfo.ExitsPath(nameof(NodeValuePath.DebugSetting))) // 节点的调试信息
|
||||||
{
|
{
|
||||||
sb.AddCode(5, $"nodeModel?.Env?.NotificationNodeValueChangeAsync(nodeModel.Guid, \"DebugSetting.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了");
|
sb.AddCode(5, $"NodeModel?.Env?.NotificationNodeValueChangeAsync(NodeModel.Guid, \"DebugSetting.\"+nameof({propertyName}), value); // 通知远程环境属性发生改变了");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); // 通知UI属性发生改变了");
|
sb.AppendLine($" SetProperty<{fieldType}>(ref {fieldName}, value); // 通知UI属性发生改变了");
|
||||||
@@ -223,20 +224,21 @@ namespace Serein.Library.NodeGenerator
|
|||||||
sb.AppendLine(" } ");
|
sb.AppendLine(" } ");
|
||||||
sb.AppendLine(" ");
|
sb.AppendLine(" ");
|
||||||
sb.AppendLine(" storage = value; ");
|
sb.AppendLine(" storage = value; ");
|
||||||
sb.AppendLine(" OnPropertyChanged(propertyName); ");
|
//sb.AppendLine(" OnPropertyChanged(propertyName); ");
|
||||||
|
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
|
||||||
sb.AppendLine(" } ");
|
sb.AppendLine(" } ");
|
||||||
|
|
||||||
sb.AppendLine(" /// <summary> ");
|
//sb.AppendLine(" /// <summary> ");
|
||||||
sb.AppendLine(" /// 略 ");
|
//sb.AppendLine(" /// 略 ");
|
||||||
sb.AppendLine(" /// <para>此方法为自动生成</para> ");
|
//sb.AppendLine(" /// <para>此方法为自动生成</para> ");
|
||||||
sb.AppendLine(" /// </summary>");
|
//sb.AppendLine(" /// </summary>");
|
||||||
sb.AppendLine(" /// <param name=\"propertyName\"></param> ");
|
//sb.AppendLine(" /// <param name=\"propertyName\"></param> ");
|
||||||
sb.AppendLine(" ");
|
//sb.AppendLine(" ");
|
||||||
sb.AppendLine(" protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) ");
|
//sb.AppendLine(" protected void OnPropertyChanged([System.Runtime.CompilerServices.CallerMemberName] string propertyName = null) ");
|
||||||
sb.AppendLine(" {");
|
//sb.AppendLine(" {");
|
||||||
//sb.AppendLine(" Console.WriteLine(\"测试:\"+ propertyName);");
|
//sb.AppendLine(" Console.WriteLine(\"测试:\"+ propertyName);");
|
||||||
sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
|
//sb.AppendLine(" PropertyChanged?.Invoke(this, new System.ComponentModel.PropertyChangedEventArgs(propertyName)); ");
|
||||||
sb.AppendLine(" }");
|
//sb.AppendLine(" }");
|
||||||
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
<!--<ResourceDictionary Source="/Themes/ExplicitDataControl.xaml" />-->
|
<!--<ResourceDictionary Source="/Themes/ExplicitDataControl.xaml" />-->
|
||||||
<ResourceDictionary Source="/Themes/MethodDetailsControl.xaml" />
|
<ResourceDictionary Source="/Themes/MethodDetailsControl.xaml" />
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</ResourceDictionary.MergedDictionaries>
|
</ResourceDictionary.MergedDictionaries>
|
||||||
|
|
||||||
<Style TargetType="{x:Type TextBlock }">
|
<Style TargetType="{x:Type TextBlock }">
|
||||||
|
|||||||
@@ -10,18 +10,25 @@ namespace Serein.Workbench
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class App : Application
|
public partial class App : Application
|
||||||
{
|
{
|
||||||
#if DEBUG
|
void LoadLocalProject()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
if (1 == 1)
|
||||||
|
{
|
||||||
|
string filePath;
|
||||||
|
filePath = @"F:\临时\project\linux\project.dnf";
|
||||||
|
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
|
||||||
|
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
||||||
|
App.FileDataPath = System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
public static SereinProjectData? FlowProjectData { get; set; }
|
public static SereinProjectData? FlowProjectData { get; set; }
|
||||||
public static string FileDataPath { get; set; } = "";
|
public static string FileDataPath { get; set; } = "";
|
||||||
|
|
||||||
public App()
|
public App()
|
||||||
{
|
{
|
||||||
// TestExp();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,28 +73,11 @@ namespace Serein.Workbench
|
|||||||
Shutdown(); // 关闭应用程序
|
Shutdown(); // 关闭应用程序
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
this.LoadLocalProject();
|
||||||
#if DEBUG
|
|
||||||
else if(1 == 11)
|
|
||||||
{
|
|
||||||
//string filePath = @"F:\临时\project\new project.dnf";
|
|
||||||
|
|
||||||
string filePath;
|
|
||||||
//filePath = @"F:\临时\project\tmp\project.dnf";
|
|
||||||
//filePath = @"D:\Project\C#\TestNetFramework\Net45DllTest\Net45DllTest\bin\Debug\project.dnf";
|
|
||||||
//filePath = @"D:\Project\C#\DynamicControl\SereinFlow\Net462DllTest\bin\Debug\project.dnf";
|
|
||||||
//filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\project.dnf";
|
|
||||||
filePath = @"F:\临时\project\linux\project.dnf";
|
|
||||||
//string filePath = @"D:\Project\C#\DynamicControl\SereinFlow\.Output\Debug\net8.0-windows7.0\U9 project.dnf";
|
|
||||||
string content = System.IO.File.ReadAllText(filePath); // 读取整个文件内容
|
|
||||||
App.FlowProjectData = JsonConvert.DeserializeObject<SereinProjectData>(content);
|
|
||||||
App.FileDataPath =System.IO.Path.GetDirectoryName(filePath)!; // filePath;//
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if DEBUG && false
|
#if DEBUG && false
|
||||||
|
|
||||||
@@ -190,5 +180,3 @@ namespace Serein.Workbench
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -101,13 +101,14 @@
|
|||||||
<Grid Grid.Row="1" Grid.Column="2" x:Name="FlowChartStackGrid">
|
<Grid Grid.Row="1" Grid.Column="2" x:Name="FlowChartStackGrid">
|
||||||
|
|
||||||
<StackPanel x:Name="FlowChartStackPanel"
|
<StackPanel x:Name="FlowChartStackPanel"
|
||||||
|
|
||||||
ClipToBounds="True">
|
ClipToBounds="True">
|
||||||
<Canvas
|
<Canvas
|
||||||
x:Name="FlowChartCanvas"
|
x:Name="FlowChartCanvas"
|
||||||
Background="#E1FBEA"
|
Background="#E1FBEA"
|
||||||
AllowDrop="True"
|
AllowDrop="True"
|
||||||
Width="700"
|
Width="2000"
|
||||||
Height="700"
|
Height="2000"
|
||||||
MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown"
|
MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown"
|
||||||
MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp"
|
MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp"
|
||||||
MouseDown="FlowChartCanvas_MouseDown"
|
MouseDown="FlowChartCanvas_MouseDown"
|
||||||
|
|||||||
@@ -16,7 +16,6 @@ using System.Windows.Input;
|
|||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using System.Windows.Media.Animation;
|
using System.Windows.Media.Animation;
|
||||||
using System.Windows.Shapes;
|
using System.Windows.Shapes;
|
||||||
using System.Windows.Threading;
|
|
||||||
using DataObject = System.Windows.DataObject;
|
using DataObject = System.Windows.DataObject;
|
||||||
|
|
||||||
namespace Serein.Workbench
|
namespace Serein.Workbench
|
||||||
@@ -66,7 +65,7 @@ namespace Serein.Workbench
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 存储所有的连接。考虑集成在运行环境中。
|
/// 存储所有的连接。考虑集成在运行环境中。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<Connection> Connections { get; } = [];
|
private List<ConnectionControl> Connections { get; } = [];
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 起始节点
|
/// 起始节点
|
||||||
@@ -198,7 +197,6 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
EnvDecorator.OnNodeLocated += FlowEnvironment_OnNodeLocate;
|
EnvDecorator.OnNodeLocated += FlowEnvironment_OnNodeLocate;
|
||||||
EnvDecorator.OnNodeMoved += FlowEnvironment_OnNodeMoved;
|
EnvDecorator.OnNodeMoved += FlowEnvironment_OnNodeMoved;
|
||||||
|
|
||||||
EnvDecorator.OnEnvOut += FlowEnvironment_OnEnvOut;
|
EnvDecorator.OnEnvOut += FlowEnvironment_OnEnvOut;
|
||||||
this.EnvDecorator.SetConsoleOut(); // 设置输出
|
this.EnvDecorator.SetConsoleOut(); // 设置输出
|
||||||
}
|
}
|
||||||
@@ -222,7 +220,6 @@ namespace Serein.Workbench
|
|||||||
EnvDecorator.OnInterruptTrigger -= FlowEnvironment_OnInterruptTrigger;
|
EnvDecorator.OnInterruptTrigger -= FlowEnvironment_OnInterruptTrigger;
|
||||||
|
|
||||||
EnvDecorator.OnIOCMembersChanged -= FlowEnvironment_OnIOCMembersChanged;
|
EnvDecorator.OnIOCMembersChanged -= FlowEnvironment_OnIOCMembersChanged;
|
||||||
|
|
||||||
EnvDecorator.OnNodeLocated -= FlowEnvironment_OnNodeLocate;
|
EnvDecorator.OnNodeLocated -= FlowEnvironment_OnNodeLocate;
|
||||||
EnvDecorator.OnNodeMoved -= FlowEnvironment_OnNodeMoved;
|
EnvDecorator.OnNodeMoved -= FlowEnvironment_OnNodeMoved;
|
||||||
|
|
||||||
@@ -253,7 +250,7 @@ namespace Serein.Workbench
|
|||||||
InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Height);// 设置画布大小
|
InitializeCanvas(project.Basic.Canvas.Width, project.Basic.Canvas.Height);// 设置画布大小
|
||||||
foreach (var connection in Connections)
|
foreach (var connection in Connections)
|
||||||
{
|
{
|
||||||
connection.Refresh();
|
connection.AddOrRefreshLine(); // 窗体完成加载后试图刷新所有连接线
|
||||||
}
|
}
|
||||||
|
|
||||||
var canvasData = project.Basic.Canvas;
|
var canvasData = project.Basic.Canvas;
|
||||||
@@ -371,26 +368,17 @@ namespace Serein.Workbench
|
|||||||
if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接
|
if (eventArgs.ChangeType == NodeConnectChangeEventArgs.ConnectChangeType.Create) // 添加连接
|
||||||
{
|
{
|
||||||
// 添加连接
|
// 添加连接
|
||||||
var connection = new Connection
|
var connection = new ConnectionControl(EnvDecorator, FlowChartCanvas, connectionType, fromNode, toNode);
|
||||||
{
|
|
||||||
Start = fromNode,
|
|
||||||
End = toNode,
|
|
||||||
Type = connectionType
|
|
||||||
};
|
|
||||||
if (toNode is FlipflopNodeControl flipflopControl
|
if (toNode is FlipflopNodeControl flipflopControl
|
||||||
&& flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
|
&& flipflopControl?.ViewModel?.NodeModel is NodeModelBase nodeModel) // 某个节点连接到了触发器,尝试从全局触发器视图中移除该触发器
|
||||||
{
|
{
|
||||||
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
|
NodeTreeViewer.RemoteGlobalFlipFlop(nodeModel); // 从全局触发器树树视图中移除
|
||||||
}
|
}
|
||||||
|
connection.InvalidateVisual(); // 添加贝塞尔曲线显示
|
||||||
BsControl.Draw(FlowChartCanvas, connection); // 添加贝塞尔曲线显示
|
|
||||||
ConfigureLineContextMenu(connection); // 设置连接右键事件
|
|
||||||
Connections.Add(connection);
|
Connections.Add(connection);
|
||||||
EndConnection();
|
EndConnection();
|
||||||
connection.Refresh();
|
|
||||||
//UpdateConnections(fromNode);
|
|
||||||
// connection.ArrowPath?.InvalidateVisual();
|
|
||||||
// connection.BezierPath?.InvalidateVisual();
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -494,7 +482,7 @@ namespace Serein.Workbench
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!TryPlaceNodeInRegion(nodeControl, position)) // 将节点放置在区域中
|
if (!TryPlaceNodeInRegion(nodeControl, position)) // 判断是否为区域,如果是,将节点放置在区域中
|
||||||
{
|
{
|
||||||
PlaceNodeOnCanvas(nodeControl, position.X, position.Y); // 将节点放置在画布上
|
PlaceNodeOnCanvas(nodeControl, position.X, position.Y); // 将节点放置在画布上
|
||||||
}
|
}
|
||||||
@@ -736,26 +724,22 @@ namespace Serein.Workbench
|
|||||||
private void FlowEnvironment_OnNodeMoved(NodeMovedEventArgs eventArgs)
|
private void FlowEnvironment_OnNodeMoved(NodeMovedEventArgs eventArgs)
|
||||||
{
|
{
|
||||||
if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl)) return;
|
if (!TryGetControl(eventArgs.NodeGuid, out var nodeControl)) return;
|
||||||
|
|
||||||
var newLeft = eventArgs.X;
|
|
||||||
var newTop = eventArgs.Y;
|
|
||||||
|
|
||||||
// 限制控件不超出FlowChartCanvas的边界
|
|
||||||
if (newLeft >= 0 && newLeft + nodeControl.ActualWidth <= FlowChartCanvas.ActualWidth)
|
|
||||||
{
|
|
||||||
Canvas.SetLeft(nodeControl, newLeft);
|
|
||||||
|
|
||||||
}
|
|
||||||
if (newTop >= 0 && newTop + nodeControl.ActualHeight <= FlowChartCanvas.ActualHeight)
|
|
||||||
{
|
|
||||||
Canvas.SetTop(nodeControl, newTop);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
UpdateConnections(nodeControl);
|
UpdateConnections(nodeControl);
|
||||||
|
|
||||||
//Canvas.SetLeft(nodeControl,);
|
//var newLeft = eventArgs.X;
|
||||||
//Canvas.SetTop(nodeControl, );
|
//var newTop = eventArgs.Y;
|
||||||
|
//// 限制控件不超出FlowChartCanvas的边界
|
||||||
|
//if (newLeft >= 0 && newLeft + nodeControl.ActualWidth <= FlowChartCanvas.ActualWidth)
|
||||||
|
//{
|
||||||
|
// Canvas.SetLeft(nodeControl, newLeft);
|
||||||
|
|
||||||
|
//}
|
||||||
|
//if (newTop >= 0 && newTop + nodeControl.ActualHeight <= FlowChartCanvas.ActualHeight)
|
||||||
|
//{
|
||||||
|
// Canvas.SetTop(nodeControl, newTop);
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -874,6 +858,7 @@ namespace Serein.Workbench
|
|||||||
nodeControl.MouseLeftButtonUp += Block_MouseLeftButtonUp;
|
nodeControl.MouseLeftButtonUp += Block_MouseLeftButtonUp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 开始创建连接 True线 操作,设置起始块和绘制连接线。
|
/// 开始创建连接 True线 操作,设置起始块和绘制连接线。
|
||||||
@@ -1023,39 +1008,6 @@ namespace Serein.Workbench
|
|||||||
nodeControl.ContextMenu = contextMenu;
|
nodeControl.ContextMenu = contextMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 配置连接曲线的右键菜单
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="line"></param>
|
|
||||||
private void ConfigureLineContextMenu(Connection connection)
|
|
||||||
{
|
|
||||||
var contextMenu = new ContextMenu();
|
|
||||||
contextMenu.Items.Add(CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
|
|
||||||
if (connection.ArrowPath is null || connection.BezierPath is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
connection.ArrowPath.ContextMenu = contextMenu;
|
|
||||||
connection.BezierPath.ContextMenu = contextMenu;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 删除该连线
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="line"></param>
|
|
||||||
private void DeleteConnection(Connection connection)
|
|
||||||
{
|
|
||||||
var connectionToRemove = connection;
|
|
||||||
if (connectionToRemove is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 获取起始节点与终止节点,消除映射关系
|
|
||||||
var fromNodeGuid = connectionToRemove.Start.ViewModel.NodeModel.Guid;
|
|
||||||
var toNodeGuid = connectionToRemove.End.ViewModel.NodeModel.Guid;
|
|
||||||
EnvDecorator.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connection.Type);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 查看返回类型(树形结构展开类型的成员)
|
/// 查看返回类型(树形结构展开类型的成员)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -1137,7 +1089,19 @@ namespace Serein.Workbench
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
|
private void FlowChartCanvas_MouseMove(object sender, MouseEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
if (e.LeftButton == MouseButtonState.Pressed && GlobalJunctionData.MyGlobalData is not null)
|
||||||
|
{
|
||||||
|
// 正在连接节点
|
||||||
|
var virtualLine = GlobalJunctionData.MyGlobalData.VirtualLine;
|
||||||
|
var controlPointPosition = GlobalJunctionData.MyGlobalData.StartPoint;
|
||||||
|
var currentPoint = e.GetPosition(FlowChartCanvas);
|
||||||
|
virtualLine.VirtualLine.X1 = controlPointPosition.X;
|
||||||
|
virtualLine.VirtualLine.Y1 = controlPointPosition.Y;
|
||||||
|
virtualLine.VirtualLine.X2 = currentPoint.X;
|
||||||
|
virtualLine.VirtualLine.Y2 = currentPoint.Y;
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (IsConnecting) // 正在连接节点
|
if (IsConnecting) // 正在连接节点
|
||||||
{
|
{
|
||||||
Point position = e.GetPosition(FlowChartCanvas);
|
Point position = e.GetPosition(FlowChartCanvas);
|
||||||
@@ -1151,7 +1115,7 @@ namespace Serein.Workbench
|
|||||||
currentLine.Y2 = position.Y;
|
currentLine.Y2 = position.Y;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsCanvasDragging && e.MiddleButton == MouseButtonState.Pressed) // 按住中键的同时进行画布的移动 IsCanvasDragging &&
|
if (IsCanvasDragging && e.MiddleButton == MouseButtonState.Pressed) // 正在移动画布(按住中键)
|
||||||
{
|
{
|
||||||
Point currentMousePosition = e.GetPosition(this);
|
Point currentMousePosition = e.GetPosition(this);
|
||||||
double deltaX = currentMousePosition.X - startCanvasDragPoint.X;
|
double deltaX = currentMousePosition.X - startCanvasDragPoint.X;
|
||||||
@@ -1164,7 +1128,7 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
foreach (var line in Connections)
|
foreach (var line in Connections)
|
||||||
{
|
{
|
||||||
line.Refresh();
|
line.AddOrRefreshLine(); // 画布移动时刷新所有连接线
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1253,7 +1217,7 @@ namespace Serein.Workbench
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 尝试将节点放置在区域中
|
/// 判断是否为区域,如果是,将节点放置在区域中
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeControl"></param>
|
/// <param name="nodeControl"></param>
|
||||||
/// <param name="position"></param>
|
/// <param name="position"></param>
|
||||||
@@ -1324,6 +1288,10 @@ namespace Serein.Workbench
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void Block_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
private void Block_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (GlobalJunctionData.IsCreatingConnection)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if(sender is NodeControlBase nodeControl)
|
if(sender is NodeControlBase nodeControl)
|
||||||
{
|
{
|
||||||
ChangeViewerObjOfNode(nodeControl);
|
ChangeViewerObjOfNode(nodeControl);
|
||||||
@@ -1332,9 +1300,7 @@ namespace Serein.Workbench
|
|||||||
startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置
|
startControlDragPoint = e.GetPosition(FlowChartCanvas); // 记录鼠标按下时的位置
|
||||||
((UIElement)sender).CaptureMouse(); // 捕获鼠标
|
((UIElement)sender).CaptureMouse(); // 捕获鼠标
|
||||||
e.Handled = true; // 防止事件传播影响其他控件
|
e.Handled = true; // 防止事件传播影响其他控件
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。
|
/// 控件的鼠标移动事件,根据鼠标拖动更新控件的位置。批量移动计算移动逻辑。
|
||||||
@@ -1409,7 +1375,7 @@ namespace Serein.Workbench
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 改变对象树?
|
||||||
private void ChangeViewerObjOfNode(NodeControlBase nodeControl)
|
private void ChangeViewerObjOfNode(NodeControlBase nodeControl)
|
||||||
{
|
{
|
||||||
var node = nodeControl.ViewModel.NodeModel;
|
var node = nodeControl.ViewModel.NodeModel;
|
||||||
@@ -1472,79 +1438,9 @@ namespace Serein.Workbench
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
EnvDecorator.ConnectNodeAsync(formNodeGuid, toNodeGuid, currentConnectionType);
|
EnvDecorator.ConnectNodeAsync(formNodeGuid, toNodeGuid, currentConnectionType);
|
||||||
|
|
||||||
}
|
}
|
||||||
/*else if (IsConnecting)
|
|
||||||
{
|
|
||||||
bool isRegion = false;
|
|
||||||
NodeControlBase? targetBlock;
|
|
||||||
|
|
||||||
if (sender is ActionNodeControl)
|
GlobalJunctionData.OK();
|
||||||
{
|
|
||||||
targetBlock = sender as ActionNodeControl; // 动作
|
|
||||||
}
|
|
||||||
else if (sender is ActionRegionControl)
|
|
||||||
{
|
|
||||||
targetBlock = sender as ActionRegionControl; // 组合动作
|
|
||||||
isRegion = true;
|
|
||||||
}
|
|
||||||
else if (sender is ConditionNodeControl)
|
|
||||||
{
|
|
||||||
targetBlock = sender as ConditionNodeControl; // 条件
|
|
||||||
}
|
|
||||||
else if (sender is ConditionRegionControl)
|
|
||||||
{
|
|
||||||
targetBlock = sender as ConditionRegionControl; // 组合条件
|
|
||||||
isRegion = true;
|
|
||||||
}
|
|
||||||
else if (sender is FlipflopNodeControl)
|
|
||||||
{
|
|
||||||
targetBlock = sender as FlipflopNodeControl; // 触发器
|
|
||||||
}
|
|
||||||
else if (sender is ExpOpNodeControl)
|
|
||||||
{
|
|
||||||
targetBlock = sender as ExpOpNodeControl; // 触发器
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
targetBlock = null;
|
|
||||||
}
|
|
||||||
if (targetBlock == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startConnectBlock != null && targetBlock != null && startConnectBlock != targetBlock)
|
|
||||||
{
|
|
||||||
|
|
||||||
var connection = new Connection { Start = startConnectBlock, End = targetBlock, Type = currentConnectionType };
|
|
||||||
|
|
||||||
if (currentConnectionType == ConnectionType.IsSucceed)
|
|
||||||
{
|
|
||||||
startConnectBlock.ViewModel.Node.SucceedBranch.Add(targetBlock.ViewModel.Node);
|
|
||||||
}
|
|
||||||
else if (currentConnectionType == ConnectionType.IsFail)
|
|
||||||
{
|
|
||||||
startConnectBlock.ViewModel.Node.FailBranch.Add(targetBlock.ViewModel.Node);
|
|
||||||
}
|
|
||||||
else if (currentConnectionType == ConnectionType.IsError)
|
|
||||||
{
|
|
||||||
startConnectBlock.ViewModel.Node.ErrorBranch.Add(targetBlock.ViewModel.Node);
|
|
||||||
}
|
|
||||||
else if (currentConnectionType == ConnectionType.Upstream)
|
|
||||||
{
|
|
||||||
startConnectBlock.ViewModel.Node.UpstreamBranch.Add(targetBlock.ViewModel.Node);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 保存连接关系
|
|
||||||
BsControl.Draw(FlowChartCanvas, connection);
|
|
||||||
ConfigureLineContextMenu(connection);
|
|
||||||
|
|
||||||
targetBlock.ViewModel.Node.PreviousNodes.Add(startConnectBlock.ViewModel.Node); // 将当前发起连接的节点,添加到被连接的节点的上一节点队列。(用于回溯)
|
|
||||||
connections.Add(connection);
|
|
||||||
}
|
|
||||||
EndConnection();
|
|
||||||
}*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -1577,13 +1473,13 @@ namespace Serein.Workbench
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 更新与指定控件相关的所有连接的位置。
|
/// 更新与指定控件相关的所有连接的位置。
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void UpdateConnections(UserControl block)
|
private void UpdateConnections(NodeControlBase nodeControl)
|
||||||
{
|
{
|
||||||
foreach (var connection in Connections)
|
foreach (var connection in Connections)
|
||||||
{
|
{
|
||||||
if (connection.Start == block || connection.End == block)
|
if (connection.Start == nodeControl || connection.End == nodeControl)
|
||||||
{
|
{
|
||||||
connection.Refresh();
|
connection.AddOrRefreshLine(); // 主动更新某个控件相关的所有连接线
|
||||||
//connection.RemoveFromCanvas();
|
//connection.RemoveFromCanvas();
|
||||||
//BezierLineDrawer.UpdateBezierLine(FlowChartCanvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
|
//BezierLineDrawer.UpdateBezierLine(FlowChartCanvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
|
||||||
}
|
}
|
||||||
@@ -1602,6 +1498,9 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
private void FlowChartCanvas_MouseUp(object sender, MouseButtonEventArgs e)
|
private void FlowChartCanvas_MouseUp(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (IsCanvasDragging)
|
if (IsCanvasDragging)
|
||||||
{
|
{
|
||||||
IsCanvasDragging = false;
|
IsCanvasDragging = false;
|
||||||
@@ -1789,6 +1688,10 @@ namespace Serein.Workbench
|
|||||||
/// <param name="e"></param>
|
/// <param name="e"></param>
|
||||||
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
private void FlowChartCanvas_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||||
{
|
{
|
||||||
|
if (GlobalJunctionData.IsCreatingConnection)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!IsSelectControl)
|
if (!IsSelectControl)
|
||||||
{
|
{
|
||||||
// 进入选取状态
|
// 进入选取状态
|
||||||
@@ -1840,7 +1743,30 @@ namespace Serein.Workbench
|
|||||||
FlowChartCanvas.ReleaseMouseCapture();
|
FlowChartCanvas.ReleaseMouseCapture();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建连线
|
||||||
|
if (GlobalJunctionData.MyGlobalData is not null)
|
||||||
|
{
|
||||||
|
var myData = GlobalJunctionData.MyGlobalData;
|
||||||
|
var canvas = this.FlowChartCanvas;
|
||||||
|
if (GlobalJunctionData.CanCreate)
|
||||||
|
{
|
||||||
|
//var startPoint = myDataType.StartPoint;
|
||||||
|
var currentendPoint = e.GetPosition(canvas); // 当前鼠标落点
|
||||||
|
var changingJunctionPosition = myData.ChangingJunction.TranslatePoint(new Point(0, 0), canvas);
|
||||||
|
var changingJunctionRect = new Rect(changingJunctionPosition, new Size(myData.ChangingJunction.Width, myData.ChangingJunction.Height));
|
||||||
|
|
||||||
|
if (changingJunctionRect.Contains(currentendPoint))
|
||||||
|
{
|
||||||
|
this.EnvDecorator.ConnectNodeAsync(myData.StartJunction.NodeGuid, myData.ChangingJunction.NodeGuid, ConnectionType.IsSucceed);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
GlobalJunctionData.OK();
|
||||||
|
}
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 完成选取操作
|
/// 完成选取操作
|
||||||
@@ -1941,21 +1867,21 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
#region 节点对齐 (有些小瑕疵)
|
#region 节点对齐 (有些小瑕疵)
|
||||||
|
|
||||||
public void UpdateConnectedLines()
|
//public void UpdateConnectedLines()
|
||||||
{
|
//{
|
||||||
//foreach (var nodeControl in selectNodeControls)
|
// //foreach (var nodeControl in selectNodeControls)
|
||||||
//{
|
// //{
|
||||||
// UpdateConnections(nodeControl);
|
// // UpdateConnections(nodeControl);
|
||||||
//}
|
// //}
|
||||||
this.Dispatcher.Invoke(() =>
|
// this.Dispatcher.Invoke(() =>
|
||||||
{
|
// {
|
||||||
foreach (var line in Connections)
|
// foreach (var line in Connections)
|
||||||
{
|
// {
|
||||||
line.Refresh();
|
// line.AddOrRefreshLine(); // 节点完成对齐
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
|
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
#region Plan A 群组对齐
|
#region Plan A 群组对齐
|
||||||
@@ -2236,9 +2162,9 @@ namespace Serein.Workbench
|
|||||||
#region 静态方法:创建节点,创建菜单子项,获取区域
|
#region 静态方法:创建节点,创建菜单子项,获取区域
|
||||||
|
|
||||||
|
|
||||||
private static TControl CreateNodeControl<TControl, TViewModel>(NodeModelBase model)
|
private static TNodeControl CreateNodeControl<TNodeControl, TNodeViewModel>(NodeModelBase model)
|
||||||
where TControl : NodeControlBase
|
where TNodeControl : NodeControlBase
|
||||||
where TViewModel : NodeControlViewModelBase
|
where TNodeViewModel : NodeControlViewModelBase
|
||||||
{
|
{
|
||||||
|
|
||||||
if (model is null)
|
if (model is null)
|
||||||
@@ -2249,11 +2175,15 @@ namespace Serein.Workbench
|
|||||||
{
|
{
|
||||||
model.Guid = Guid.NewGuid().ToString();
|
model.Guid = Guid.NewGuid().ToString();
|
||||||
}
|
}
|
||||||
var viewModel = Activator.CreateInstance(typeof(TViewModel), [model]);
|
var viewModel = Activator.CreateInstance(typeof(TNodeViewModel), [model]);
|
||||||
var controlObj = Activator.CreateInstance(typeof(TControl), [viewModel]);
|
var controlObj = Activator.CreateInstance(typeof(TNodeControl), [viewModel]);
|
||||||
if (controlObj is TControl control)
|
if (controlObj is TNodeControl nodeControl)
|
||||||
{
|
{
|
||||||
return control;
|
|
||||||
|
//nodeControl.ExecuteJunctionControl = new NodeExecuteJunctionControl(this);
|
||||||
|
|
||||||
|
|
||||||
|
return nodeControl;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -2316,7 +2246,7 @@ namespace Serein.Workbench
|
|||||||
/// <typeparam name="T"></typeparam>
|
/// <typeparam name="T"></typeparam>
|
||||||
/// <param name="element"></param>
|
/// <param name="element"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
private static T? GetParentOfType<T>(DependencyObject element) where T : DependencyObject
|
public static T? GetParentOfType<T>(DependencyObject element) where T : DependencyObject
|
||||||
{
|
{
|
||||||
while (element != null)
|
while (element != null)
|
||||||
{
|
{
|
||||||
@@ -2331,7 +2261,7 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region 节点数、IOC视图管理
|
#region 节点树、IOC视图管理
|
||||||
|
|
||||||
private void JudgmentFlipFlopNode(NodeControlBase nodeControl)
|
private void JudgmentFlipFlopNode(NodeControlBase nodeControl)
|
||||||
{
|
{
|
||||||
@@ -2708,388 +2638,14 @@ namespace Serein.Workbench
|
|||||||
|
|
||||||
#region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线
|
#region 创建两个控件之间的连接关系,在UI层面上显示为 带箭头指向的贝塞尔曲线
|
||||||
|
|
||||||
|
#region 拓展方法 Extension
|
||||||
public static class BsControl
|
|
||||||
{
|
|
||||||
public static Connection Draw(Canvas canvas, Connection connection)
|
|
||||||
{
|
|
||||||
connection.Canvas = canvas;
|
|
||||||
UpdateBezierLineInDragging(canvas, connection);
|
|
||||||
//MakeDraggable(canvas, connection, connection.Start);
|
|
||||||
//MakeDraggable(canvas, connection, connection.End);
|
|
||||||
|
|
||||||
if (connection.BezierPath is null)
|
|
||||||
{
|
|
||||||
connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
|
||||||
Canvas.SetZIndex(connection.BezierPath, -1);
|
|
||||||
canvas.Children.Add(connection.BezierPath);
|
|
||||||
}
|
|
||||||
if (connection.ArrowPath is null)
|
|
||||||
{
|
|
||||||
connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
|
||||||
Canvas.SetZIndex(connection.ArrowPath, -1);
|
|
||||||
canvas.Children.Add(connection.ArrowPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BezierLineDrawer.UpdateBezierLine(canvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
|
|
||||||
|
|
||||||
return connection;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static bool isUpdating = false; // 是否正在更新线条显示
|
|
||||||
|
|
||||||
|
|
||||||
// 拖动时重新绘制
|
#endregion
|
||||||
public static void UpdateBezierLineInDragging(Canvas canvas, Connection connection)
|
|
||||||
{
|
|
||||||
if (isUpdating)
|
|
||||||
return;
|
|
||||||
|
|
||||||
isUpdating = true;
|
|
||||||
|
|
||||||
canvas.Dispatcher.InvokeAsync(() =>
|
|
||||||
{
|
|
||||||
if (connection is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection.BezierPath is null)
|
|
||||||
{
|
|
||||||
connection.BezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
|
||||||
//Canvas.SetZIndex(connection.BezierPath, -1);
|
|
||||||
canvas.Children.Add(connection.BezierPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (connection.ArrowPath is null)
|
|
||||||
{
|
|
||||||
connection.ArrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(connection.Type), Fill = BezierLineDrawer.GetLineColor(connection.Type), StrokeThickness = 1 };
|
|
||||||
//Canvas.SetZIndex(connection.ArrowPath, -1);
|
|
||||||
canvas.Children.Add(connection.ArrowPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
BezierLineDrawer.UpdateBezierLine(canvas, connection.Start, connection.End, connection.BezierPath, connection.ArrowPath);
|
|
||||||
isUpdating = false;
|
|
||||||
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// private static Point clickPosition; // 当前点击事件
|
|
||||||
// private static bool isDragging = false; // 是否正在移动控件
|
|
||||||
//private static void MakeDraggable(Canvas canvas, Connection connection, UIElement element)
|
|
||||||
//{
|
|
||||||
// if (connection.IsSetEven)
|
|
||||||
// {
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// element.MouseLeftButtonDown += (sender, e) =>
|
|
||||||
// {
|
|
||||||
// isDragging = true;
|
|
||||||
// //clickPosition = e.GetPosition(element);
|
|
||||||
// //element.CaptureMouse();
|
|
||||||
// };
|
|
||||||
// element.MouseLeftButtonUp += (sender, e) =>
|
|
||||||
// {
|
|
||||||
// isDragging = false;
|
|
||||||
// //element.ReleaseMouseCapture();
|
|
||||||
// };
|
|
||||||
|
|
||||||
// element.MouseMove += (sender, e) =>
|
|
||||||
// {
|
|
||||||
// if (isDragging)
|
|
||||||
// {
|
|
||||||
// if (VisualTreeHelper.GetParent(element) is Canvas canvas)
|
|
||||||
// {
|
|
||||||
// Point currentPosition = e.GetPosition(canvas);
|
|
||||||
// double newLeft = currentPosition.X - clickPosition.X;
|
|
||||||
// double newTop = currentPosition.Y - clickPosition.Y;
|
|
||||||
|
|
||||||
// Canvas.SetLeft(element, newLeft);
|
|
||||||
// Canvas.SetTop(element, newTop);
|
|
||||||
// UpdateBezierLine(canvas, connection);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// };
|
|
||||||
|
|
||||||
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class Connection
|
|
||||||
{
|
|
||||||
public ConnectionType Type { get; set; }
|
|
||||||
public Canvas? Canvas { get; set; }// 贝塞尔曲线所在画布
|
|
||||||
|
|
||||||
public System.Windows.Shapes.Path? BezierPath { get; set; }// 贝塞尔曲线路径
|
|
||||||
public System.Windows.Shapes.Path? ArrowPath { get; set; } // 箭头路径
|
|
||||||
|
|
||||||
public required NodeControlBase Start { get; set; } // 起始
|
|
||||||
public required NodeControlBase End { get; set; } // 结束
|
|
||||||
|
|
||||||
|
|
||||||
public void RemoveFromCanvas()
|
|
||||||
{
|
|
||||||
if(Canvas != null)
|
|
||||||
{
|
|
||||||
Canvas.Children.Remove(BezierPath); // 移除线
|
|
||||||
Canvas.Children.Remove(ArrowPath); // 移除线
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 重新绘制
|
|
||||||
/// </summary>
|
|
||||||
public void Refresh()
|
|
||||||
{
|
|
||||||
if(Canvas is null || BezierPath is null || ArrowPath is null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BezierLineDrawer.UpdateBezierLine(Canvas, Start, End, BezierPath, ArrowPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public static class BezierLineDrawer
|
|
||||||
{
|
|
||||||
public enum Localhost
|
|
||||||
{
|
|
||||||
Left,
|
|
||||||
Right,
|
|
||||||
Top,
|
|
||||||
Bottom,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 绘制曲线
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="canvas">所在画布</param>
|
|
||||||
/// <param name="startElement">起始控件</param>
|
|
||||||
/// <param name="endElement">终点控件</param>
|
|
||||||
/// <param name="bezierPath">曲线</param>
|
|
||||||
/// <param name="arrowPath">箭头</param>
|
|
||||||
public static void UpdateBezierLine(Canvas canvas,
|
|
||||||
FrameworkElement startElement,
|
|
||||||
FrameworkElement endElement,
|
|
||||||
System.Windows.Shapes.Path bezierPath,
|
|
||||||
System.Windows.Shapes.Path arrowPath)
|
|
||||||
{
|
|
||||||
Point startPoint = startElement.TranslatePoint(new Point(startElement.ActualWidth / 2, startElement.ActualHeight / 2), canvas);
|
|
||||||
Point endPoint = CalculateEndpointOutsideElement(endElement, canvas, startPoint, out Localhost localhost);
|
|
||||||
// 根据终点位置决定起点位置 (位于控件的边缘)
|
|
||||||
startPoint = CalculateEdgePoint(startElement, localhost, canvas);
|
|
||||||
|
|
||||||
PathFigure pathFigure = new PathFigure { StartPoint = startPoint };
|
|
||||||
BezierSegment bezierSegment;
|
|
||||||
|
|
||||||
if (localhost == Localhost.Left || localhost == Localhost.Right)
|
|
||||||
{
|
|
||||||
bezierSegment = new BezierSegment
|
|
||||||
{
|
|
||||||
Point1 = new Point((startPoint.X + endPoint.X) / 2, startPoint.Y),
|
|
||||||
Point2 = new Point((startPoint.X + endPoint.X) / 2, endPoint.Y),
|
|
||||||
Point3 = endPoint,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else // if (localhost == Localhost.Top || localhost == Localhost.Bottom)
|
|
||||||
{
|
|
||||||
|
|
||||||
bezierSegment = new BezierSegment
|
|
||||||
{
|
|
||||||
Point1 = new Point(startPoint.X, (startPoint.Y + endPoint.Y) / 2),
|
|
||||||
Point2 = new Point(endPoint.X, (startPoint.Y + endPoint.Y) / 2),
|
|
||||||
Point3 = endPoint,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
var minZ = canvas.Children.OfType<UIElement>()//linq语句,取Zindex的最大值
|
|
||||||
.Select(x => Grid.GetZIndex(x))
|
|
||||||
.Min();
|
|
||||||
Grid.SetZIndex(bezierPath, minZ - 1);
|
|
||||||
// Canvas.SetZIndex(bezierPath, 0);
|
|
||||||
pathFigure.Segments.Add(bezierSegment);
|
|
||||||
|
|
||||||
PathGeometry pathGeometry = new PathGeometry();
|
|
||||||
pathGeometry.Figures.Add(pathFigure);
|
|
||||||
bezierPath.Data = pathGeometry;
|
|
||||||
|
|
||||||
Point arrowStartPoint = CalculateBezierTangent(startPoint, bezierSegment.Point3, bezierSegment.Point2, endPoint);
|
|
||||||
UpdateArrowPath(endPoint, arrowStartPoint, arrowPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Point CalculateBezierTangent(Point startPoint, Point controlPoint1, Point controlPoint2, Point endPoint)
|
|
||||||
{
|
|
||||||
double t = 11; // 末端点
|
|
||||||
|
|
||||||
// 计算贝塞尔曲线在 t = 1 处的一阶导数
|
|
||||||
double dx = 3 * Math.Pow(1 - t, 2) * (controlPoint1.X - startPoint.X) +
|
|
||||||
6 * (1 - t) * t * (controlPoint2.X - controlPoint1.X) +
|
|
||||||
3 * Math.Pow(t, 2) * (endPoint.X - controlPoint2.X);
|
|
||||||
|
|
||||||
double dy = 3 * Math.Pow(1 - t, 2) * (controlPoint1.Y - startPoint.Y) +
|
|
||||||
6 * (1 - t) * t * (controlPoint2.Y - controlPoint1.Y) +
|
|
||||||
3 * Math.Pow(t, 2) * (endPoint.Y - controlPoint2.Y);
|
|
||||||
|
|
||||||
// 返回切线向量
|
|
||||||
return new Point(dx, dy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 绘制箭头
|
|
||||||
private static void UpdateArrowPath(Point endPoint,
|
|
||||||
Point controlPoint,
|
|
||||||
System.Windows.Shapes.Path arrowPath)
|
|
||||||
{
|
|
||||||
|
|
||||||
double arrowLength = 10;
|
|
||||||
double arrowWidth = 5;
|
|
||||||
|
|
||||||
Vector direction = endPoint - controlPoint;
|
|
||||||
direction.Normalize();
|
|
||||||
|
|
||||||
Point arrowPoint1 = endPoint + direction * arrowLength + new Vector(-direction.Y, direction.X) * arrowWidth;
|
|
||||||
Point arrowPoint2 = endPoint + direction * arrowLength + new Vector(direction.Y, -direction.X) * arrowWidth;
|
|
||||||
|
|
||||||
PathFigure arrowFigure = new PathFigure { StartPoint = endPoint };
|
|
||||||
arrowFigure.Segments.Add(new LineSegment(arrowPoint1, true));
|
|
||||||
arrowFigure.Segments.Add(new LineSegment(arrowPoint2, true));
|
|
||||||
arrowFigure.Segments.Add(new LineSegment(endPoint, true));
|
|
||||||
|
|
||||||
PathGeometry arrowGeometry = new PathGeometry();
|
|
||||||
arrowGeometry.Figures.Add(arrowFigure);
|
|
||||||
|
|
||||||
arrowPath.Data = arrowGeometry;
|
|
||||||
|
|
||||||
}
|
|
||||||
// 计算起点位于控件边缘的四个中心点之一
|
|
||||||
private static Point CalculateEdgePoint(FrameworkElement element, Localhost localhost, Canvas canvas)
|
|
||||||
{
|
|
||||||
Point point = new Point();
|
|
||||||
|
|
||||||
switch (localhost)
|
|
||||||
{
|
|
||||||
case Localhost.Right:
|
|
||||||
point = new Point(0, element.ActualHeight / 2); // 左边中心
|
|
||||||
break;
|
|
||||||
case Localhost.Left:
|
|
||||||
point = new Point(element.ActualWidth, element.ActualHeight / 2); // 右边中心
|
|
||||||
break;
|
|
||||||
case Localhost.Bottom:
|
|
||||||
point = new Point(element.ActualWidth / 2, 0); // 上边中心
|
|
||||||
break;
|
|
||||||
case Localhost.Top:
|
|
||||||
point = new Point(element.ActualWidth / 2, element.ActualHeight); // 下边中心
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算角落
|
|
||||||
//switch (localhost)
|
|
||||||
//{
|
|
||||||
// case Localhost.Right:
|
|
||||||
// point = new Point(0, element.ActualHeight / 2); // 左边中心
|
|
||||||
// break;
|
|
||||||
// case Localhost.Left:
|
|
||||||
// point = new Point(element.ActualWidth, element.ActualHeight / 2); // 右边中心
|
|
||||||
// break;
|
|
||||||
// case Localhost.Bottom:
|
|
||||||
// point = new Point(element.ActualWidth / 2, 0); // 上边中心
|
|
||||||
// break;
|
|
||||||
// case Localhost.Top:
|
|
||||||
// point = new Point(element.ActualWidth / 2, element.ActualHeight); // 下边中心
|
|
||||||
// break;
|
|
||||||
//}
|
|
||||||
|
|
||||||
// 将相对控件的坐标转换到画布中的全局坐标
|
|
||||||
return element.TranslatePoint(point, canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 计算终点落点位置
|
|
||||||
private static Point CalculateEndpointOutsideElement(FrameworkElement element, Canvas canvas, Point startPoint, out Localhost localhost)
|
|
||||||
{
|
|
||||||
Point centerPoint = element.TranslatePoint(new Point(element.ActualWidth / 2, element.ActualHeight / 2), canvas);
|
|
||||||
Vector direction = centerPoint - startPoint;
|
|
||||||
direction.Normalize();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var tx = centerPoint.X - startPoint.X;
|
|
||||||
var ty = startPoint.Y - centerPoint.Y;
|
|
||||||
|
|
||||||
|
|
||||||
localhost = (tx < ty, Math.Abs(tx) > Math.Abs(ty)) switch
|
|
||||||
{
|
|
||||||
(true, true) => Localhost.Right,
|
|
||||||
(true, false) => Localhost.Bottom,
|
|
||||||
(false, true) => Localhost.Left,
|
|
||||||
(false, false) => Localhost.Top,
|
|
||||||
};
|
|
||||||
|
|
||||||
double halfWidth = element.ActualWidth / 2 + 10;
|
|
||||||
double halfHeight = element.ActualHeight / 2 + 10;
|
|
||||||
|
|
||||||
|
|
||||||
#region 固定中位
|
|
||||||
|
|
||||||
//if (localhost == Localhost.Left)
|
|
||||||
//{
|
|
||||||
// centerPoint.X -= halfWidth;
|
|
||||||
//}
|
|
||||||
//else if (localhost == Localhost.Right)
|
|
||||||
//{
|
|
||||||
// centerPoint.X -= -halfWidth;
|
|
||||||
//}
|
|
||||||
//else if (localhost == Localhost.Top)
|
|
||||||
//{
|
|
||||||
// centerPoint.Y -= halfHeight;
|
|
||||||
//}
|
|
||||||
//else if (localhost == Localhost.Bottom)
|
|
||||||
//{
|
|
||||||
// centerPoint.Y -= -halfHeight;
|
|
||||||
//}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region 落点自由移动
|
|
||||||
double margin = 0;
|
|
||||||
if (localhost == Localhost.Left)
|
|
||||||
{
|
|
||||||
centerPoint.X -= halfWidth;
|
|
||||||
centerPoint.Y -= direction.Y / (1 + Math.Abs(direction.X)) * halfHeight - margin;
|
|
||||||
}
|
|
||||||
else if (localhost == Localhost.Right)
|
|
||||||
{
|
|
||||||
centerPoint.X -= -halfWidth;
|
|
||||||
centerPoint.Y -= direction.Y / (1 + Math.Abs(direction.X)) * halfHeight - margin;
|
|
||||||
}
|
|
||||||
else if (localhost == Localhost.Top)
|
|
||||||
{
|
|
||||||
centerPoint.Y -= halfHeight;
|
|
||||||
centerPoint.X -= direction.X / (1 + Math.Abs(direction.Y)) * halfWidth - margin;
|
|
||||||
}
|
|
||||||
else if (localhost == Localhost.Bottom)
|
|
||||||
{
|
|
||||||
centerPoint.Y -= -halfHeight;
|
|
||||||
centerPoint.X -= direction.X / (1 + Math.Abs(direction.Y)) * halfWidth - margin;
|
|
||||||
}
|
|
||||||
#endregion
|
|
||||||
return centerPoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static SolidColorBrush GetLineColor(ConnectionType currentConnectionType)
|
|
||||||
{
|
|
||||||
return currentConnectionType switch
|
|
||||||
{
|
|
||||||
ConnectionType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")),
|
|
||||||
ConnectionType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
|
|
||||||
ConnectionType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
|
|
||||||
ConnectionType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
|
|
||||||
_ => throw new Exception(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,34 +1,41 @@
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System;
|
||||||
|
|
||||||
namespace Serein.Workbench.Node.ViewModel
|
namespace Serein.Workbench.Node.ViewModel
|
||||||
{
|
{
|
||||||
public abstract class NodeControlViewModelBase : INotifyPropertyChanged
|
public abstract class NodeControlViewModelBase
|
||||||
{
|
{
|
||||||
|
///// <summary>
|
||||||
|
///// 对应的节点实体类
|
||||||
|
///// </summary>
|
||||||
|
public NodeModelBase NodeModel { get; }
|
||||||
|
|
||||||
public NodeControlViewModelBase(NodeModelBase nodeModel)
|
public NodeControlViewModelBase(NodeModelBase nodeModel)
|
||||||
{
|
{
|
||||||
NodeModel = nodeModel;
|
NodeModel = nodeModel;
|
||||||
|
|
||||||
// 订阅来自 NodeModel 的通知事件
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private NodeModelBase _nodeModelBase;
|
|
||||||
/// <summary>
|
//private NodeModelBase _nodeModelBase;
|
||||||
/// 对应的节点实体类
|
|
||||||
/// </summary>
|
//public NodeModelBase NodeModel
|
||||||
public NodeModelBase NodeModel
|
//{
|
||||||
{
|
// get => _nodeModelBase; set
|
||||||
get => _nodeModelBase; set
|
// {
|
||||||
{
|
// if (value != null)
|
||||||
if (value != null)
|
// {
|
||||||
{
|
// _nodeModelBase = value;
|
||||||
_nodeModelBase = value;
|
// OnPropertyChanged();
|
||||||
OnPropertyChanged();
|
// }
|
||||||
}
|
// }
|
||||||
}
|
//}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//private bool isSelect;
|
//private bool isSelect;
|
||||||
@@ -61,12 +68,12 @@ namespace Serein.Workbench.Node.ViewModel
|
|||||||
// }
|
// }
|
||||||
//}
|
//}
|
||||||
|
|
||||||
public event PropertyChangedEventHandler? PropertyChanged;
|
//public event PropertyChangedEventHandler? PropertyChanged;
|
||||||
protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
//protected void OnPropertyChanged([CallerMemberName] string propertyName = null)
|
||||||
{
|
//{
|
||||||
//Console.WriteLine(propertyName);
|
// //Console.WriteLine(propertyName);
|
||||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
// PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||||
}
|
//}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1,19 +1,20 @@
|
|||||||
<local:NodeControlBase x:Class="Serein.Workbench.Node.View.ActionNodeControl"
|
<local:NodeControlBase x:Class="Serein.Workbench.Node.View.ActionNodeControl"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:local="clr-namespace:Serein.Workbench.Node.View"
|
xmlns:local="clr-namespace:Serein.Workbench.Node.View"
|
||||||
xmlns:vm="clr-namespace:Serein.Workbench.Node.ViewModel"
|
xmlns:vm="clr-namespace:Serein.Workbench.Node.ViewModel"
|
||||||
xmlns:Converters="clr-namespace:Serein.Workbench.Tool.Converters"
|
xmlns:Converters="clr-namespace:Serein.Workbench.Tool.Converters"
|
||||||
xmlns:themes="clr-namespace:Serein.Workbench.Themes"
|
xmlns:themes="clr-namespace:Serein.Workbench.Themes"
|
||||||
d:DataContext="{d:DesignInstance vm:ActionNodeControlViewModel}"
|
d:DataContext="{d:DesignInstance vm:ActionNodeControlViewModel}"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
MaxWidth="300">
|
MaxWidth="300">
|
||||||
<!--d:DataContext="{d:DesignData vm:ActionNodeControlViewModel}"-->
|
|
||||||
<UserControl.Resources>
|
<UserControl.Resources>
|
||||||
<!--<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />-->
|
<!--<BooleanToVisibilityConverter x:Key="BoolToVisConverter" />-->
|
||||||
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
<Converters:InvertableBooleanToVisibilityConverter x:Key="InvertedBoolConverter"/>
|
||||||
|
<!--<ResourceDictionary Source="/Serein.Workbench;Node/View/NodeExecuteJunctionControl.xaml" x:Key="NodeExecuteJunctionControl"/>-->
|
||||||
</UserControl.Resources>
|
</UserControl.Resources>
|
||||||
|
|
||||||
<Border BorderBrush="#8DE9FD" BorderThickness="1">
|
<Border BorderBrush="#8DE9FD" BorderThickness="1">
|
||||||
@@ -48,20 +49,33 @@
|
|||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
|
<Grid Grid.Row="0" Background="#8DE9FD" >
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<local:ExecuteJunctionControl Grid.Column="0" NodeGuid="{Binding NodeModel.Guid}" x:Name="ExecuteJunctionControl"/>
|
||||||
|
<TextBlock Grid.Column="1" Text="{Binding NodeModel.MethodDetails.MethodTips, Mode=TwoWay}" HorizontalAlignment="Center"/>
|
||||||
|
<local:ResultJunctionControl Grid.Column="2" NodeGuid="{Binding NodeModel.Guid}" x:Name="ResultJunctionControl"/>
|
||||||
|
|
||||||
|
</Grid>
|
||||||
|
|
||||||
<StackPanel Grid.Row="0" Orientation="Horizontal" Background="#8DE9FD">
|
|
||||||
<CheckBox IsChecked="{Binding NodeModel.DebugSetting.IsEnable, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
<!--<StackPanel Background="#8DE9FD" >
|
||||||
<CheckBox IsChecked="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay}" VerticalContentAlignment="Center"/>
|
|
||||||
<TextBlock Text="{Binding NodeModel.MethodDetails.MethodTips, Mode=TwoWay}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
</StackPanel>-->
|
||||||
</StackPanel>
|
|
||||||
<themes:MethodDetailsControl Grid.Row="1" MethodDetails="{Binding NodeModel.MethodDetails}"/>
|
<themes:MethodDetailsControl Grid.Row="2" MethodDetails="{Binding NodeModel.MethodDetails}"/>
|
||||||
<!-- ParameterProtectionMask 参数保护 -->
|
<!-- ParameterProtectionMask 参数保护 -->
|
||||||
<!--取反 Visibility="{Binding DebugSetting.IsEnable, Converter={StaticResource InvertedBoolConverter}, ConverterParameter=Inverted}"-->
|
<!--取反 Visibility="{Binding DebugSetting.IsEnable, Converter={StaticResource InvertedBoolConverter}, ConverterParameter=Inverted}"-->
|
||||||
<Border Grid.Row="1" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
|
<Border Grid.Row="2" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderBrush="#0A4651" BorderThickness="0"
|
||||||
Visibility="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay,
|
Visibility="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay,
|
||||||
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" />
|
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" />
|
||||||
<Grid Grid.Row="2" Background="#D5F0FC" >
|
<Grid Grid.Row="3" Background="#D5F0FC" >
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
@@ -70,10 +84,25 @@
|
|||||||
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
<TextBlock Text="result" HorizontalAlignment="Center" VerticalAlignment="Center" />
|
||||||
</Border>
|
</Border>
|
||||||
<Border Grid.Column="1" BorderThickness="1">
|
<Border Grid.Column="1" BorderThickness="1">
|
||||||
<TextBlock Text="{Binding NodeModel.MethodDetails.ReturnType, Mode=TwoWay}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<TextBlock Text="{Binding NodeModel.MethodDetails.ReturnType.FullName, Mode=OneTime}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
<Grid Grid.Row="4" Background="#8DE9FD" >
|
||||||
|
<Grid.RowDefinitions>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="*"/>
|
||||||
|
</Grid.RowDefinitions>
|
||||||
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
|
<ColumnDefinition Width="*"/>
|
||||||
|
</Grid.ColumnDefinitions>
|
||||||
|
<CheckBox Grid.Row="0" Grid.Column="0" IsChecked="{Binding NodeModel.DebugSetting.IsEnable, Mode=TwoWay}"/>
|
||||||
|
<TextBlock Grid.Row="0" Grid.Column="1" Text="是否使能" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
|
<CheckBox Grid.Row="1" Grid.Column="0" IsChecked="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay}"/>
|
||||||
|
<TextBlock Grid.Row="1" Grid.Column="1" Text="参数保护" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
|
</Grid>
|
||||||
|
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
</Border>
|
</Border>
|
||||||
|
|||||||
@@ -10,10 +10,12 @@ namespace Serein.Workbench.Node.View
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class ActionNodeControl : NodeControlBase
|
public partial class ActionNodeControl : NodeControlBase
|
||||||
{
|
{
|
||||||
public ActionNodeControl(ActionNodeControlViewModel viewModel):base(viewModel)
|
public ActionNodeControl(ActionNodeControlViewModel viewModel) : base(viewModel)
|
||||||
{
|
{
|
||||||
DataContext = viewModel;
|
DataContext = viewModel;
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
ExecuteJunctionControl.NodeGuid = viewModel.NodeModel.Guid;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -25,9 +25,11 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Remove="Node\FlipflopRegionControl.xaml.cs" />
|
<Compile Remove="Node\FlipflopRegionControl.xaml.cs" />
|
||||||
|
<Compile Remove="Node\Junction\NodeJunctionViewBase.cs" />
|
||||||
<Compile Remove="Node\NodeBase.cs" />
|
<Compile Remove="Node\NodeBase.cs" />
|
||||||
<Compile Remove="Themes\ConditionControl.xaml.cs" />
|
<Compile Remove="Themes\ConditionControl.xaml.cs" />
|
||||||
<Compile Remove="Themes\ConditionControlModel.cs" />
|
<Compile Remove="Themes\ConditionControlModel.cs" />
|
||||||
|
<Compile Remove="Themes\ConnectionControl.xaml.cs" />
|
||||||
<Compile Remove="Themes\ExplicitDataControl.xaml.cs" />
|
<Compile Remove="Themes\ExplicitDataControl.xaml.cs" />
|
||||||
<Compile Remove="Themes\ObjectViewerControl1.xaml.cs" />
|
<Compile Remove="Themes\ObjectViewerControl1.xaml.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
@@ -35,6 +37,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Page Remove="Node\FlipflopRegionControl.xaml" />
|
<Page Remove="Node\FlipflopRegionControl.xaml" />
|
||||||
<Page Remove="Themes\ConditionControl.xaml" />
|
<Page Remove="Themes\ConditionControl.xaml" />
|
||||||
|
<Page Remove="Themes\ConnectionControl.xaml" />
|
||||||
<Page Remove="Themes\ExplicitDataControl.xaml" />
|
<Page Remove="Themes\ExplicitDataControl.xaml" />
|
||||||
<Page Remove="Themes\MultiConditionConverter.xaml" />
|
<Page Remove="Themes\MultiConditionConverter.xaml" />
|
||||||
<Page Remove="Themes\ObjectViewerControl1.xaml" />
|
<Page Remove="Themes\ObjectViewerControl1.xaml" />
|
||||||
@@ -50,6 +53,8 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
|
<!--<PackageReference Include="LivetCask2" Version="4.0.2" />-->
|
||||||
|
<!--<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />-->
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
<ResourceDictionary
|
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:local="clr-namespace:Serein.Workbench.Themes"
|
||||||
xmlns:local="clr-namespace:Serein.Workbench.Themes"
|
xmlns:view="clr-namespace:Serein.Workbench.Node.View"
|
||||||
xmlns:sys="clr-namespace:System;assembly=mscorlib"
|
xmlns:sys="clr-namespace:System;assembly=mscorlib" >
|
||||||
>
|
|
||||||
|
|
||||||
|
|
||||||
<ResourceDictionary.MergedDictionaries>
|
<ResourceDictionary.MergedDictionaries>
|
||||||
@@ -31,15 +30,17 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Background="#E3FDFD">
|
<Grid Background="#E3FDFD">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="30"/>
|
<ColumnDefinition Width="30"/>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<view:ArgJunctionControl Grid.Column="0" ArgIndex="{Binding Index}" NodeGuid="{Binding NodeModel.Guid}" />
|
||||||
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
<TextBlock Grid.Column="1" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<CheckBox Grid.Column="2" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="3" MinWidth="50" Text="无须指定参数"/>
|
<TextBlock Grid.Column="3" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
|
<TextBlock Grid.Column="4" MinWidth="50" Text="无须指定参数"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
@@ -57,15 +58,17 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Background="#E3FDFD">
|
<Grid Background="#E3FDFD">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="30"/>
|
<ColumnDefinition Width="30"/>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<view:ArgJunctionControl Grid.Column="0" ArgIndex="{Binding Index}" NodeGuid="{Binding NodeModel.Guid}" />
|
||||||
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
<TextBlock Grid.Column="1" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<CheckBox Grid.Column="2" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
||||||
<ComboBox Grid.Column="3"
|
<TextBlock Grid.Column="3" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
|
<ComboBox Grid.Column="4"
|
||||||
MinWidth="50"
|
MinWidth="50"
|
||||||
ItemsSource="{Binding Items}"
|
ItemsSource="{Binding Items}"
|
||||||
SelectedItem="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
SelectedItem="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
|
||||||
@@ -85,15 +88,17 @@
|
|||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<Grid Background="#E3FDFD">
|
<Grid Background="#E3FDFD">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
|
<ColumnDefinition Width="auto"/>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="30"/>
|
<ColumnDefinition Width="30"/>
|
||||||
<ColumnDefinition Width="50"/>
|
<ColumnDefinition Width="50"/>
|
||||||
<ColumnDefinition Width="*"/>
|
<ColumnDefinition Width="*"/>
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
<TextBlock Grid.Column="0" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
<view:ArgJunctionControl Grid.Column="0" ArgIndex="{Binding Index}" NodeGuid="{Binding NodeModel.Guid}" />
|
||||||
<CheckBox Grid.Column="1" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
<TextBlock Grid.Column="1" Text="{Binding Index,StringFormat=agr{0}}" HorizontalAlignment="Center" VerticalAlignment="Center"/>
|
||||||
<TextBlock Grid.Column="2" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
<CheckBox Grid.Column="2" IsChecked="{Binding IsExplicitData, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" VerticalContentAlignment="Center"/>
|
||||||
<TextBox Grid.Column="3" MinWidth="50" Text="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
<TextBlock Grid.Column="3" MinWidth="50" Text="{Binding Name}" HorizontalAlignment="Left" VerticalAlignment="Center"/>
|
||||||
|
<TextBox Grid.Column="4" MinWidth="50" Text="{Binding DataValue, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ namespace Serein.Workbench.Themes
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 方法参数控件
|
/// 方法参数控件
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public partial class MethodDetailsControl : UserControl//,ItemsControl
|
public partial class MethodDetailsControl : UserControl
|
||||||
{
|
{
|
||||||
static MethodDetailsControl()
|
static MethodDetailsControl()
|
||||||
{
|
{
|
||||||
|
|||||||
42
Workbench/Extension/MyExtension.cs
Normal file
42
Workbench/Extension/MyExtension.cs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
93
Workbench/Node/Junction/JunctionCode.cs
Normal file
93
Workbench/Node/Junction/JunctionCode.cs
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
using Serein.Library;
|
||||||
|
using Serein.Library.Utils;
|
||||||
|
using System;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node.View
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public abstract class JunctionControlBase : UserControl
|
||||||
|
{
|
||||||
|
|
||||||
|
public double _MyWidth = 20;
|
||||||
|
public double _MyHeight = 20;
|
||||||
|
|
||||||
|
protected JunctionControlBase()
|
||||||
|
{
|
||||||
|
//this.Width = 20;
|
||||||
|
//this.Height = 20;
|
||||||
|
this.MouseDown += ControlPointBase_MouseDown;
|
||||||
|
this.MouseMove += ControlPointBase_MouseMove; ;
|
||||||
|
}
|
||||||
|
#region 控件属性,所在的节点
|
||||||
|
public static readonly DependencyProperty NodeGuidProperty =
|
||||||
|
DependencyProperty.Register("NodeGuid", typeof(string), typeof(JunctionControlBase), new PropertyMetadata(default(string)));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 所在的节点
|
||||||
|
/// </summary>
|
||||||
|
public string NodeGuid
|
||||||
|
{
|
||||||
|
get { return (string)GetValue(NodeGuidProperty); }
|
||||||
|
set { SetValue(NodeGuidProperty, value.ToString()); }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 控件属性,连接器类型
|
||||||
|
public static readonly DependencyProperty JunctionTypeProperty =
|
||||||
|
DependencyProperty.Register("JunctionType", typeof(string), typeof(JunctionControlBase), new PropertyMetadata(default(string)));
|
||||||
|
|
||||||
|
public JunctionType JunctionType
|
||||||
|
{
|
||||||
|
get { return EnumHelper.ConvertEnum<JunctionType>(GetValue(JunctionTypeProperty).ToString()); }
|
||||||
|
set { SetValue(JunctionTypeProperty, value.ToString()); }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public abstract void Render();
|
||||||
|
private void ControlPointBase_MouseMove(object sender, MouseEventArgs e)
|
||||||
|
{
|
||||||
|
if (GlobalJunctionData.MyGlobalData is null) return;
|
||||||
|
GlobalJunctionData.MyGlobalData.ChangingJunction = this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 在碰撞点上按下鼠标控件开始进行移动
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="sender"></param>
|
||||||
|
/// <param name="e"></param>
|
||||||
|
private void ControlPointBase_MouseDown(object sender, MouseButtonEventArgs e)
|
||||||
|
{
|
||||||
|
if (e.LeftButton == MouseButtonState.Pressed)
|
||||||
|
{
|
||||||
|
GlobalJunctionData.MyGlobalData = new ConnectingData();
|
||||||
|
var myDataType = GlobalJunctionData.MyGlobalData;
|
||||||
|
myDataType.StartJunction = this;
|
||||||
|
var canvas = MainWindow.GetParentOfType<Canvas>(this);
|
||||||
|
myDataType.StartPoint = this.TranslatePoint(new Point(this.Width /2 , this.Height /2 ), canvas);
|
||||||
|
myDataType.VirtualLine = new MyLine(canvas, new Line // 虚拟线
|
||||||
|
{
|
||||||
|
Stroke = Brushes.OldLace,
|
||||||
|
StrokeThickness = 2
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
e.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
69
Workbench/Node/Junction/JunctionData.cs
Normal file
69
Workbench/Node/Junction/JunctionData.cs
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node.View
|
||||||
|
{
|
||||||
|
|
||||||
|
#region Model,不科学的全局变量
|
||||||
|
public class MyLine
|
||||||
|
{
|
||||||
|
public MyLine(Canvas canvas, Line line)
|
||||||
|
{
|
||||||
|
Canvas = canvas;
|
||||||
|
VirtualLine = line;
|
||||||
|
canvas?.Children.Add(line);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Canvas Canvas { get; set; }
|
||||||
|
public Line VirtualLine { get; set; }
|
||||||
|
|
||||||
|
public void Remove()
|
||||||
|
{
|
||||||
|
Canvas?.Children.Remove(VirtualLine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ConnectingData
|
||||||
|
{
|
||||||
|
public JunctionControlBase StartJunction { get; set; }
|
||||||
|
public JunctionControlBase ChangingJunction { get; set; }
|
||||||
|
public Point StartPoint { get; set; }
|
||||||
|
public MyLine VirtualLine { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GlobalJunctionData
|
||||||
|
{
|
||||||
|
private static ConnectingData? myGlobalData;
|
||||||
|
|
||||||
|
public static ConnectingData? MyGlobalData
|
||||||
|
{
|
||||||
|
get => myGlobalData;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (myGlobalData == null)
|
||||||
|
{
|
||||||
|
myGlobalData = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public static bool IsCreatingConnection => myGlobalData is not null;
|
||||||
|
|
||||||
|
public static bool CanCreate => myGlobalData?.ChangingJunction.Equals(myGlobalData?.StartJunction) == false;
|
||||||
|
|
||||||
|
public static void OK()
|
||||||
|
{
|
||||||
|
myGlobalData?.VirtualLine.Remove();
|
||||||
|
myGlobalData = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
237
Workbench/Node/Junction/NodeJunctionViewBase.cs
Normal file
237
Workbench/Node/Junction/NodeJunctionViewBase.cs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Workbench/Node/Junction/View/ArgJunctionControl.cs
Normal file
54
Workbench/Node/Junction/View/ArgJunctionControl.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using Serein.Library;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node.View
|
||||||
|
{
|
||||||
|
public class ArgJunctionControl : JunctionControlBase
|
||||||
|
{
|
||||||
|
public ArgJunctionControl()
|
||||||
|
{
|
||||||
|
base.JunctionType = JunctionType.ArgData;
|
||||||
|
Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
#region 控件属性,对应的参数
|
||||||
|
public static readonly DependencyProperty ArgIndexProperty =
|
||||||
|
DependencyProperty.Register("ArgIndex", typeof(int), typeof(ArgJunctionControl), new PropertyMetadata(default(int)));
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 所在的节点
|
||||||
|
/// </summary>
|
||||||
|
public int ArgIndex
|
||||||
|
{
|
||||||
|
get { return (int)GetValue(ArgIndexProperty); }
|
||||||
|
set { SetValue(ArgIndexProperty, value); }
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
public override void Render()
|
||||||
|
{
|
||||||
|
if(double.IsNaN(base.Width))
|
||||||
|
{
|
||||||
|
base.Width = base._MyWidth;
|
||||||
|
}
|
||||||
|
if (double.IsNaN(base.Height))
|
||||||
|
{
|
||||||
|
base.Height = base._MyHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var ellipse = new Ellipse
|
||||||
|
{
|
||||||
|
Width = base.Width,
|
||||||
|
Height = base.Height,
|
||||||
|
Fill = Brushes.Orange,
|
||||||
|
ToolTip = "入参"
|
||||||
|
};
|
||||||
|
Content = ellipse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
39
Workbench/Node/Junction/View/ExecuteJunctionControl.cs
Normal file
39
Workbench/Node/Junction/View/ExecuteJunctionControl.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using Serein.Library;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node.View
|
||||||
|
{
|
||||||
|
public class ExecuteJunctionControl : JunctionControlBase
|
||||||
|
{
|
||||||
|
//public override JunctionType JunctionType { get; } = JunctionType.Execute;
|
||||||
|
public ExecuteJunctionControl()
|
||||||
|
{
|
||||||
|
base.JunctionType = JunctionType.Execute;
|
||||||
|
Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Render()
|
||||||
|
{
|
||||||
|
if (double.IsNaN(base.Width))
|
||||||
|
{
|
||||||
|
base.Width = base._MyWidth;
|
||||||
|
}
|
||||||
|
if (double.IsNaN(base.Height))
|
||||||
|
{
|
||||||
|
base.Height = base._MyHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rect = new Rectangle
|
||||||
|
{
|
||||||
|
Width = base.Width,
|
||||||
|
Height = base.Height,
|
||||||
|
Fill = Brushes.Green,
|
||||||
|
ToolTip = "方法执行"
|
||||||
|
};
|
||||||
|
Content = rect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
38
Workbench/Node/Junction/View/NextStepJunctionControl.cs
Normal file
38
Workbench/Node/Junction/View/NextStepJunctionControl.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using Serein.Library;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node.View
|
||||||
|
{
|
||||||
|
|
||||||
|
public class NextStepJunctionControl : JunctionControlBase
|
||||||
|
{
|
||||||
|
//public override JunctionType JunctionType { get; } = JunctionType.NextStep;
|
||||||
|
public NextStepJunctionControl()
|
||||||
|
{
|
||||||
|
base.JunctionType = JunctionType.NextStep;
|
||||||
|
Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Render()
|
||||||
|
{
|
||||||
|
if (double.IsNaN(base.Width))
|
||||||
|
{
|
||||||
|
base.Width = base._MyWidth;
|
||||||
|
}
|
||||||
|
if (double.IsNaN(base.Height))
|
||||||
|
{
|
||||||
|
base.Height = base._MyHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rect = new Rectangle
|
||||||
|
{
|
||||||
|
Width = base.Width,
|
||||||
|
Height = base.Height,
|
||||||
|
Fill = Brushes.Blue,
|
||||||
|
ToolTip = "下一个方法值"
|
||||||
|
};
|
||||||
|
Content = rect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
39
Workbench/Node/Junction/View/ResultJunctionControl.cs
Normal file
39
Workbench/Node/Junction/View/ResultJunctionControl.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using Serein.Library;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node.View
|
||||||
|
{
|
||||||
|
|
||||||
|
public class ResultJunctionControl : JunctionControlBase
|
||||||
|
{
|
||||||
|
//public override JunctionType JunctionType { get; } = JunctionType.ReturnData;
|
||||||
|
|
||||||
|
public ResultJunctionControl()
|
||||||
|
{
|
||||||
|
base.JunctionType = JunctionType.ReturnData;
|
||||||
|
Render();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Render()
|
||||||
|
{
|
||||||
|
if (double.IsNaN(base.Width))
|
||||||
|
{
|
||||||
|
base.Width = base._MyWidth;
|
||||||
|
}
|
||||||
|
if (double.IsNaN(base.Height))
|
||||||
|
{
|
||||||
|
base.Height = base._MyHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
var rect = new Rectangle
|
||||||
|
{
|
||||||
|
Width = base.Width,
|
||||||
|
Height = base.Height,
|
||||||
|
Fill = Brushes.Red,
|
||||||
|
ToolTip = "返回值"
|
||||||
|
};
|
||||||
|
Content = rect;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
using Serein.Library;
|
using Serein.Library;
|
||||||
using Serein.Library.Api;
|
using Serein.Library.Api;
|
||||||
using Serein.Workbench.Node.ViewModel;
|
using Serein.Workbench.Node.ViewModel;
|
||||||
|
using System.Windows;
|
||||||
using System.Windows.Controls;
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Input;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
|
|
||||||
namespace Serein.Workbench.Node.View
|
namespace Serein.Workbench.Node.View
|
||||||
@@ -14,7 +17,7 @@ namespace Serein.Workbench.Node.View
|
|||||||
{
|
{
|
||||||
public NodeControlViewModelBase ViewModel { get; set; }
|
public NodeControlViewModelBase ViewModel { get; set; }
|
||||||
|
|
||||||
|
|
||||||
protected NodeControlBase()
|
protected NodeControlBase()
|
||||||
{
|
{
|
||||||
this.Background = Brushes.Transparent;
|
this.Background = Brushes.Transparent;
|
||||||
@@ -24,7 +27,33 @@ namespace Serein.Workbench.Node.View
|
|||||||
ViewModel = viewModelBase;
|
ViewModel = viewModelBase;
|
||||||
this.Background = Brushes.Transparent;
|
this.Background = Brushes.Transparent;
|
||||||
this.DataContext = viewModelBase;
|
this.DataContext = viewModelBase;
|
||||||
|
SetBinding();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void SetBinding()
|
||||||
|
{
|
||||||
|
// 绑定 Canvas.Left
|
||||||
|
Binding leftBinding = new Binding("X")
|
||||||
|
{
|
||||||
|
Source = ViewModel.NodeModel.Position, // 如果 X 属性在当前 DataContext 中
|
||||||
|
Mode = BindingMode.TwoWay
|
||||||
|
};
|
||||||
|
BindingOperations.SetBinding(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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
320
Workbench/Node/View/ConnectionControl.cs
Normal file
320
Workbench/Node/View/ConnectionControl.cs
Normal file
@@ -0,0 +1,320 @@
|
|||||||
|
using Serein.Library;
|
||||||
|
using Serein.Library.Api;
|
||||||
|
using Serein.Workbench.Extension;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
using Color = System.Windows.Media.Color;
|
||||||
|
using ColorConverter = System.Windows.Media.ColorConverter;
|
||||||
|
using Point = System.Windows.Point;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Node.View
|
||||||
|
{
|
||||||
|
#region 连接点相关代码
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public class ConnectionModelBase
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 起始节点
|
||||||
|
/// </summary>
|
||||||
|
public NodeModelBase StartNode { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// 目标节点
|
||||||
|
/// </summary>
|
||||||
|
public NodeModelBase EndNode { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 来源于起始节点的(控制点)类型
|
||||||
|
/// </summary>
|
||||||
|
public JunctionType JoinTypeOfStart { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接到目标节点的(控制点)类型
|
||||||
|
/// </summary>
|
||||||
|
public JunctionType JoinTypeOfEnd { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接类型
|
||||||
|
/// </summary>
|
||||||
|
public ConnectionType Type { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public interface IJunctionNode
|
||||||
|
{
|
||||||
|
string BoundNodeGuid { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接点
|
||||||
|
/// </summary>
|
||||||
|
public class JunctionNode : IJunctionNode
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 连接点类型
|
||||||
|
/// </summary>
|
||||||
|
public JunctionType JunctionType { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// 对应的视图对象
|
||||||
|
/// </summary>
|
||||||
|
public NodeModelBase NodeModel { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
///
|
||||||
|
/// </summary>
|
||||||
|
public string BoundNodeGuid { get => NodeModel.Guid; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 有1个Execute
|
||||||
|
* 有1个NextStep
|
||||||
|
* 有0~65535个入参 ushort
|
||||||
|
* 有1个ReturnData(void方法返回null)
|
||||||
|
*
|
||||||
|
* Execute: // 执行这个方法
|
||||||
|
* 只接受 NextStep 的连接
|
||||||
|
* ArgData:
|
||||||
|
* 互相之间不能连接,只能接受 Execute、ReturnData 的连接
|
||||||
|
* Execute:表示从 Execute所在节点 获取数据
|
||||||
|
* ReturnData: 表示从对应节点获取数据
|
||||||
|
* ReturnData:
|
||||||
|
* 只能发起主动连接,且只能连接到 ArgData
|
||||||
|
* NextStep
|
||||||
|
* 只能连接连接 Execute
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接控件,表示控件的连接关系
|
||||||
|
/// </summary>
|
||||||
|
public class ConnectionControl : Shape
|
||||||
|
{
|
||||||
|
private readonly IFlowEnvironment environment;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 初始化连接控件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="Canvas"></param>
|
||||||
|
/// <param name="Type"></param>
|
||||||
|
public ConnectionControl(IFlowEnvironment environment,
|
||||||
|
Canvas Canvas,
|
||||||
|
ConnectionType Type,
|
||||||
|
NodeControlBase Start,
|
||||||
|
NodeControlBase End)
|
||||||
|
{
|
||||||
|
this.environment = environment;
|
||||||
|
this.Canvas = Canvas;
|
||||||
|
this.Type = Type;
|
||||||
|
this.Start = Start;
|
||||||
|
this.End = End;
|
||||||
|
|
||||||
|
InitElementPoint();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 所在的画布
|
||||||
|
/// </summary>
|
||||||
|
public Canvas Canvas { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 连接类型
|
||||||
|
/// </summary>
|
||||||
|
public ConnectionType Type { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 起始控件
|
||||||
|
/// </summary>
|
||||||
|
public NodeControlBase Start { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 结束控件
|
||||||
|
/// </summary>
|
||||||
|
public NodeControlBase End { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 配置连接曲线的右键菜单
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="line"></param>
|
||||||
|
private void ConfigureLineContextMenu(ConnectionControl connection)
|
||||||
|
{
|
||||||
|
var contextMenu = new ContextMenu();
|
||||||
|
contextMenu.Items.Add(MainWindow.CreateMenuItem("删除连线", (s, e) => DeleteConnection(connection)));
|
||||||
|
connection.ContextMenu = contextMenu;
|
||||||
|
connection.ContextMenu = contextMenu;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除该连线
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="line"></param>
|
||||||
|
private void DeleteConnection(ConnectionControl connection)
|
||||||
|
{
|
||||||
|
var connectionToRemove = connection;
|
||||||
|
if (connectionToRemove is null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 获取起始节点与终止节点,消除映射关系
|
||||||
|
var fromNodeGuid = connectionToRemove.Start.ViewModel.NodeModel.Guid;
|
||||||
|
var toNodeGuid = connectionToRemove.End.ViewModel.NodeModel.Guid;
|
||||||
|
environment.RemoveConnectAsync(fromNodeGuid, toNodeGuid, connection.Type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 移除
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveFromCanvas()
|
||||||
|
{
|
||||||
|
Canvas.Children.Remove(this); // 移除线
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 重新绘制
|
||||||
|
/// </summary>
|
||||||
|
public void AddOrRefreshLine()
|
||||||
|
{
|
||||||
|
this.InvalidateVisual();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void InitElementPoint()
|
||||||
|
{
|
||||||
|
leftCenterOfEndLocation = new Point(0, End.ActualHeight / 2); // 目标节点选择左侧边缘中心
|
||||||
|
rightCenterOfStartLocation = new Point(Start.ActualWidth, Start.ActualHeight / 2); // 起始节点选择右侧边缘中心
|
||||||
|
brush = GetLineColor(Type); // 线条颜色
|
||||||
|
hitVisiblePen = new Pen(Brushes.Transparent, 1.0); // 初始化碰撞检测线
|
||||||
|
hitVisiblePen.Freeze(); // Freeze以提高性能
|
||||||
|
visualPen = new Pen(brush, 1.0); // 默认可视化Pen
|
||||||
|
visualPen.Freeze(); // Freeze以提高性能
|
||||||
|
ConfigureLineContextMenu(this); // 设置连接右键事件
|
||||||
|
linkSize = 4; // 整线条粗细
|
||||||
|
Canvas.Children.Add(this); // 添加线
|
||||||
|
Grid.SetZIndex(this, -9999999); // 置底
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 控件重绘事件
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="drawingContext"></param>
|
||||||
|
protected override void OnRender(DrawingContext drawingContext)
|
||||||
|
{
|
||||||
|
RefreshPoint(Canvas, this.Start, this.End); // 刷新坐标
|
||||||
|
DrawBezierCurve(drawingContext, startPoint, endPoint, linkSize, brush); // 刷新线条显示位置
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly StreamGeometry streamGeometry = new StreamGeometry();
|
||||||
|
private Point rightCenterOfStartLocation; // 目标节点选择左侧边缘中心
|
||||||
|
private Point leftCenterOfEndLocation; // 起始节点选择右侧边缘中心
|
||||||
|
private Pen hitVisiblePen; // 初始化碰撞检测线
|
||||||
|
private Pen visualPen; // 默认可视化Pen
|
||||||
|
private Point startPoint; // 连接线的起始节点
|
||||||
|
private Point endPoint; // 连接线的终点
|
||||||
|
private Brush brush; // 线条颜色
|
||||||
|
double linkSize; // 根据缩放比例调整线条粗细
|
||||||
|
protected override Geometry DefiningGeometry => streamGeometry;
|
||||||
|
|
||||||
|
#region 工具方法
|
||||||
|
|
||||||
|
public void RefreshPoint(Canvas canvas, FrameworkElement startElement, FrameworkElement endElement)
|
||||||
|
{
|
||||||
|
endPoint = endElement.TranslatePoint(leftCenterOfEndLocation, canvas); // 计算终点位置
|
||||||
|
startPoint = startElement.TranslatePoint(rightCenterOfStartLocation, canvas); // 获取起始节点的中心位置
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 根据连接类型指定颜色
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currentConnectionType"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="Exception"></exception>
|
||||||
|
public static SolidColorBrush GetLineColor(ConnectionType currentConnectionType)
|
||||||
|
{
|
||||||
|
return currentConnectionType switch
|
||||||
|
{
|
||||||
|
ConnectionType.IsSucceed => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#04FC10")),
|
||||||
|
ConnectionType.IsFail => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#F18905")),
|
||||||
|
ConnectionType.IsError => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#FE1343")),
|
||||||
|
ConnectionType.Upstream => new SolidColorBrush((Color)ColorConverter.ConvertFromString("#4A82E4")),
|
||||||
|
_ => throw new Exception(),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
private Point c0, c1; // 用于计算贝塞尔曲线控制点逻辑
|
||||||
|
private Vector axis = new Vector(1, 0);
|
||||||
|
private Vector startToEnd;
|
||||||
|
private void DrawBezierCurve(DrawingContext drawingContext,
|
||||||
|
Point start,
|
||||||
|
Point end,
|
||||||
|
double linkSize,
|
||||||
|
Brush brush,
|
||||||
|
bool isHitTestVisible = false,
|
||||||
|
double strokeThickness = 1.0,
|
||||||
|
bool isMouseOver = false,
|
||||||
|
double dashOffset = 0.0)
|
||||||
|
{
|
||||||
|
// 控制点的计算逻辑
|
||||||
|
double power = 8 * 8; // 控制贝塞尔曲线的“拉伸”强度
|
||||||
|
|
||||||
|
// 计算轴向向量与起点到终点的向量
|
||||||
|
//var axis = new Vector(1, 0);
|
||||||
|
startToEnd = (end.ToVector() - start.ToVector()).NormalizeTo();
|
||||||
|
|
||||||
|
// 计算拉伸程度k,拉伸与水平夹角正相关
|
||||||
|
var k = 1 - Math.Pow(Math.Max(0, axis.DotProduct(startToEnd)), 10.0);
|
||||||
|
|
||||||
|
// 如果起点x大于终点x,增加额外的偏移量,避免重叠
|
||||||
|
var bias = start.X > end.X ? Math.Abs(start.X - end.X) * 0.25 : 0;
|
||||||
|
|
||||||
|
// 控制点的实际计算
|
||||||
|
c0 = new Point(+(power + bias) * k + start.X, start.Y);
|
||||||
|
c1 = new Point(-(power + bias) * k + end.X, end.Y);
|
||||||
|
|
||||||
|
// 准备StreamGeometry以用于绘制曲线
|
||||||
|
streamGeometry.Clear();
|
||||||
|
using (var context = streamGeometry.Open())
|
||||||
|
{
|
||||||
|
context.BeginFigure(start, true, false); // 曲线起点
|
||||||
|
context.BezierTo(c0, c1, end, true, false); // 画贝塞尔曲线
|
||||||
|
}
|
||||||
|
|
||||||
|
drawingContext.DrawGeometry(null, visualPen, streamGeometry);
|
||||||
|
|
||||||
|
// 绘制碰撞检测线
|
||||||
|
//if (true)
|
||||||
|
//{
|
||||||
|
// //hitVisiblePen = new Pen(Brushes.Transparent, linkSize + strokeThickness);
|
||||||
|
// //hitVisiblePen.Freeze();
|
||||||
|
// drawingContext.DrawGeometry(null, hitVisiblePen, streamGeometry);
|
||||||
|
//}
|
||||||
|
//else
|
||||||
|
//{
|
||||||
|
|
||||||
|
|
||||||
|
//}
|
||||||
|
|
||||||
|
}
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
10
Workbench/Themes/ConnectionControl.xaml
Normal file
10
Workbench/Themes/ConnectionControl.xaml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<UserControl x:Class="Serein.Workbench.Themes.ConnectionControl"
|
||||||
|
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
|
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||||
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
|
xmlns:local="clr-namespace:Serein.Workbench.Themes"
|
||||||
|
mc:Ignorable="d"
|
||||||
|
d:DesignHeight="450" d:DesignWidth="800">
|
||||||
|
<Canvas x:Name="PART_Canvas" />
|
||||||
|
</UserControl>
|
||||||
89
Workbench/Themes/ConnectionControl.xaml.cs
Normal file
89
Workbench/Themes/ConnectionControl.xaml.cs
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
using Serein.Library;
|
||||||
|
using Serein.Workbench.Node.View;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows;
|
||||||
|
using System.Windows.Controls;
|
||||||
|
using System.Windows.Data;
|
||||||
|
using System.Windows.Documents;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using System.Windows.Media;
|
||||||
|
using System.Windows.Media.Imaging;
|
||||||
|
using System.Windows.Navigation;
|
||||||
|
using System.Windows.Shapes;
|
||||||
|
|
||||||
|
namespace Serein.Workbench.Themes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ConnectionControl.xaml 的交互逻辑
|
||||||
|
/// </summary>
|
||||||
|
public partial class ConnectionControl : UserControl
|
||||||
|
{
|
||||||
|
public static readonly DependencyProperty StartProperty =
|
||||||
|
DependencyProperty.Register("Start", typeof(NodeControlBase), typeof(ConnectionControl),
|
||||||
|
new PropertyMetadata(null, OnConnectionChanged));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty EndProperty =
|
||||||
|
DependencyProperty.Register("End", typeof(NodeControlBase), typeof(ConnectionControl),
|
||||||
|
new PropertyMetadata(null, OnConnectionChanged));
|
||||||
|
|
||||||
|
public static readonly DependencyProperty TypeProperty =
|
||||||
|
DependencyProperty.Register("Type", typeof(ConnectionType), typeof(ConnectionControl),
|
||||||
|
new PropertyMetadata(ConnectionType.IsSucceed, OnConnectionChanged));
|
||||||
|
|
||||||
|
public NodeControlBase Start
|
||||||
|
{
|
||||||
|
get { return (NodeControlBase)GetValue(StartProperty); }
|
||||||
|
set { SetValue(StartProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public NodeControlBase End
|
||||||
|
{
|
||||||
|
get { return (NodeControlBase)GetValue(EndProperty); }
|
||||||
|
set { SetValue(EndProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConnectionType Type
|
||||||
|
{
|
||||||
|
get { return (ConnectionType)GetValue(TypeProperty); }
|
||||||
|
set { SetValue(TypeProperty, value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OnConnectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
|
||||||
|
{
|
||||||
|
var control = d as ConnectionControl;
|
||||||
|
control?.Refresh();
|
||||||
|
}
|
||||||
|
public ConnectionControl()
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
}
|
||||||
|
public void Refresh()
|
||||||
|
{
|
||||||
|
if (Start == null || End == null || PART_Canvas == null)
|
||||||
|
return;
|
||||||
|
InitializePath();
|
||||||
|
//BezierLineDrawer.UpdateBezierLine(PART_Canvas, Start, End, _bezierPath, _arrowPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private System.Windows.Shapes.Path _bezierPath;
|
||||||
|
private System.Windows.Shapes.Path _arrowPath;
|
||||||
|
|
||||||
|
private void InitializePath()
|
||||||
|
{
|
||||||
|
if (_bezierPath == null)
|
||||||
|
{
|
||||||
|
_bezierPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(Type), StrokeThickness = 1 };
|
||||||
|
PART_Canvas.Children.Add(_bezierPath);
|
||||||
|
}
|
||||||
|
if (_arrowPath == null)
|
||||||
|
{
|
||||||
|
_arrowPath = new System.Windows.Shapes.Path { Stroke = BezierLineDrawer.GetLineColor(Type), Fill = BezierLineDrawer.GetLineColor(Type), StrokeThickness = 1 };
|
||||||
|
PART_Canvas.Children.Add(_arrowPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user