效果图
项目中需要做一个机台的平面视图,点击其中一个料盒时,弹出该料盒的料管列表,用WPF示例做了一下,效果如下:

用户控件XAML
1 <UserControl x:Class="WpfApp1.Views.BoardStackControl"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
5 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
6 xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
7 xmlns:local="clr-namespace:WpfApp1.Views"
8 xmlns:wpfapp1="clr-namespace:WpfApp1"
9 d:DataContext="{d:DesignInstance Type=wpfapp1:MainViewModel}"
10 Width="224" Height="300"
11 mc:Ignorable="d"
12 d:DesignHeight="300" d:DesignWidth="250">
13 <UserControl.DataContext>
14 <wpfapp1:MainViewModel />
15 </UserControl.DataContext>
16 <Grid>
17 <!--<ItemsControl ItemsSource="{Binding NestGroups}">
18 <ItemsControl.ItemsPanel>
19 <ItemsPanelTemplate>
20 <StackPanel Orientation="Horizontal"></StackPanel>
21 </ItemsPanelTemplate>
22 </ItemsControl.ItemsPanel>
23 <ItemsControl.ItemTemplate>
24 <DataTemplate>-->
25 <Border Margin="1">
26 <Grid Width="220" Height="220">
27 <Ellipse Stroke="#dcdfe3" StrokeThickness="3" Width="220" Height="220"/>
28 <Ellipse Stroke="#dcdfe3" StrokeThickness="3" Width="80" Height="80" HorizontalAlignment="Center" VerticalAlignment="Center"/>
29 <ItemsControl ItemsSource="{Binding LeftTubes3}">
30 <ItemsControl.ItemsPanel>
31 <ItemsPanelTemplate>
32 <Canvas/>
33 </ItemsPanelTemplate>
34 </ItemsControl.ItemsPanel>
35 <ItemsControl.ItemContainerStyle>
36 <Style TargetType="ContentPresenter">
37 <Setter Property="Canvas.Left" Value="{Binding X, Mode=OneWay}"/>
38 <Setter Property="Canvas.Top" Value="{Binding Y, Mode=OneWay}"/>
39 <Setter Property="RenderTransformOrigin" Value="0.5,0.5"/>
40 <Setter Property="RenderTransform">
41 <Setter.Value>
42 <RotateTransform Angle="{Binding Angle}"/>
43 </Setter.Value>
44 </Setter>
45 </Style>
46 </ItemsControl.ItemContainerStyle>
47 <ItemsControl.ItemTemplate>
48 <DataTemplate>
49 <Grid>
50 <Border Width="35" Height="50" Tag="{Binding .}" x:Name="animatedBorder" MouseLeftButtonDown="Border_MouseLeftButtonDown"
51 CornerRadius="3" Background="#FFE6E6E6" BorderBrush="Gray" BorderThickness="1">
52 <ItemsControl ItemsSource="{Binding Tubes}" Margin="2" IsHitTestVisible="False">
53 <ItemsControl.ItemsPanel>
54 <ItemsPanelTemplate>
55 <UniformGrid Columns="{Binding Rows}" Rows="{Binding Cols}" IsHitTestVisible="False"/>
56 </ItemsPanelTemplate>
57 </ItemsControl.ItemsPanel>
58 <ItemsControl.ItemTemplate>
59 <DataTemplate>
60 <Ellipse Width="{Binding Width}" Height="{Binding Height}" Fill="#FF4F81BD" Margin="{Binding Margin}" Stroke="Black" StrokeThickness="0.5" IsHitTestVisible="False"/>
61 </DataTemplate>
62 </ItemsControl.ItemTemplate>
63 </ItemsControl>
64 <Border.Triggers>
65 <EventTrigger RoutedEvent="MouseLeftButtonUp">
66 <BeginStoryboard>
67 <Storyboard>
68 <DoubleAnimation Storyboard.TargetProperty="Opacity" From="1" To="0.5" Duration="0:0:0.1" AutoReverse="True"/>
69 </Storyboard>
70 </BeginStoryboard>
71 </EventTrigger>
72 </Border.Triggers>
73 </Border>
74 <Border Width="20" Height="20" CornerRadius="10" Background="#FF4F81BD" BorderBrush="White" BorderThickness="1" HorizontalAlignment="Center" VerticalAlignment="Top" Margin="0,-10,0,0">
75 <TextBlock Text="{Binding Index}" Foreground="White" FontWeight="Bold" HorizontalAlignment="Center" VerticalAlignment="Center"/>
76 </Border>
77 </Grid>
78 </DataTemplate>
79 </ItemsControl.ItemTemplate>
80 </ItemsControl>
81 </Grid>
82 </Border>
83 <!--</DataTemplate>
84 </ItemsControl.ItemTemplate>
85 </ItemsControl>-->
86
87 </Grid>
88 </UserControl>
89
90
View Code
用户控件XMAL.CS
1 using CommunityToolkit.Mvvm.Messaging;
2 using Microsoft.Extensions.Logging;
3 using System.Collections.ObjectModel;
4 using System.Diagnostics;
5 using System.Windows;
6 using System.Windows.Controls;
7 using System.Windows.Input;
8 using System.Windows.Media;
9 using WpfApp1.Entities;
10
11 namespace WpfApp1.Views;
12
13 public partial class BoardStackControl : UserControl
14 {
15 public BoardStackControl()
16 {
17 InitializeComponent();
18
19 }
20
21 public static readonly RoutedEvent BorderClickedEvent =
22 EventManager.RegisterRoutedEvent(
23 "BorderClicked",
24 RoutingStrategy.Bubble,
25 typeof(RoutedEventHandler),
26 typeof(BoardStackControl)
27 );
28
29 public event RoutedEventHandler BorderClicked
30 {
31 add => AddHandler(BorderClickedEvent, value);
32 remove => RemoveHandler(BorderClickedEvent, value);
33 }
34
35 private void Border_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
36 {
37 if (sender is Border border && border.DataContext is BoxPosition boxPosition)
38 {
39 // 触发路由事件,并携带索引
40 var args = new RoutedEventArgs(BorderClickedEvent, boxPosition.Index);
41 RaiseEvent(args);
42 e.Handled = true;
43 }
44 }
45
46 }
View Code
主窗口XAML
1 <Window x:Class="WpfApp1.MainWindow"
2 xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
3 xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
4 xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
5 xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
6 xmlns:local="clr-namespace:WpfApp1"
7 xmlns:view="clr-namespace:WpfApp1.Views"
8 mc:Ignorable="d"
9 Title="MainWindow" Height="450" Width="800">
10 <Window.DataContext>
11 <local:MainViewModel />
12 </Window.DataContext>
13 <Grid>
14 <Border BorderBrush="Gray" BorderThickness="1">
15 <Grid>
16 <Grid.RowDefinitions>
17 <RowDefinition Height="100"/>
18 <RowDefinition Height="300"/>
19 </Grid.RowDefinitions>
20 <Grid.ColumnDefinitions>
21 <ColumnDefinition Width="*"></ColumnDefinition>
22 <ColumnDefinition Width="*"></ColumnDefinition>
23 <ColumnDefinition Width="*"></ColumnDefinition>
24 </Grid.ColumnDefinitions>
25
26 <Border Grid.ColumnSpan="3" Grid.Row="0" BorderBrush="LightGray" BorderThickness="0,0,0,1">
27 <Grid>
28 <Rectangle Fill="#FFD3D3D3" Height="60" VerticalAlignment="Center"/>
29
30 <Path Data="M117 91q-6-7 1-12l2-26q-3-1-2-7L85 34q-3 4-8 1L54 48q0 4-5 5L48 54 38 61l-1-1 10-7-3-3-10 6-1-1 10-6 1-1L44 47q1-4 5-4L75 28c2-4 6-5 10-2l40 15q9 0 6 9l2 29q7 5 0 13l14 8v7H101v-7Z"
31 Fill="#FF4F81BD" Stroke="Black" StrokeThickness="1"
32 HorizontalAlignment="Center" VerticalAlignment="Center"
33 Margin="-20,0,0,0" />
34 </Grid>
35 </Border>
36
37 <view:BoardStackControl Grid.Row="1" Grid.Column="0" BorderClicked="Rack1Control_BorderClicked" />
38 <view:BoardStackControl Grid.Row="1" Grid.Column="1" BorderClicked="Rack2Control_BorderClicked" />
39 <view:BoardStackControl Grid.Row="1" Grid.Column="2" BorderClicked="Rack3Control_BorderClicked" />
40 </Grid>
41 </Border>
42 </Grid>
43 </Window>
View Code
主窗口XAML.CS
1 using System.Diagnostics;
2 using System.Text;
3 using System.Windows;
4 using System.Windows.Controls;
5 using System.Windows.Data;
6 using System.Windows.Documents;
7 using System.Windows.Input;
8 using System.Windows.Media;
9 using System.Windows.Media.Imaging;
10 using System.Windows.Navigation;
11 using System.Windows.Shapes;
12 using WpfApp1.Entities;
13 using WpfApp1.Views;
14
15 namespace WpfApp1
16 {
17 /// <summary>
18 /// Interaction logic for MainWindow.xaml
19 /// </summary>
20 public partial class MainWindow : Window
21 {
22 public MainWindow()
23 {
24 InitializeComponent();
25 }
26
27 private void Rack1Control_BorderClicked(object sender, RoutedEventArgs e)
28 {
29 if (e.OriginalSource is int index)
30 {
31 MessageBox.Show($"点击了第 1 个板栈的第 {index} 个 Border");
32 ExecuteMainWindowMethod(index);
33 }
34 }
35 private void Rack2Control_BorderClicked(object sender, RoutedEventArgs e)
36 {
37 if (e.OriginalSource is int index)
38 {
39 MessageBox.Show($"点击了第 2 个板栈的第 {index} 个 Border");
40 ExecuteMainWindowMethod(index);
41 }
42 }
43 private void Rack3Control_BorderClicked(object sender, RoutedEventArgs e)
44 {
45 if (e.OriginalSource is int index)
46 {
47 MessageBox.Show($"点击了第 3 个板栈的第 {index} 个 Border");
48 ExecuteMainWindowMethod(index);
49 }
50 }
51
52 private void ExecuteMainWindowMethod(int index)
53 {
54 //// 这里编写 MainWindow 的具体逻辑
55 //// 例如更新 UI 或处理业务逻辑
56 //var vm = (MainViewModel)this.DataContext;
57 //vm.RackClickedCommand.Execute($"2_{deviceCode}_{rackIndex}");
58 }
59 }
60 }
View Code
主窗口ViewModel
1 using CommunityToolkit.Mvvm.ComponentModel;
2 using CommunityToolkit.Mvvm.Input;
3 using CommunityToolkit.Mvvm.Messaging;
4 using System;
5 using System.Collections.Generic;
6 using System.ComponentModel;
7 using System.Diagnostics;
8 using System.Runtime.CompilerServices;
9 using System.Windows.Input;
10 using WpfApp1.Entities;
11 using WpfApp1.Views;
12
13 namespace WpfApp1
14 {
15 public partial class MainViewModel
16 {
17
18 public MainViewModel()
19 {
20
21 LeftTubes3 = CreateCircularTubes(12, 18, 110, 50);
22
23 //IsActive = true;
24 }
25
26 #region 左侧板栈UI
27
28 public List<BoxPosition> LeftTubes3 { get; set; } = [];
29
30 private static List<BoxPosition> CreateCircularTubes(int count, int tubs, double outerRadius, double innerRadius)
31 {
32 var positions = new List<BoxPosition>();
33 double centerX = outerRadius;
34 double centerY = outerRadius;
35
36 for (int i = 0; i < count; i++)
37 {
38 // 计算角度 (360度均匀分布)
39 double angleDeg = 360.0 * i / count;
40 double angleRad = angleDeg * Math.PI / 180.0;
41
42 // 计算位置 (在内外半径之间)
43 double radius = (outerRadius + innerRadius) / 2;
44 double x = centerX + radius * Math.Cos(angleRad) - 17; // 25是料盒宽度的一半
45 double y = centerY + radius * Math.Sin(angleRad) - 25; // 35是料盒高度的一半
46
47 // 行数和列数
48 var rows = 2;
49 var cols = 3;
50 var margin = 2;
51 var width = 10;
52 var height = 10;
53 switch (tubs)
54 {
55 case 3:
56 rows = 1;
57 cols = 3;
58 margin = 2;
59 width = 10;
60 height = 10;
61 break;
62 //case 6:
63 // rows = 2;
64 // cols = 3;
65 // break;
66 case 12:
67 rows = 3;
68 cols = 4;
69 margin = 1;
70 width = 5;
71 height = 5;
72 break;
73 case 18:
74 rows = 3;
75 cols = 6;
76 margin = 1;
77 width = 4;
78 height = 4;
79 break;
80 case 96:
81 rows = 8;
82 cols = 12;
83 margin = 0;
84 width = 1;
85 height = 1;
86 break;
87 }
88
89 // 创建6个料管
90 var tubes = new List<Tube>();
91 for (int j = 0; j < tubs; j++)
92 {
93 tubes.Add(new Tube
94 {
95 Margin = margin,
96 Width = width,
97 Height = height,
98 });
99 }
100
101 positions.Add(new BoxPosition
102 {
103 Index = i + 1,
104 X = x,
105 Y = y,
106 Rows = rows,
107 Cols = cols,
108 Angle = angleDeg + 90, // 旋转角度等于位置角度
109 Tubes = tubes
110 });
111 }
112
113 return positions;
114 }
115
116
117 #endregion
118
119 }
120 }
View Code
Entities
1 using System;
2 using System.Collections.Generic;
3 using System.Linq;
4 using System.Text;
5 using System.Threading.Tasks;
6
7 namespace WpfApp1.Entities
8 {
9 public class NestGroup
10 {
11 public List<BoxPosition> Nests { get; set; } = [];
12 }
13 public class BoxPosition
14 {
15 public int Index { get; set; }
16 public double X { get; set; }
17 public double Y { get; set; }
18 public double Angle { get; set; }
19 public int Rows { get; set; } = 2;
20 public int Cols { get; set; } = 3;
21 public List<Tube> Tubes { get; set; } = [];
22 }
23
24 public class Tube
25 {
26 public int Margin { get; set; } = 2;
27 public int Width { get; set; } = 10;
28 public int Height { get; set; } = 10;
29 }
30 }
View Code