Workbench项目中,优化了节点的复制、粘贴,加载。

This commit is contained in:
fengjiayi
2024-12-14 23:46:37 +08:00
parent c5e2abea96
commit 8d0258ebea
17 changed files with 396 additions and 298 deletions

View File

@@ -28,7 +28,26 @@ namespace Serein.Library
/// </summary> /// </summary>
public abstract partial class NodeModelBase : IDynamicFlowNode public abstract partial class NodeModelBase : IDynamicFlowNode
{ {
#region #region
/// <summary>
/// 保存自定义信息
/// </summary>
/// <returns></returns>
public virtual NodeInfo SaveCustomData(NodeInfo nodeInfo)
{
return nodeInfo;
}
/// <summary>
/// 加载自定义数据
/// </summary>
/// <param name="nodeInfo"></param>
public virtual void LoadCustomData(NodeInfo nodeInfo)
{
return;
}
/// <summary> /// <summary>
/// 移除该节点 /// 移除该节点
/// </summary> /// </summary>
@@ -36,16 +55,52 @@ namespace Serein.Library
{ {
} }
/// <summary>
/// 移除该节点
/// </summary>
public virtual void RemoveFromEnv()
{
if (this.DebugSetting.CancelInterruptCallback != null)
{
this.DebugSetting.CancelInterruptCallback?.Invoke();
}
this.DebugSetting.GetInterruptTask = null;
this.DebugSetting.NodeModel = null;
this.DebugSetting.CancelInterruptCallback = null;
this.DebugSetting = null;
foreach (var pd in this.MethodDetails.ParameterDetailss)
{
pd.DataValue = null;
pd.Items = null;
pd.NodeModel = null;
pd.ExplicitType = null;
pd.DataType = null;
pd.Name = null;
pd.ArgDataSourceNodeGuid = null;
pd.ExplicitTypeName = null;
}
this.MethodDetails.ParameterDetailss = null;
this.MethodDetails.ActingInstance = null;
this.MethodDetails.NodeModel = null;
this.MethodDetails.ReturnType = null;
this.MethodDetails.AssemblyName = null;
this.MethodDetails.MethodAnotherName = null;
this.MethodDetails.MethodLockName = null;
this.MethodDetails.MethodName = null;
this.MethodDetails.ActingInstanceType = null;
this.MethodDetails = null;
this.Position = null;
this.DisplayName = null;
#endregion this.Env = null;
}
#region /
/// <summary> /// <summary>
/// 输出方法参数信息 /// 输出方法参数信息
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public virtual ParameterData[] SaveParameterInfo() public ParameterData[] SaveParameterInfo()
{ {
if(MethodDetails.ParameterDetailss == null) if(MethodDetails.ParameterDetailss == null)
{ {
@@ -69,20 +124,13 @@ namespace Serein.Library
} }
} }
/// <summary>
/// 保存自定义信息
/// </summary>
/// <returns></returns>
public virtual NodeInfo SaveCustomData(NodeInfo nodeInfo)
{
return nodeInfo;
}
/// <summary> /// <summary>
/// 导出为节点信息 /// 导出为节点信息
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public virtual NodeInfo ToInfo() public NodeInfo ToInfo()
{ {
// if (MethodDetails == null) return null; // if (MethodDetails == null) return null;
@@ -110,25 +158,20 @@ namespace Serein.Library
IsInterrupt = this.DebugSetting.IsInterrupt, IsInterrupt = this.DebugSetting.IsInterrupt,
IsEnable = this.DebugSetting.IsEnable, IsEnable = this.DebugSetting.IsEnable,
}; };
nodeInfo.Position.X = Math.Round(nodeInfo.Position.X, 1);
nodeInfo.Position.Y = Math.Round(nodeInfo.Position.Y, 1);
nodeInfo = SaveCustomData(nodeInfo); nodeInfo = SaveCustomData(nodeInfo);
return nodeInfo; return nodeInfo;
} }
/// <summary>
/// 加载自定义数据
/// </summary>
/// <param name="nodeInfo"></param>
public virtual void LoadCustomData(NodeInfo nodeInfo)
{
return;
}
/// <summary> /// <summary>
/// 从节点信息加载节点 /// 从节点信息加载节点
/// </summary> /// </summary>
/// <param name="nodeInfo"></param> /// <param name="nodeInfo"></param>
/// <returns></returns> /// <returns></returns>
public virtual void LoadInfo(NodeInfo nodeInfo) public void LoadInfo(NodeInfo nodeInfo)
{ {
this.Guid = nodeInfo.Guid; this.Guid = nodeInfo.Guid;
this.Position = nodeInfo.Position ?? new PositionOfUI(0, 0);// 加载位置信息 this.Position = nodeInfo.Position ?? new PositionOfUI(0, 0);// 加载位置信息
@@ -520,33 +563,33 @@ namespace Serein.Library
#endregion #endregion
#region null //#region 入参存在取值转换器调用对应的转换器获取入参数据如果获取成功不为null会跳过循
if (pd.ExplicitType.IsEnum && !(pd.Convertor is null)) //if (pd.ExplicitType.IsEnum && !(pd.Convertor is null))
{ //{
//var resultEnum = Enum.ToObject(ed.ExplicitType, ed.DataValue); // //var resultEnum = Enum.ToObject(ed.ExplicitType, ed.DataValue);
var resultEnum = Enum.Parse(pd.ExplicitType, pd.DataValue); // var resultEnum = Enum.Parse(pd.ExplicitType, pd.DataValue);
var value = pd.Convertor(resultEnum); // var value = pd.Convertor(resultEnum);
if (value is null) // if (value is null)
{ // {
throw new InvalidOperationException("转换器调用失败"); // throw new InvalidOperationException("转换器调用失败");
} // }
else // else
{ // {
if (hasParams) // if (hasParams)
{ // {
paramsArgs.SetValue(value, paramsArgIndex++); // paramsArgs.SetValue(value, paramsArgIndex++);
// 处理可选参数 // // 处理可选参数
//paramsArgs[paramsArgIndex++] = value; // //paramsArgs[paramsArgIndex++] = value;
} // }
else // else
{ // {
parameters[i] = value; // parameters[i] = value;
} // }
continue; // continue;
} // }
} //}
#endregion //#endregion
#region BinValue的类型转换器null #region BinValue的类型转换器null
// 入参存在基于BinValue的类型转换器获取枚举转换器中记录的类型 // 入参存在基于BinValue的类型转换器获取枚举转换器中记录的类型

View File

@@ -35,11 +35,11 @@ namespace Serein.Library
[PropertyInfo(IsNotification = true)] [PropertyInfo(IsNotification = true)]
private bool _isExplicitData ; private bool _isExplicitData ;
/// <summary> ///// <summary>
/// 转换器 IEnumConvertor&lt;,&gt; ///// 转换器 IEnumConvertor&lt;,&gt;
/// </summary> ///// </summary>
[PropertyInfo] //[PropertyInfo]
private Func<object, object> _convertor ; //private Func<object, object> _convertor ;
/// <summary> /// <summary>
/// 方法入参若无相关转换器特性标注则无需关注该变量。该变量用于需要用到枚举BinValue转换器时指示相应的入参变量需要转为的类型。 /// 方法入参若无相关转换器特性标注则无需关注该变量。该变量用于需要用到枚举BinValue转换器时指示相应的入参变量需要转为的类型。
@@ -167,7 +167,7 @@ namespace Serein.Library
IsExplicitData = this.IsExplicitData, IsExplicitData = this.IsExplicitData,
ExplicitType = this.ExplicitType, ExplicitType = this.ExplicitType,
ExplicitTypeName = this.ExplicitTypeName, ExplicitTypeName = this.ExplicitTypeName,
Convertor = this.Convertor, //Convertor = this.Convertor,
DataType = this.DataType, DataType = this.DataType,
Name = this.Name, Name = this.Name,
DataValue = string.IsNullOrEmpty(DataValue) ? string.Empty : DataValue, DataValue = string.IsNullOrEmpty(DataValue) ? string.Empty : DataValue,
@@ -180,14 +180,6 @@ namespace Serein.Library
public override string ToString() public override string ToString()
{ {
if(_convertor is null)
{
return $"[{this.Index}] {this.Name} : {this.DataType?.FullName}";
}
else
{
}
return $"[{this.Index}] {this.Name} : {this.ExplicitType.FullName} -> {this.DataType.FullName}"; return $"[{this.Index}] {this.Name} : {this.ExplicitType.FullName} -> {this.DataType.FullName}";
} }
} }

