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

@@ -102,17 +102,23 @@
<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">
<ListBox ItemsSource="{Binding Nodes}" VirtualizingPanel.IsVirtualizing="True" VirtualizingPanel.VirtualizationMode="Recycling">
<ListBox.ItemTemplate>
<DataTemplate>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
<StackPanel x:Name="FlowChartStackPanel"
ClipToBounds="True">
<!-- 虚拟化 VirtualizingStackPanel.IsVirtualizing="True" -->
<Canvas
x:Name="FlowChartCanvas"
Background="#E1FBEA"
AllowDrop="True"
Width="2000"
Height="2000"
Width="1920"
Height="1080"
MouseLeftButtonDown ="FlowChartCanvas_MouseLeftButtonDown"
MouseLeftButtonUp="FlowChartCanvas_MouseLeftButtonUp"
MouseDown="FlowChartCanvas_MouseDown"

View File

@@ -14,11 +14,14 @@ using Serein.Workbench.Node.View;
using Serein.Workbench.Node.ViewModel;
using Serein.Workbench.Themes;
using Serein.Workbench.Tool;
using SqlSugar.Extensions;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
@@ -188,11 +191,7 @@ namespace Serein.Workbench
InitFlowEnvironmentEvent(); // 配置环境事件
if (App.FlowProjectData is not null)
{
EnvDecorator.LoadProject(new FlowEnvInfo { Project = App.FlowProjectData }, App.FileDataPath); // 加载项目
}
}
@@ -253,6 +252,13 @@ namespace Serein.Workbench
#region
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)
{
@@ -659,15 +665,17 @@ namespace Serein.Workbench
}
}
FlowChartCanvas.Children.Remove(nodeControl);
nodeControl.RemoveAllConection();
NodeControls.Remove(nodeControl.ViewModel.NodeModel.Guid);
}
/// <summary>
/// 编辑项目时添加节点
/// 添加节点事件
/// </summary>
/// <param name="nodeDataBase"></param>
/// <param name="eventArgs">添加节点事件参数</param>
/// <exception cref="NotImplementedException"></exception>
private void FlowEnvironment_NodeCreateEvent(NodeCreateEventArgs eventArgs)
{
@@ -738,6 +746,8 @@ namespace Serein.Workbench
NodeTreeViewer.AddGlobalFlipFlop(EnvDecorator, node); // 新增的触发器节点添加到全局触发器
}
}
GC.Collect();
#endregion
}
@@ -819,7 +829,10 @@ namespace Serein.Workbench
//{
// nodeControl.ViewModel.IsInterrupt = true;
//}
if(nodeControl.ContextMenu == null)
{
return;
}
foreach (var menuItem in nodeControl.ContextMenu.Items)
{
if (menuItem is MenuItem menu)
@@ -2669,147 +2682,16 @@ namespace Serein.Workbench
#region
if (Keyboard.Modifiers == ModifierKeys.Control)
{
#region
if (e.Key == Key.C && selectNodeControls.Count > 0)
{
// 处理复制操作
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;
CpoyNodeInfo();
}
#endregion
#region
else if (e.Key == Key.V)
{
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;
}
PasteNodeInfo();
}
}
#endregion
if (e.KeyStates == Keyboard.GetKeyStates(Key.Escape))
{
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>
/// 卸载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>
/// <param name="sender"></param>
@@ -2913,31 +3005,6 @@ namespace Serein.Workbench
data = SerinExpressionEvaluator.Evaluate(exp,result!, out isChange);
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>
<Border BorderBrush="#8DE9FD" BorderThickness="4" >
<Border BorderBrush="#8DE9FD" BorderThickness="1">
<Grid>
<Grid.ToolTip>
<ToolTip Background="LightYellow" Foreground="#071042" Content="{Binding NodeModel.MethodDetails}" />
@@ -32,17 +32,14 @@
<Border x:Name="InterruptBorder" Tag="{Binding NodeModel.DebugSetting.IsInterrupt}">
<Border.Style>
<Style TargetType="Border">
<!--默认无边框-->
<Setter Property="BorderBrush" Value="Transparent" />
<Setter Property="BorderThickness" Value="0" />
<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">
<Setter Property="BorderBrush" Value="Red" />
<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>
</Style.Triggers>
</Style>
@@ -50,9 +47,6 @@
<Grid Background="#8DE9FD" >
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
<RowDefinition Height="*"/>
@@ -62,10 +56,6 @@
</Grid.RowDefinitions>
<Grid Grid.Row="0" >
<!--<Grid Grid.Row="0" >-->
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="*"/>
@@ -79,19 +69,11 @@
<local:NextStepJunctionControl Grid.Column="2" MyNode="{Binding NodeModel}" x:Name="NextStepJunctionControl" HorizontalAlignment="Right" Grid.RowSpan="2"/>
</Grid>
<!--<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"
<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"
Visibility="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay,
Converter={StaticResource InvertedBoolConverter},ConverterParameter=Normal}" />
<Grid Grid.Row="3" Background="#D5F0FC" >
Converter={StaticResource InvertedBoolConverter}, ConverterParameter=Normal}" />
<Grid Grid.Row="3" Background="#D5F0FC" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="*"/>
@@ -110,20 +92,20 @@
</Grid>
<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}"/>
<TextBlock Text="是否使能" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Text="是否使能"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="2,1,2,1">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding NodeModel.MethodDetails.IsProtectionParameter, Mode=TwoWay}"/>
<TextBlock Text="参数保护" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Text="参数保护"/>
</StackPanel>
<StackPanel Orientation="Horizontal" Margin="2,1,2,1">
<StackPanel Orientation="Horizontal">
<CheckBox IsChecked="{Binding NodeModel.DebugSetting.IsInterrupt, Mode=TwoWay}"/>
<TextBlock Text="中断节点" HorizontalAlignment="Left" VerticalAlignment="Center"/>
<TextBlock Text="中断节点"/>
</StackPanel>
</StackPanel>
@@ -131,10 +113,6 @@
</Grid>
</Border>
<!--Visibility="{Binding IsEnable, Converter={StaticResource BoolToVisConverter}, ConverterParameter=False}"-->
</Grid>
</Border>
</local:NodeControlBase>

View File

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