效果图
项目中需要做一个机台的平面视图,点击其中一个料盒时,弹出该料盒的料管列表,用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