View File

@@ -918,7 +918,7 @@ namespace Serein.NodeFlow.Env
else else
{ {
// 加载方法节点 // 加载方法节点
if (string.IsNullOrEmpty(nodeInfo.AssemblyName) && string.IsNullOrEmpty(nodeInfo.MethodName)) if (string.IsNullOrEmpty(nodeInfo.MethodName))
{ {
continue; continue;
} }

View File

@@ -65,7 +65,7 @@ namespace Serein.NodeFlow.Model
ArgDataSourceNodeGuid = string.Empty, ArgDataSourceNodeGuid = string.Empty,
ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData,
NodeModel = this, NodeModel = this,
Convertor = null, //Convertor = null,
ExplicitTypeName = "Value", ExplicitTypeName = "Value",
Items = null, Items = null,
}; };

View File

@@ -54,7 +54,7 @@ namespace Serein.NodeFlow.Model
ArgDataSourceNodeGuid = string.Empty, ArgDataSourceNodeGuid = string.Empty,
ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData,
NodeModel = this, NodeModel = this,
Convertor = null, //Convertor = null,
ExplicitTypeName = "Value", ExplicitTypeName = "Value",
Items = null, Items = null,
}; };

View File

@@ -215,13 +215,14 @@ public static class NodeMethodDetailsHelper
ConvertorInstance[key] = (instance, convertMethod); ConvertorInstance[key] = (instance, convertMethod);
} }
object func(object enumValue) //object func(object enumValue)
{ //{
(var obj, var methodInfo) = ConvertorInstance[key]; // (var obj, var methodInfo) = ConvertorInstance[key];
return methodInfo?.Invoke(obj, [enumValue]); // return methodInfo?.Invoke(obj, [enumValue]);
} //}
// 确保实例实现了所需接口 // 确保实例实现了所需接口
ParameterDetails ed = GetExplicitDataOfParameter(it, index, paremType, true, func); // 自定义的转换器 获取参数 ParameterDetails ed = GetExplicitDataOfParameter(it, index, paremType, true); // 自定义的转换器 获取参数
return ed; return ed;
} }
@@ -242,8 +243,7 @@ public static class NodeMethodDetailsHelper
private static ParameterDetails GetExplicitDataOfParameter(ParameterInfo parameterInfo, private static ParameterDetails GetExplicitDataOfParameter(ParameterInfo parameterInfo,
int index, int index,
Type explicitParemType, Type explicitParemType,
bool isExplicitData, bool isExplicitData)
Func<object, object> func = null)
{ {
bool hasParams = parameterInfo.IsDefined(typeof(ParamArrayAttribute)); // 判断是否为可变参数 bool hasParams = parameterInfo.IsDefined(typeof(ParamArrayAttribute)); // 判断是否为可变参数
@@ -269,7 +269,7 @@ public static class NodeMethodDetailsHelper
Index = index, // 索引 Index = index, // 索引
ExplicitTypeName = explicitTypeName, // Select/Bool/Value ExplicitTypeName = explicitTypeName, // Select/Bool/Value
ExplicitType = explicitParemType,// 显示的入参类型 ExplicitType = explicitParemType,// 显示的入参类型
Convertor = func, // 转换器 //Convertor = func, // 转换器
DataType = dataType, // 实际的入参类型 DataType = dataType, // 实际的入参类型
Name = parameterInfo.Name, Name = parameterInfo.Name,
DataValue = parameterInfo.HasDefaultValue ? parameterInfo?.DefaultValue?.ToString() : "", // 如果存在默认值,则使用默认值 DataValue = parameterInfo.HasDefaultValue ? parameterInfo?.DefaultValue?.ToString() : "", // 如果存在默认值,则使用默认值

View File

@@ -102,17 +102,23 @@
<GridSplitter Grid.Row="1" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" /> <GridSplitter Grid.Row="1" Grid.Column="1" Width="5" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" ResizeBehavior="PreviousAndNext" Background="Gray" />
<Grid Grid.Row="1" Grid.Column="2" x:Name="FlowChartStackGrid"> <Grid Grid.Row="1" Grid.Column="2" x:Name="FlowChartStackGrid">
<ListBox ItemsSource="{Binding Nodes}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">
<ListBox.ItemTemplate>
<DataTemplate>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel x:Name="FlowChartStackPanel" <StackPanel x:Name="FlowChartStackPanel"
ClipToBounds="True"> ClipToBounds="True">
<!-- 虚拟化 VirtualizingStackPanel.IsVirtualizing="True" --> <!-- 虚拟化 VirtualizingStackPanel.IsVirtualizing="True" -->
<Canvas <Canvas
x:Name="FlowChartCanvas" x:Name="FlowChartCanvas"
Background="#E1FBEA" Background="#E1FBEA"
AllowDrop="True" AllowDrop="True"
Width="2000" Width="1920"
Height="2000" Height="1080"
MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown" MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown"
MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp" MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp"
MouseDown="FlowChartCanvas_MouseDown" MouseDown="FlowChartCanvas_MouseDown"

View File

@@ -14,11 +14,14 @@ using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel; using Serein.Workbench.Node.ViewModel;
using Serein.Workbench.Themes; using Serein.Workbench.Themes;
using Serein.Workbench.Tool; using Serein.Workbench.Tool;
using SqlSugar.Extensions;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text;
using System.Windows; using System.Windows;
using System.Windows.Controls; using System.Windows.Controls;
using System.Windows.Controls.Primitives; using System.Windows.Controls.Primitives;
@@ -188,11 +191,7 @@ namespace Serein.Workbench
InitFlowEnvironmentEvent(); // 配置环境事件 InitFlowEnvironmentEvent(); // 配置环境事件
if (App.FlowProjectData is not null)
{
EnvDecorator.LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); // 加载项目
}
} }
@@ -253,6 +252,13 @@ namespace Serein.Workbench
#region #region
private void Window_Loaded(object sender, RoutedEventArgs e) private void Window_Loaded(object sender, RoutedEventArgs e)
{ {
if (App.FlowProjectData is not null)
{
_ = Task.Run(() =>
{
EnvDecorator.LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); // 加载项目
});
}
} }
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e) private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{ {
@@ -659,15 +665,17 @@ namespace Serein.Workbench
} }
} }
FlowChartCanvas.Children.Remove(nodeControl); FlowChartCanvas.Children.Remove(nodeControl);
nodeControl.RemoveAllConection(); nodeControl.RemoveAllConection();
NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid); NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid);
} }
/// <summary> /// <summary>
/// 编辑项目时添加节点 /// 添加节点事件
/// </summary> /// </summary>
/// <param name="nodeDataBase"></param> /// <param name="eventArgs">添加节点事件参数</param>
/// <exception cref="NotImplementedException"></exception> /// <exception cref="NotImplementedException"></exception>
private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs) private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs)
{ {
@@ -738,6 +746,8 @@ namespace Serein.Workbench
NodeTreeViewer.AddGlobalFlipFlop(EnvDecorator, node); // 新增的触发器节点添加到全局触发器 NodeTreeViewer.AddGlobalFlipFlop(EnvDecorator, node); // 新增的触发器节点添加到全局触发器
} }
} }
GC.Collect();
#endregion #endregion
} }
@@ -819,7 +829,10 @@ namespace Serein.Workbench
//{ //{
// nodeControl.ViewModel.IsInterrupt = true; // nodeControl.ViewModel.IsInterrupt = true;
//} //}
if(nodeControl.ContextMenu == null)
{
return;
}
foreach (var menuItem in nodeControl.ContextMenu.Items) foreach (var menuItem in nodeControl.ContextMenu.Items)
{ {
if (menuItem is MenuItem menu) if (menuItem is MenuItem menu)
@@ -2669,147 +2682,16 @@ namespace Serein.Workbench
#region #region
if (Keyboard.Modifiers == ModifierKeys.Control) if (Keyboard.Modifiers == ModifierKeys.Control)
{ {
#region
if (e.Key == Key.C && selectNodeControls.Count > 0) if (e.Key == Key.C && selectNodeControls.Count > 0)
{ {
// 处理复制操作 CpoyNodeInfo();
List<NodeInfo> selectNodeInfos = selectNodeControls.Select(control => control.ViewModel.NodeModel.ToInfo()).ToList();
/*foreach (var node in selectNodeInfos.ToArray())
{
// 遍历这些节点的子节点,获得完整的已选节点信息
foreach (var childNodeGuid in node.ChildNodeGuids)
{
if (!string.IsNullOrEmpty(childNodeGuid)
&& NodeControls.TryGetValue(childNodeGuid, out var nodeControl))
{
var newNodeInfo = nodeControl.ViewModel.NodeModel.ToInfo();
selectNodeInfos.Add(newNodeInfo);
}
}
}*/
Dictionary<string, string> guids = new Dictionary<string, string>(); // 记录 Guid
// 遍历当前已选节点
foreach (var node in selectNodeInfos.ToArray())
{
if (!guids.ContainsKey(node.Guid))
{
// 如果是没出现过的Guid则记录并新增对应的映射。
guids.TryAdd(node.Guid, Guid.NewGuid().ToString());
}
else
{
// 出现过的Guid说明重复添加了。应该不会走到这。
continue;
}
if(node.ChildNodeGuids is null)
{
continue; // 跳过没有子节点的节点
}
// 遍历这些节点的子节点,获得完整的已选节点信息
foreach (var childNodeGuid in node.ChildNodeGuids)
{
if (!guids.ContainsKey(childNodeGuid))
{
// 如果是没出现过的Guid则记录并新增对应的映射。
guids.TryAdd(node.Guid, Guid.NewGuid().ToString());
}
if (!string.IsNullOrEmpty(childNodeGuid)
&& NodeControls.TryGetValue(childNodeGuid, out var nodeControl))
{
var newNodeInfo = nodeControl.ViewModel.NodeModel.ToInfo();
selectNodeInfos.Add(newNodeInfo);
}
}
}
var replacer = new GuidReplacer();
foreach(var kv in guids)
{
replacer.AddReplacement(kv.Key, kv.Value);
}
JObject json = new JObject()
{
["nodes"] = JArray.FromObject(selectNodeInfos)
};
var jsonText = json.ToString();
string result = replacer.Replace(jsonText);
try
{
Clipboard.SetDataObject(result, true); // 持久性设置
SereinEnv.WriteLine(InfoType.INFO, $"复制已选节点({selectNodeInfos.Count}个)");
}
catch (Exception ex)
{
SereinEnv.WriteLine(InfoType.ERROR, $"复制失败:{ex.Message}");
}
//SereinEnv.WriteLine(InfoType.INFO, json.ToString());
e.Handled = true;
} }
#endregion
#region
else if (e.Key == Key.V) else if (e.Key == Key.V)
{ {
PasteNodeInfo();
if (Clipboard.ContainsText()) }
{ }
string clipboardText = Clipboard.GetText(TextDataFormat.Text);
List<NodeInfo> nodes = JsonConvert.DeserializeObject<List<NodeInfo>>(JObject.Parse(clipboardText)["nodes"].ToString());
if (nodes is not null && nodes.Count >= 0)
{
Point mousePosition = Mouse.GetPosition(FlowChartCanvas);
PositionOfUI positionOfUI = new PositionOfUI(mousePosition.X, mousePosition.Y); // 坐标数据
SereinEnv.WriteLine(InfoType.INFO, $"粘贴节点({nodes.Count}个)");
// 获取第一个节点的原始位置
var index0NodeX = nodes[0].Position.X;
var index0NodeY = nodes[0].Position.Y;
// 计算所有节点相对于第一个节点的偏移量
foreach (var node in nodes)
{
var offsetX = node.Position.X - index0NodeX;
var offsetY = node.Position.Y - index0NodeY;
// 根据鼠标位置平移节点
node.Position = new PositionOfUI(positionOfUI.X + offsetX, positionOfUI.Y + offsetY);
}
_ = EnvDecorator.LoadNodeInfosAsync(nodes);
}
//SereinEnv.WriteLine(InfoType.INFO, $"剪贴板文本内容: {clipboardText}");
}
else if (Clipboard.ContainsImage())
{
var image = Clipboard.GetImage();
}
else
{
SereinEnv.WriteLine(InfoType.INFO, "剪贴板中没有可识别的数据。");
}
e.Handled = true;
}
#endregion
return;
}
#endregion #endregion
if (e.KeyStates == Keyboard.GetKeyStates(Key.Escape)) if (e.KeyStates == Keyboard.GetKeyStates(Key.Escape))
{ {
IsControlDragging = false; IsControlDragging = false;
@@ -2860,9 +2742,219 @@ namespace Serein.Workbench
} }
#region
/// <summary>
/// 复制节点
/// </summary>
private void CpoyNodeInfo()
{
// 处理复制操作
var dictSelection = selectNodeControls
.Select(control => control.ViewModel.NodeModel.ToInfo())
.ToDictionary(kvp => kvp.Guid, kvp => kvp);
// 遍历当前已选节点
foreach (var node in dictSelection.Values.ToArray())
{
// 遍历这些节点的子节点,获得完整的已选节点信息
foreach (var childNodeGuid in node.ChildNodeGuids)
{
if(!dictSelection.ContainsKey(childNodeGuid) && NodeControls.TryGetValue(childNodeGuid,out var childNode))
{
dictSelection.Add(childNodeGuid, childNode.ViewModel.NodeModel.ToInfo());
}
}
}
JObject json = new JObject()
{
["nodes"] = JArray.FromObject(dictSelection.Values)
};
var jsonText = json.ToString();
try
{
//Clipboard.SetDataObject(result, true); // 持久性设置
Clipboard.SetDataObject(jsonText, true); // 持久性设置
SereinEnv.WriteLine(InfoType.INFO, $"复制已选节点({dictSelection.Count}个)");
}
catch (Exception ex)
{
SereinEnv.WriteLine(InfoType.ERROR, $"复制失败:{ex.Message}");
}
}
/// <summary>
/// 粘贴节点
/// </summary>
private void PasteNodeInfo()
{
if (Clipboard.ContainsText())
{
try
{
string clipboardText = Clipboard.GetText(TextDataFormat.Text);
string jsonText = JObject.Parse(clipboardText)["nodes"].ToString();
List<NodeInfo> nodes = JsonConvert.DeserializeObject<List<NodeInfo>>(jsonText);
if (nodes is null || nodes.Count < 0)
{
return;
}
#region
Dictionary<string, string> guids = new Dictionary<string, string>(); // 记录 Guid
// 遍历当前已选节点
foreach (var node in nodes.ToArray())
{
if (NodeControls.ContainsKey(node.Guid) && !guids.ContainsKey(node.Guid))
{
// 如果是没出现过、且在当前记录中重复的Guid则记录并新增对应的映射。
guids.TryAdd(node.Guid, Guid.NewGuid().ToString());
}
else
{
// 出现过的Guid说明重复添加了。应该不会走到这。
continue;
}
if (node.ChildNodeGuids is null)
{
continue; // 跳过没有子节点的节点
}
// 遍历这些节点的子节点,获得完整的已选节点信息
foreach (var childNodeGuid in node.ChildNodeGuids)
{
if (NodeControls.ContainsKey(node.Guid) && !NodeControls.ContainsKey(node.Guid))
{
// 当前Guid并不重复跳过替换
continue;
}
if (!guids.ContainsKey(childNodeGuid))
{
// 如果是没出现过的Guid则记录并新增对应的映射。
guids.TryAdd(node.Guid, Guid.NewGuid().ToString());
}
if (!string.IsNullOrEmpty(childNodeGuid)
&& NodeControls.TryGetValue(childNodeGuid, out var nodeControl))
{
var newNodeInfo = nodeControl.ViewModel.NodeModel.ToInfo();
nodes.Add(newNodeInfo);
}
}
}
//var flashText = new FlashText.NET.TextReplacer();
//var t = guids.Select(kvp => (kvp.Key, kvp.Value)).ToArray();
//var result = flashText.ReplaceWords(jsonText, t);
StringBuilder sb = new StringBuilder(jsonText);
foreach (var kv in guids)
{
sb.Replace(kv.Key, kv.Value);
}
string result = sb.ToString();
/*var replacer = new GuidReplacer();
foreach (var kv in guids)
{
replacer.AddReplacement(kv.Key, kv.Value);
}
string result = replacer.Replace(jsonText);*/
//SereinEnv.WriteLine(InfoType.ERROR, result);
nodes = JsonConvert.DeserializeObject<List<NodeInfo>>(result);
if (nodes is null || nodes.Count < 0)
{
return;
}
#endregion
Point mousePosition = Mouse.GetPosition(FlowChartCanvas);
PositionOfUI positionOfUI = new PositionOfUI(mousePosition.X, mousePosition.Y); // 坐标数据
// 获取第一个节点的原始位置
var index0NodeX = nodes[0].Position.X;
var index0NodeY = nodes[0].Position.Y;
// 计算所有节点相对于第一个节点的偏移量
foreach (var node in nodes)
{
var offsetX = node.Position.X - index0NodeX;
var offsetY = node.Position.Y - index0NodeY;
// 根据鼠标位置平移节点
node.Position = new PositionOfUI(positionOfUI.X + offsetX, positionOfUI.Y + offsetY);
}
_ = EnvDecorator.LoadNodeInfosAsync(nodes);
}
catch (Exception ex)
{
SereinEnv.WriteLine(InfoType.ERROR, $"粘贴节点时发生异常:{ex}");
}
// SereinEnv.WriteLine(InfoType.INFO, $"剪贴板文本内容: {clipboardText}");
}
else if (Clipboard.ContainsImage())
{
// var image = Clipboard.GetImage();
}
else
{
SereinEnv.WriteLine(InfoType.INFO, "剪贴板中没有可识别的数据。");
}
}
#endregion
/// <summary> /// <summary>
/// 卸载DLL文件清空当前项目
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void UnloadAllButton_Click(object sender, RoutedEventArgs e)
{
EnvDecorator.ClearAll();
}
/// <summary>
/// 卸载DLL文件清空当前项目
/// </summary>
private void UnloadAllAssemblies()
{
DllStackPanel.Children.Clear();
FlowChartCanvas.Children.Clear();
Connections.Clear();
NodeControls.Clear();
//currentLine = null;
//startConnectNodeControl = null;
MessageBox.Show("所有DLL已卸载。", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
}
/* /// <summary>
/// 对象装箱测试 /// 对象装箱测试
/// </summary> /// </summary>
/// <param name="sender"></param> /// <param name="sender"></param>
@@ -2913,31 +3005,6 @@ namespace Serein.Workbench
data = SerinExpressionEvaluator.Evaluate(exp,result!, out isChange); data = SerinExpressionEvaluator.Evaluate(exp,result!, out isChange);
SereinEnv.WriteLine(InfoType.INFO, $"{exp} => {data}"); SereinEnv.WriteLine(InfoType.INFO, $"{exp} => {data}");
} }
*/
/// <summary>
/// 卸载DLL文件清空当前项目
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void UnloadAllButton_Click(object sender, RoutedEventArgs e)
{
EnvDecorator.ClearAll();
}
/// <summary>
/// 卸载DLL文件清空当前项目
/// </summary>
private void UnloadAllAssemblies()
{
DllStackPanel.Children.Clear();
FlowChartCanvas.Children.Clear();
Connections.Clear();
NodeControls.Clear();
//currentLine = null;
//startConnectNodeControl = null;
MessageBox.Show("所有DLL已卸载。", "信息", MessageBoxButton.OK, MessageBoxImage.Information);
}
} }
} }

