feat - 添加WPFNodeDemo
parent
5955b36967
commit
ffb005fec6
@ -0,0 +1,3 @@
|
||||
<Solution>
|
||||
<Project Path="WpfNodeTest/WpfNodeTest.csproj" />
|
||||
</Solution>
|
||||
@ -0,0 +1,9 @@
|
||||
<Application x:Class="WpfNodeTest.App"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:local="clr-namespace:WpfNodeTest"
|
||||
StartupUri="MainWindow.xaml">
|
||||
<Application.Resources>
|
||||
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
@ -0,0 +1,10 @@
|
||||
using System.Windows;
|
||||
|
||||
[assembly: ThemeInfo(
|
||||
ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
|
||||
//(used if a resource is not found in the page,
|
||||
// or application resource dictionaries)
|
||||
ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
|
||||
//(used if a resource is not found in the page,
|
||||
// app, or any theme specific resource dictionaries)
|
||||
)]
|
||||
@ -0,0 +1,34 @@
|
||||
{
|
||||
"NodeTypes": [
|
||||
{
|
||||
"TypeName": "提升机",
|
||||
"DefaultInputCount": 1,
|
||||
"DefaultOutputCount": 1,
|
||||
"Description": "垂直运输设备"
|
||||
},
|
||||
{
|
||||
"TypeName": "电梯",
|
||||
"DefaultInputCount": 1,
|
||||
"DefaultOutputCount": 1,
|
||||
"Description": "楼层间运输"
|
||||
},
|
||||
{
|
||||
"TypeName": "工位",
|
||||
"DefaultInputCount": 1,
|
||||
"DefaultOutputCount": 0,
|
||||
"Description": "作业工位"
|
||||
},
|
||||
{
|
||||
"TypeName": "物流接驳位",
|
||||
"DefaultInputCount": 2,
|
||||
"DefaultOutputCount": 2,
|
||||
"Description": "物流中转点"
|
||||
},
|
||||
{
|
||||
"TypeName": "库位",
|
||||
"DefaultInputCount": 1,
|
||||
"DefaultOutputCount": 0,
|
||||
"Description": "存储位置"
|
||||
}
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,84 @@
|
||||
<Window x:Class="WpfNodeTest.MainWindow"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:local="clr-namespace:WpfNodeTest"
|
||||
xmlns:nodenetwork="clr-namespace:NodeNetwork.Views;assembly=NodeNetwork"
|
||||
xmlns:views="clr-namespace:WpfNodeTest.Views"
|
||||
mc:Ignorable="d"
|
||||
Title="MainWindow" Height="720" Width="1280">
|
||||
<!--基GRID-->
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="50"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<!--顶边功能区-->
|
||||
<Grid Grid.Row="0" Background="#2C2C2C">
|
||||
<StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0">
|
||||
<TextBlock Text="AGV接驳位编辑器" FontSize="16" FontWeight="Bold" Foreground="White" VerticalAlignment="Center" Margin="0,0,20,0"/>
|
||||
<Button Content="加载" Command="{Binding LoadFileCommand}" Width="80" Height="30" Margin="5,0"/>
|
||||
<Button Content="保存" Command="{Binding SaveFileCommand}" Width="80" Height="30" Margin="5,0"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<!--顶边功能区-->
|
||||
<!--工作区-->
|
||||
<Grid Grid.Row="1">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="300"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<!--侧边栏-->
|
||||
<Grid Grid.Column="0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="400"/>
|
||||
</Grid.RowDefinitions>
|
||||
<!--控件库-->
|
||||
<Grid Grid.Row="0" Background="#F5F5F5">
|
||||
<ScrollViewer VerticalScrollBarVisibility="Auto">
|
||||
<StackPanel Margin="10">
|
||||
<TextBlock Text="节点类型(拖拽到工作区)" FontWeight="Bold" FontSize="14" Margin="0,0,0,10"/>
|
||||
<ItemsControl ItemsSource="{Binding NodeTypeInfos}">
|
||||
<ItemsControl.ItemTemplate>
|
||||
<DataTemplate>
|
||||
<views:NodeTemplateControl />
|
||||
</DataTemplate>
|
||||
</ItemsControl.ItemTemplate>
|
||||
</ItemsControl>
|
||||
</StackPanel>
|
||||
</ScrollViewer>
|
||||
</Grid>
|
||||
<!--控件库-->
|
||||
<!--属性栏-->
|
||||
<Grid Grid.Row="1" Background="#F5F5F5">
|
||||
<StackPanel Margin="10" DataContext="{Binding SelectedNode}">
|
||||
<TextBlock Text="节点属性" FontWeight="Bold" FontSize="14" Margin="0,0,0,10"/>
|
||||
|
||||
<TextBlock Text="节点名称:" Margin="0,5,0,2"/>
|
||||
<TextBox Text="{Binding Name, UpdateSourceTrigger=PropertyChanged}" Height="25"/>
|
||||
|
||||
<TextBlock Text="节点类型:" Margin="0,10,0,2"/>
|
||||
<TextBox Text="{Binding NodeType, UpdateSourceTrigger=PropertyChanged}" Height="25"/>
|
||||
|
||||
<TextBlock Text="输入端口数:" Margin="0,10,0,2"/>
|
||||
<TextBox Text="{Binding InputCount, UpdateSourceTrigger=PropertyChanged}" Height="25"/>
|
||||
|
||||
<TextBlock Text="输出端口数:" Margin="0,10,0,2"/>
|
||||
<TextBox Text="{Binding OutputCount, UpdateSourceTrigger=PropertyChanged}" Height="25"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
<!--属性栏-->
|
||||
</Grid>
|
||||
<!--侧边栏-->
|
||||
<!--编辑器-->
|
||||
<Grid Grid.Column="1">
|
||||
<nodenetwork:NetworkView x:Name="networkView" ViewModel="{Binding Network}" AllowDrop="True" DragOver="NetworkView_DragOver" Drop="NetworkView_Drop" />
|
||||
</Grid>
|
||||
<!--编辑器-->
|
||||
</Grid>
|
||||
<!--工作区-->
|
||||
</Grid>
|
||||
<!--基GRID-->
|
||||
</Window>
|
||||
@ -0,0 +1,62 @@
|
||||
using System.Windows;
|
||||
using WpfNodeTest.Models;
|
||||
using WpfNodeTest.ViewModels;
|
||||
|
||||
namespace WpfNodeTest
|
||||
{
|
||||
/// <summary>
|
||||
/// Interaction logic for MainWindow.xaml
|
||||
/// </summary>
|
||||
public partial class MainWindow : Window
|
||||
{
|
||||
public MainWindow()
|
||||
{
|
||||
InitializeComponent();
|
||||
DataContext = new MainWindowViewModel();
|
||||
}
|
||||
|
||||
// 处理拖拽悬停事件,允许拖放
|
||||
private void NetworkView_DragOver(object sender, DragEventArgs e)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("DragOver事件触发");
|
||||
if (e.Data.GetDataPresent(typeof(NodeTypeInfo)))
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("检测到NodeTypeInfo数据");
|
||||
e.Effects = DragDropEffects.Copy;
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("未检测到NodeTypeInfo数据");
|
||||
e.Effects = DragDropEffects.None;
|
||||
}
|
||||
e.Handled = true;
|
||||
}
|
||||
|
||||
// 处理拖放事件,在工作区创建新节点
|
||||
private void NetworkView_Drop(object sender, DragEventArgs e)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Drop事件触发");
|
||||
if (e.Data.GetData(typeof(NodeTypeInfo)) is NodeTypeInfo nodeType && DataContext is MainWindowViewModel vm)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"创建节点: {nodeType.TypeName}");
|
||||
var dropPosition = e.GetPosition(networkView);
|
||||
|
||||
var node = new AGVDockingNode
|
||||
{
|
||||
Name = nodeType.TypeName,
|
||||
NodeType = nodeType.TypeName,
|
||||
InputCount = nodeType.DefaultInputCount,
|
||||
OutputCount = nodeType.DefaultOutputCount,
|
||||
Position = new Point(dropPosition.X, dropPosition.Y)
|
||||
};
|
||||
|
||||
vm.Network.Nodes.Edit(list => list.Add(node));
|
||||
System.Diagnostics.Debug.WriteLine($"节点已添加到位置: ({dropPosition.X}, {dropPosition.Y})");
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("Drop失败: 数据类型不匹配或ViewModel为空");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
using NodeNetwork.ViewModels;
|
||||
using NodeNetwork.Views;
|
||||
using ReactiveUI;
|
||||
|
||||
namespace WpfNodeTest.Models;
|
||||
|
||||
/// <summary>
|
||||
/// AGV接驳位节点模型
|
||||
/// </summary>
|
||||
public class AGVDockingNode : NodeViewModel
|
||||
{
|
||||
// 静态构造函数:注册节点视图
|
||||
static AGVDockingNode()
|
||||
{
|
||||
Splat.Locator.CurrentMutable.Register(() => new NodeView(), typeof(IViewFor<AGVDockingNode>));
|
||||
}
|
||||
// 节点唯一标识
|
||||
private string _nodeId = Guid.NewGuid().ToString();
|
||||
public string NodeId
|
||||
{
|
||||
get => _nodeId;
|
||||
set => this.RaiseAndSetIfChanged(ref _nodeId, value);
|
||||
}
|
||||
|
||||
// 节点类型(提升机、电梯、工位、物流接驳位、库位等)
|
||||
private string _nodeType = "默认";
|
||||
public string NodeType
|
||||
{
|
||||
get => _nodeType;
|
||||
set => this.RaiseAndSetIfChanged(ref _nodeType, value);
|
||||
}
|
||||
|
||||
// 输入端口数量
|
||||
private int _inputCount = 1;
|
||||
public int InputCount
|
||||
{
|
||||
get => _inputCount;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _inputCount, value);
|
||||
UpdatePorts();
|
||||
}
|
||||
}
|
||||
|
||||
// 输出端口数量
|
||||
private int _outputCount = 1;
|
||||
public int OutputCount
|
||||
{
|
||||
get => _outputCount;
|
||||
set
|
||||
{
|
||||
this.RaiseAndSetIfChanged(ref _outputCount, value);
|
||||
UpdatePorts();
|
||||
}
|
||||
}
|
||||
|
||||
public AGVDockingNode()
|
||||
{
|
||||
Name = "接驳位";
|
||||
UpdatePorts();
|
||||
}
|
||||
|
||||
// 根据输入输出数量更新端口
|
||||
private void UpdatePorts()
|
||||
{
|
||||
Inputs.Edit(list =>
|
||||
{
|
||||
list.Clear();
|
||||
for (int i = 0; i < InputCount; i++)
|
||||
{
|
||||
list.Add(new NodeInputViewModel
|
||||
{
|
||||
Name = $"入口{i + 1}",
|
||||
MaxConnections = int.MaxValue // 允许多个连接
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
Outputs.Edit(list =>
|
||||
{
|
||||
list.Clear();
|
||||
for (int i = 0; i < OutputCount; i++)
|
||||
{
|
||||
list.Add(new NodeOutputViewModel
|
||||
{
|
||||
Name = $"出口{i + 1}",
|
||||
MaxConnections = int.MaxValue // 允许多个连接
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,37 @@
|
||||
namespace WpfNodeTest.Models;
|
||||
|
||||
/// <summary>
|
||||
/// 节点图数据模型,用于序列化和反序列化
|
||||
/// </summary>
|
||||
public class NodeGraphData
|
||||
{
|
||||
public List<NodeData> Nodes { get; set; } = new();
|
||||
public List<ConnectionData> Connections { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点数据
|
||||
/// </summary>
|
||||
public class NodeData
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string Name { get; set; } = string.Empty;
|
||||
public string Type { get; set; } = string.Empty;
|
||||
public double X { get; set; }
|
||||
public double Y { get; set; }
|
||||
public int InputCount { get; set; }
|
||||
public int OutputCount { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 连接数据
|
||||
/// </summary>
|
||||
public class ConnectionData
|
||||
{
|
||||
public string Id { get; set; } = string.Empty;
|
||||
public string SourceNodeId { get; set; } = string.Empty;
|
||||
public int SourcePortIndex { get; set; }
|
||||
public string TargetNodeId { get; set; } = string.Empty;
|
||||
public int TargetPortIndex { get; set; }
|
||||
public double Weight { get; set; } = 1.0; // 权重(耗时),用于路径规划
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
namespace WpfNodeTest.Models;
|
||||
|
||||
/// <summary>
|
||||
/// 节点类型配置
|
||||
/// </summary>
|
||||
public class NodeTypeConfig
|
||||
{
|
||||
public List<NodeTypeInfo> NodeTypes { get; set; } = new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 节点类型信息
|
||||
/// </summary>
|
||||
public class NodeTypeInfo
|
||||
{
|
||||
public string TypeName { get; set; } = string.Empty; // 类型名称
|
||||
public int DefaultInputCount { get; set; } = 1; // 默认输入端口数
|
||||
public int DefaultOutputCount { get; set; } = 1; // 默认输出端口数
|
||||
public string Description { get; set; } = string.Empty; // 描述
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using WpfNodeTest.Models;
|
||||
|
||||
namespace WpfNodeTest.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 节点类型配置服务
|
||||
/// </summary>
|
||||
public class NodeTypeConfigService
|
||||
{
|
||||
private NodeTypeConfig? _config;
|
||||
private readonly string _configPath = "Config/NodeTypes.json";
|
||||
|
||||
/// <summary>
|
||||
/// 加载节点类型配置
|
||||
/// </summary>
|
||||
public NodeTypeConfig LoadConfig()
|
||||
{
|
||||
if (_config != null) return _config;
|
||||
|
||||
try
|
||||
{
|
||||
if (!File.Exists(_configPath))
|
||||
{
|
||||
// 配置文件不存在,返回默认配置
|
||||
_config = CreateDefaultConfig();
|
||||
return _config;
|
||||
}
|
||||
|
||||
var json = File.ReadAllText(_configPath);
|
||||
_config = JsonSerializer.Deserialize<NodeTypeConfig>(json) ?? CreateDefaultConfig();
|
||||
return _config;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 加载失败,返回默认配置
|
||||
System.Diagnostics.Debug.WriteLine($"加载配置文件失败: {ex.Message}");
|
||||
_config = CreateDefaultConfig();
|
||||
return _config;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建默认配置
|
||||
/// </summary>
|
||||
private NodeTypeConfig CreateDefaultConfig()
|
||||
{
|
||||
return new NodeTypeConfig
|
||||
{
|
||||
NodeTypes = new List<NodeTypeInfo>
|
||||
{
|
||||
new NodeTypeInfo { TypeName = "提升机", DefaultInputCount = 1, DefaultOutputCount = 1, Description = "垂直运输设备" },
|
||||
new NodeTypeInfo { TypeName = "电梯", DefaultInputCount = 1, DefaultOutputCount = 1, Description = "楼层间运输" },
|
||||
new NodeTypeInfo { TypeName = "工位", DefaultInputCount = 1, DefaultOutputCount = 0, Description = "作业工位" },
|
||||
new NodeTypeInfo { TypeName = "物流接驳位", DefaultInputCount = 1, DefaultOutputCount = 1, Description = "物流中转点" },
|
||||
new NodeTypeInfo { TypeName = "库位", DefaultInputCount = 1, DefaultOutputCount = 0, Description = "存储位置" }
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有节点类型名称
|
||||
/// </summary>
|
||||
public List<string> GetNodeTypeNames()
|
||||
{
|
||||
var config = LoadConfig();
|
||||
return config.NodeTypes.Select(t => t.TypeName).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据类型名称获取节点类型信息
|
||||
/// </summary>
|
||||
public NodeTypeInfo? GetNodeTypeInfo(string typeName)
|
||||
{
|
||||
var config = LoadConfig();
|
||||
return config.NodeTypes.FirstOrDefault(t => t.TypeName == typeName);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,106 @@
|
||||
using CommunityToolkit.Mvvm.ComponentModel;
|
||||
using CommunityToolkit.Mvvm.Input;
|
||||
using Microsoft.Win32;
|
||||
using NodeNetwork.ViewModels;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Reactive.Linq;
|
||||
using WpfNodeTest.Models;
|
||||
using WpfNodeTest.Services;
|
||||
|
||||
namespace WpfNodeTest.ViewModels;
|
||||
|
||||
/// <summary>
|
||||
/// 主窗口ViewModel
|
||||
/// </summary>
|
||||
public partial class MainWindowViewModel : ObservableObject
|
||||
{
|
||||
private readonly NodeGraphSerializer _serializer = new();
|
||||
private readonly NodeTypeConfigService _configService = new();
|
||||
private readonly PathPlanningService _pathPlanning = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private NetworkViewModel _network = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<string> _nodeTypes = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private ObservableCollection<NodeTypeInfo> _nodeTypeInfos = new();
|
||||
|
||||
[ObservableProperty]
|
||||
private AGVDockingNode? _selectedNode;
|
||||
|
||||
public MainWindowViewModel()
|
||||
{
|
||||
LoadNodeTypes();
|
||||
|
||||
// 监听节点选择变化
|
||||
Network.SelectedNodes.Connect().Subscribe(_ =>
|
||||
{
|
||||
SelectedNode = Network.SelectedNodes.Items.FirstOrDefault() as AGVDockingNode;
|
||||
});
|
||||
}
|
||||
|
||||
// 加载节点类型列表
|
||||
private void LoadNodeTypes()
|
||||
{
|
||||
var config = _configService.LoadConfig();
|
||||
NodeTypes = new ObservableCollection<string>(config.NodeTypes.Select(t => t.TypeName));
|
||||
NodeTypeInfos = new ObservableCollection<NodeTypeInfo>(config.NodeTypes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载节点图文件
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void LoadFile()
|
||||
{
|
||||
var dialog = new OpenFileDialog
|
||||
{
|
||||
Filter = "JSON文件|*.json",
|
||||
Title = "加载节点图"
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
Network = _serializer.LoadFromFile(dialog.FileName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存节点图文件
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void SaveFile()
|
||||
{
|
||||
var dialog = new SaveFileDialog
|
||||
{
|
||||
Filter = "JSON文件|*.json",
|
||||
Title = "保存节点图"
|
||||
};
|
||||
|
||||
if (dialog.ShowDialog() == true)
|
||||
{
|
||||
_serializer.SaveToFile(Network, dialog.FileName);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加新节点
|
||||
/// </summary>
|
||||
[RelayCommand]
|
||||
private void AddNode(string nodeType)
|
||||
{
|
||||
var typeInfo = _configService.GetNodeTypeInfo(nodeType);
|
||||
var node = new AGVDockingNode
|
||||
{
|
||||
Name = nodeType,
|
||||
NodeType = nodeType,
|
||||
InputCount = typeInfo?.DefaultInputCount ?? 1,
|
||||
OutputCount = typeInfo?.DefaultOutputCount ?? 1,
|
||||
Position = new System.Windows.Point(100, 100)
|
||||
};
|
||||
|
||||
Network.Nodes.Edit(list => list.Add(node));
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,11 @@
|
||||
<UserControl x:Class="WpfNodeTest.Views.NodeTemplateControl"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
Width="250" Height="60" Margin="5">
|
||||
<Border Background="#E0E0E0" BorderBrush="#999" BorderThickness="1" CornerRadius="5" Cursor="Hand">
|
||||
<StackPanel Margin="10" VerticalAlignment="Center">
|
||||
<TextBlock Text="{Binding TypeName}" FontWeight="Bold" FontSize="12"/>
|
||||
<TextBlock Text="{Binding Description}" FontSize="10" Foreground="#666" Margin="0,2,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</UserControl>
|
||||
@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Input;
|
||||
using WpfNodeTest.Models;
|
||||
|
||||
namespace WpfNodeTest.Views
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点模板控件
|
||||
/// </summary>
|
||||
public partial class NodeTemplateControl : UserControl
|
||||
{
|
||||
private Point _startPoint;
|
||||
private bool _isDragging;
|
||||
|
||||
public NodeTemplateControl()
|
||||
{
|
||||
InitializeComponent();
|
||||
PreviewMouseLeftButtonDown += OnPreviewMouseLeftButtonDown;
|
||||
PreviewMouseMove += OnPreviewMouseMove;
|
||||
}
|
||||
|
||||
// 记录鼠标按下位置
|
||||
private void OnPreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
|
||||
{
|
||||
_startPoint = e.GetPosition(null);
|
||||
_isDragging = false;
|
||||
}
|
||||
|
||||
// 鼠标移动时启动拖放
|
||||
private void OnPreviewMouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (e.LeftButton == MouseButtonState.Pressed && !_isDragging)
|
||||
{
|
||||
Point currentPosition = e.GetPosition(null);
|
||||
Vector diff = _startPoint - currentPosition;
|
||||
|
||||
// 移动超过阈值才启动拖拽
|
||||
if (Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
|
||||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance)
|
||||
{
|
||||
_isDragging = true;
|
||||
if (DataContext is NodeTypeInfo nodeType)
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine($"启动拖拽: {nodeType.TypeName}");
|
||||
DragDrop.DoDragDrop(this, nodeType, DragDropEffects.Copy);
|
||||
}
|
||||
else
|
||||
{
|
||||
System.Diagnostics.Debug.WriteLine("DataContext不是NodeTypeInfo类型");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,66 @@
|
||||
{
|
||||
"Nodes": [
|
||||
{
|
||||
"Id": "node-1",
|
||||
"Name": "工位A",
|
||||
"Type": "工位",
|
||||
"X": 100,
|
||||
"Y": 100,
|
||||
"InputCount": 1,
|
||||
"OutputCount": 1
|
||||
},
|
||||
{
|
||||
"Id": "node-2",
|
||||
"Name": "提升机B",
|
||||
"Type": "提升机",
|
||||
"X": 300,
|
||||
"Y": 100,
|
||||
"InputCount": 2,
|
||||
"OutputCount": 2
|
||||
},
|
||||
{
|
||||
"Id": "node-3",
|
||||
"Name": "接驳位C",
|
||||
"Type": "物流接驳位",
|
||||
"X": 500,
|
||||
"Y": 100,
|
||||
"InputCount": 2,
|
||||
"OutputCount": 2
|
||||
},
|
||||
{
|
||||
"Id": "node-4",
|
||||
"Name": "库位D",
|
||||
"Type": "库位",
|
||||
"X": 700,
|
||||
"Y": 100,
|
||||
"InputCount": 1,
|
||||
"OutputCount": 1
|
||||
}
|
||||
],
|
||||
"Connections": [
|
||||
{
|
||||
"Id": "conn-1",
|
||||
"SourceNodeId": "node-1",
|
||||
"SourcePortIndex": 0,
|
||||
"TargetNodeId": "node-2",
|
||||
"TargetPortIndex": 0,
|
||||
"Weight": 1.0
|
||||
},
|
||||
{
|
||||
"Id": "conn-2",
|
||||
"SourceNodeId": "node-2",
|
||||
"SourcePortIndex": 0,
|
||||
"TargetNodeId": "node-3",
|
||||
"TargetPortIndex": 0,
|
||||
"Weight": 1.0
|
||||
},
|
||||
{
|
||||
"Id": "conn-3",
|
||||
"SourceNodeId": "node-3",
|
||||
"SourcePortIndex": 0,
|
||||
"TargetNodeId": "node-4",
|
||||
"TargetPortIndex": 0,
|
||||
"Weight": 1.0
|
||||
}
|
||||
]
|
||||
}
|
||||
Loading…
Reference in New Issue