View File

@@ -19,7 +19,7 @@
</UserControl.Resources> </UserControl.Resources>
<Border BorderBrush="#8DE9FD" BorderThickness="4" > <Border BorderBrush="#8DE9FD" BorderThickness="1">
<Grid> <Grid>
<Grid.ToolTip> <Grid.ToolTip>
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding NodeModel.MethodDetails}" /> <ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding NodeModel.MethodDetails}" />
@@ -32,17 +32,14 @@
<Border x:Name="InterruptBorder" Tag="{Binding NodeModel.DebugSetting.IsInterrupt}"> <Border x:Name="InterruptBorder" Tag="{Binding NodeModel.DebugSetting.IsInterrupt}">
<Border.Style> <Border.Style>
<Style TargetType="Border"> <Style TargetType="Border">
<!--默认无边框-->
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<Style.Triggers> <Style.Triggers>
<!--NodeModel.DebugSetting.IsInterrupt-->
<!--<DataTrigger Binding="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType={x:Type local:ActionNodeControl}}, Path=DataContext.DebugSetting.IsInterrupt}" Value="True">-->
<!--<DataTrigger Binding="{Binding DebugSetting.IsInterrupt, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Value="True">-->
<DataTrigger Binding="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}" Value="True"> <DataTrigger Binding="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}" Value="True">
<Setter Property="BorderBrush" Value="Red" /> <Setter Property="BorderBrush" Value="Red" />
<Setter Property="BorderThickness" Value="2" /> <Setter Property="BorderThickness" Value="2" />
<Setter Property="Background" Value="#80000000" /> </DataTrigger>
<DataTrigger Binding="{Binding Path=Tag,RelativeSource={RelativeSource Mode=Self}}" Value="False">
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
</DataTrigger> </DataTrigger>
</Style.Triggers> </Style.Triggers>
</Style> </Style>
@@ -50,9 +47,6 @@
<Grid Background="#8DE9FD" > <Grid Background="#8DE9FD" >
<Grid.RowDefinitions> <Grid.RowDefinitions>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
<RowDefinition Height="*"/> <RowDefinition Height="*"/>
@@ -62,10 +56,6 @@
</Grid.RowDefinitions> </Grid.RowDefinitions>
<Grid Grid.Row="0" > <Grid Grid.Row="0" >
<!--<Grid Grid.Row="0" >-->
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/> <ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -79,19 +69,11 @@
<local:NextStepJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="NextStepJunctionControl" HorizontalAlignment="Right" Grid.RowSpan="2"/> <local:NextStepJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="NextStepJunctionControl" HorizontalAlignment="Right" Grid.RowSpan="2"/>
</Grid> </Grid>
<themes:MethodDetailsControl Grid.Row="2" x:Name="MethodDetailsControl" MethodDetails="{Binding NodeModel.MethodDetails}"/>
<Border Grid.Row="2" x:Name="ParameterProtectionMask" Background="LightBlue" Opacity="0.5" BorderThickness="0"
<!--<StackPanel Background="#8DE9FD" >
</StackPanel>-->
<themes:MethodDetailsControl x:Name="MethodDetailsControl" Grid.Row="2" MethodDetails="{Binding NodeModel.MethodDetails}"/>
<!-- ParameterProtectionMask 参数保护 -->
<!--取反 Visibility="{Binding DebugSetting.IsEnable, Converter={StaticResource InvertedBoolConverter}, ConverterParameter=Inverted}"-->
<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="3" Background="#D5F0FC" > <Grid Grid.Row="3" Background="#D5F0FC" >
<Grid.ColumnDefinitions> <Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/> <ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/> <ColumnDefinition Width="*"/>
@@ -110,20 +92,20 @@
</Grid> </Grid>
<StackPanel Grid.Row="4" Background="Azure" Orientation="Horizontal" Margin="3"> <StackPanel Grid.Row="4" Background="Azure" Orientation="Horizontal" Margin="3">
<StackPanel Orientation="Horizontal" Margin="2,1,2,1"> <StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding NodeModel.DebugSetting.IsEnable, Mode=TwoWay}"/> <CheckBox IsChecked="{Binding NodeModel.DebugSetting.IsEnable, Mode=TwoWay}"/>
<TextBlock Text="是否使能" HorizontalAlignment="Left" VerticalAlignment="Center"/> <TextBlock Text="是否使能"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" Margin="2,1,2,1"> <StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay}"/> <CheckBox IsChecked="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay}"/>
<TextBlock Text="参数保护" HorizontalAlignment="Left" VerticalAlignment="Center"/> <TextBlock Text="参数保护"/>
</StackPanel> </StackPanel>
<StackPanel Orientation="Horizontal" Margin="2,1,2,1"> <StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding NodeModel.DebugSetting.IsInterrupt, Mode=TwoWay}"/> <CheckBox IsChecked="{Binding NodeModel.DebugSetting.IsInterrupt, Mode=TwoWay}"/>
<TextBlock Text="中断节点" HorizontalAlignment="Left" VerticalAlignment="Center"/> <TextBlock Text="中断节点"/>
</StackPanel> </StackPanel>
</StackPanel> </StackPanel>
@@ -131,10 +113,6 @@
</Grid> </Grid>
</Border> </Border>
<!--Visibility="{Binding IsEnable, Converter={StaticResource BoolToVisConverter}, ConverterParameter=False}"-->
</Grid> </Grid>
</Border> </Border>
</local:NodeControlBase> </local:NodeControlBase>

View File

@@ -72,6 +72,8 @@ namespace Serein.Workbench.Node.View
return []; return [];
} }
} }
} }

View File

@@ -15,7 +15,21 @@ using System.Threading;
namespace Serein.Workbench.Node.View namespace Serein.Workbench.Node.View
{ {
internal static class MyUIFunc
{
public static Pen CreateAndFreezePen()
{
// 创建Pen
Pen pen = new Pen(Brushes.Black, 1);
// 冻结Pen
if (pen.CanFreeze)
{
pen.Freeze();
}
return pen;
}
}
public class ParamsArgControl: Shape public class ParamsArgControl: Shape
{ {
@@ -27,8 +41,6 @@ namespace Serein.Workbench.Node.View
this.MouseMove += ParamsArgControl_MouseMove; this.MouseMove += ParamsArgControl_MouseMove;
this.MouseLeave += ParamsArgControl_MouseLeave; this.MouseLeave += ParamsArgControl_MouseLeave;
AddOrRemoveParamsTask = AddAsync; AddOrRemoveParamsTask = AddAsync;
} }
@@ -84,9 +96,9 @@ namespace Serein.Workbench.Node.View
// 圆形部分 // 圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
drawingContext.DrawGeometry(brush, new Pen(Brushes.Black, 1), ellipse); drawingContext.DrawGeometry(brush, MyUIFunc.CreateAndFreezePen(), ellipse);
} }
private bool isMouseOver; // 鼠标悬停状态 private bool isMouseOver; // 鼠标悬停状态

View File

@@ -50,7 +50,7 @@ namespace Serein.Workbench.Node.View
// 绘制连接器的圆形部分 // 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse); drawingContext.DrawGeometry(background, MyUIFunc.CreateAndFreezePen(), ellipse);
// 定义三角形的间距 // 定义三角形的间距
double triangleOffsetX = 4; // 三角形与圆形的间距 double triangleOffsetX = 4; // 三角形与圆形的间距
@@ -66,7 +66,7 @@ namespace Serein.Workbench.Node.View
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
} }
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry); drawingContext.DrawGeometry(background, MyUIFunc.CreateAndFreezePen(), pathGeometry);
} }
} }

View File

@@ -46,7 +46,7 @@ namespace Serein.Workbench.Node.View
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse); drawingContext.DrawGeometry(background, MyUIFunc.CreateAndFreezePen(), ellipse);
@@ -65,7 +65,7 @@ namespace Serein.Workbench.Node.View
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
} }
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry); drawingContext.DrawGeometry(background, MyUIFunc.CreateAndFreezePen(), pathGeometry);
// 绘制标签 // 绘制标签
//var formattedText = new FormattedText( //var formattedText = new FormattedText(

View File

@@ -34,7 +34,7 @@ namespace Serein.Workbench.Node.View
var circlePoint = new Point(circleCenterX, circleCenterY); var circlePoint = new Point(circleCenterX, circleCenterY);
// 绘制连接器的圆形部分 // 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse); drawingContext.DrawGeometry(background, MyUIFunc.CreateAndFreezePen(), ellipse);
// 绘制连接器的圆形部分 // 绘制连接器的圆形部分
//var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); //var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
@@ -54,7 +54,7 @@ namespace Serein.Workbench.Node.View
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
} }
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry); drawingContext.DrawGeometry(background, MyUIFunc.CreateAndFreezePen(), pathGeometry);
} }
} }
} }

View File

@@ -39,7 +39,7 @@ namespace Serein.Workbench.Node.View
// 绘制连接器的圆形部分 // 绘制连接器的圆形部分
var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2); var ellipse = new EllipseGeometry(circlePoint, connectorSize / 2, connectorSize / 2);
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), ellipse); drawingContext.DrawGeometry(background, MyUIFunc.CreateAndFreezePen(), ellipse);
// 定义三角形的间距 // 定义三角形的间距
double triangleOffsetX = 4; // 三角形与圆形的间距 double triangleOffsetX = 4; // 三角形与圆形的间距
@@ -55,7 +55,7 @@ namespace Serein.Workbench.Node.View
context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY + 4.5), true, false);
context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false); context.LineTo(new Point(triangleCenterX, triangleCenterY - 4.5), true, false);
} }
drawingContext.DrawGeometry(background, new Pen(Brushes.Black, 1), pathGeometry); drawingContext.DrawGeometry(background, MyUIFunc.CreateAndFreezePen(), pathGeometry);
} }
} }
} }

View File

@@ -22,6 +22,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;

View File

@@ -103,9 +103,6 @@ namespace Serein.Workbench.Node.View
/// </summary> /// </summary>
public class ConnectionControl public class ConnectionControl
{ {
/// <summary> /// <summary>
/// 所在的画布 /// 所在的画布
/// </summary> /// </summary>