为柱状传感器图添加等级选择分为高中低

This commit is contained in:
YONGYE 2025-10-11 09:21:00 +08:00
parent db162f3be3
commit 18a63a1c1d
5 changed files with 285 additions and 163 deletions

View File

@ -1,4 +1,4 @@
<Window x:Class="WpfApp.MainWindow"
<Window x:Class="WpfApp.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"
@ -348,10 +348,10 @@
<!-- 左侧保存/申请类型 -->
<StackPanel Grid.Column="0" Orientation="Horizontal" VerticalAlignment="Center" Margin="0,0,10,0">
<TextBlock Text="像素/每微米:" VerticalAlignment="Center" Margin="0,0,5,0"/>
<ComboBox Width="80" Height="25" VerticalAlignment="Center">
<ComboBoxItem Content="低等"/>
<ComboBoxItem Content="中等" IsSelected="True"/>
<ComboBoxItem Content="高等"/>
<ComboBox x:Name="LevelComboBox" Width="80" Height="25" VerticalAlignment="Center" SelectionChanged="LevelComboBox_SelectionChanged">
<ComboBoxItem Content="低等" Tag="Low"/>
<ComboBoxItem Content="中等" Tag="Medium" IsSelected="True"/>
<ComboBoxItem Content="高等" Tag="High"/>
</ComboBox>
<!--<RadioButton Content="日志记录" VerticalAlignment="Center" Margin="10,0,0,0" Foreground="Black"/>-->
</StackPanel>

View File

@ -3,7 +3,9 @@ using LiveCharts;
using LiveCharts.Wpf;
using SqlSugar;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
using WpfApp.src.components;
using WpfApp.src.view;
@ -407,6 +409,37 @@ namespace WpfApp
//LineBarChartDemo lineBarChartDemo = new LineBarChartDemo();
//lineBarChartDemo.Show();
}
/// <summary>
/// 等级选择下拉框变化事件处理
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void LevelComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
if (sender is ComboBox comboBox && comboBox.SelectedItem is ComboBoxItem selectedItem)
{
// 根据选择的Tag确定等级
SensorLevel selectedLevel = SensorLevel.Medium; // 默认值
switch (selectedItem.Tag?.ToString())
{
case "Low":
selectedLevel = SensorLevel.Low;
break;
case "Medium":
selectedLevel = SensorLevel.Medium;
break;
case "High":
selectedLevel = SensorLevel.High;
break;
}
// 更新所有传感器图表的等级
Sensor1.Level = selectedLevel;
Sensor2.Level = selectedLevel;
Sensor3.Level = selectedLevel;
}
}
#endregion
}
}

View File

@ -3,10 +3,12 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:WpfApp.src.components"
mc:Ignorable="d" d:DesignWidth="200" >
<Border Background="White" BorderBrush="#CCCCCC" BorderThickness="1">
<Grid Margin="0,0,0,-1">
<Grid.RowDefinitions>
<RowDefinition Height="30"/>
<RowDefinition Height="30"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
@ -19,9 +21,33 @@
FontWeight="Bold"
Background="{Binding HeaderBackground, RelativeSource={RelativeSource AncestorType=UserControl}}"/>
<!-- 图表区域 -->
<Viewbox Grid.Row="1" Stretch="Uniform">
<!-- 等级选择栏 -->
<Grid Grid.Row="1" Margin="25,2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0"
Text="等级:"
VerticalAlignment="Center"
FontSize="10"
Margin="0,0,5,0"/>
<ComboBox Grid.Column="1"
Name="LevelComboBox"
Height="20"
FontSize="10"
SelectedValue="{Binding Level, RelativeSource={RelativeSource AncestorType=UserControl}}"
SelectedValuePath="Tag">
<ComboBoxItem Content="低" Tag="{x:Static local:SensorLevel.Low}"/>
<ComboBoxItem Content="中" Tag="{x:Static local:SensorLevel.Medium}" IsSelected="True"/>
<ComboBoxItem Content="高" Tag="{x:Static local:SensorLevel.High}"/>
</ComboBox>
</Grid>
<!-- 图表区域 -->
<Viewbox Grid.Row="2" Stretch="Uniform">
<Canvas Width="160" Height="440" Grid.Row="1" Background="White" Margin="20,20,20,3" Name="ChartCanvas">
<!-- 柱状图容器边框 - 左边界线 -->
<Line X1="40" Y1="20" X2="40" Y2="420" Stroke="Black" StrokeThickness="1"/>
@ -32,155 +58,47 @@
<!-- 底部边界线 -->
<Line X1="40" Y1="420" X2="120" Y2="420" Stroke="Black" StrokeThickness="1"/>
<!-- 大刻度线和标签 - 从40到-40每5个单位一个刻度 -->
<!-- 40 -->
<Line X1="120" Y1="20" X2="130" Y2="20" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="15" Text="40" FontSize="10"/>
<!-- 35 -->
<Line X1="40" Y1="45" X2="130" Y2="45" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="40" Text="35" FontSize="10"/>
<!-- 30 -->
<Line X1="40" Y1="70" X2="130" Y2="70" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="65" Text="30" FontSize="10"/>
<!-- 25 -->
<Line X1="40" Y1="95" X2="130" Y2="95" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="90" Text="25" FontSize="10"/>
<!-- 20 -->
<Line X1="40" Y1="120" X2="130" Y2="120" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="115" Text="20" FontSize="10"/>
<!-- 15 -->
<Line X1="40" Y1="145" X2="130" Y2="145" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="140" Text="15" FontSize="10"/>
<!-- 10 -->
<Line X1="40" Y1="170" X2="130" Y2="170" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="165" Text="10" FontSize="10"/>
<!-- 5 -->
<Line X1="40" Y1="195" X2="130" Y2="195" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="190" Text="5" FontSize="10"/>
<!-- 0 -->
<Line X1="40" Y1="220" X2="130" Y2="220" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="215" Text="0" FontSize="10"/>
<!-- -5 -->
<Line X1="40" Y1="245" X2="130" Y2="245" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="240" Text="-5" FontSize="10"/>
<!-- -10 -->
<Line X1="40" Y1="270" X2="130" Y2="270" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="265" Text="-10" FontSize="10"/>
<!-- -15 -->
<Line X1="40" Y1="295" X2="130" Y2="295" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="290" Text="-15" FontSize="10"/>
<!-- -20 -->
<Line X1="40" Y1="320" X2="130" Y2="320" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="315" Text="-20" FontSize="10"/>
<!-- -25 -->
<Line X1="40" Y1="345" X2="130" Y2="345" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="340" Text="-25" FontSize="10"/>
<!-- -30 -->
<Line X1="40" Y1="370" X2="130" Y2="370" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="365" Text="-30" FontSize="10"/>
<!-- -35 -->
<Line X1="40" Y1="395" X2="130" Y2="395" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="390" Text="-35" FontSize="10"/>
<!-- -40 -->
<Line X1="120" Y1="420" X2="130" Y2="420" Stroke="Black" StrokeThickness="1"/>
<TextBlock Canvas.Left="135" Canvas.Top="415" Text="-40" FontSize="10"/>
<!-- 小刻度线 - 每个大刻度之间3个小刻度 -->
<!-- 40到35之间的小刻度 -->
<Line X1="40" Y1="26.25" X2="125" Y2="26.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="32.5" X2="125" Y2="32.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="38.75" X2="125" Y2="38.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- 35到30之间的小刻度 -->
<Line X1="40" Y1="51.25" X2="125" Y2="51.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="57.5" X2="125" Y2="57.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="63.75" X2="125" Y2="63.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- 30到25之间的小刻度 -->
<Line X1="40" Y1="76.25" X2="125" Y2="76.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="82.5" X2="125" Y2="82.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="88.75" X2="125" Y2="88.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- 25到20之间的小刻度 -->
<Line X1="40" Y1="101.25" X2="125" Y2="101.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="107.5" X2="125" Y2="107.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="113.75" X2="125" Y2="113.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- 20到15之间的小刻度 -->
<Line X1="40" Y1="126.25" X2="125" Y2="126.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="132.5" X2="125" Y2="132.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="138.75" X2="125" Y2="138.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- 15到10之间的小刻度 -->
<Line X1="40" Y1="151.25" X2="125" Y2="151.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="157.5" X2="125" Y2="157.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="163.75" X2="125" Y2="163.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- 10到5之间的小刻度 -->
<Line X1="40" Y1="176.25" X2="125" Y2="176.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="182.5" X2="125" Y2="182.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="188.75" X2="125" Y2="188.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- 5到0之间的小刻度 -->
<Line X1="40" Y1="201.25" X2="125" Y2="201.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="207.5" X2="125" Y2="207.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="213.75" X2="125" Y2="213.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- 0到-5之间的小刻度 -->
<Line X1="40" Y1="226.25" X2="125" Y2="226.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="232.5" X2="125" Y2="232.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="238.75" X2="125" Y2="238.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- -5到-10之间的小刻度 -->
<Line X1="40" Y1="251.25" X2="125" Y2="251.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="257.5" X2="125" Y2="257.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="263.75" X2="125" Y2="263.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- -10到-15之间的小刻度 -->
<Line X1="40" Y1="276.25" X2="125" Y2="276.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="282.5" X2="125" Y2="282.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="288.75" X2="125" Y2="288.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- -15到-20之间的小刻度 -->
<Line X1="40" Y1="301.25" X2="125" Y2="301.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="307.5" X2="125" Y2="307.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="313.75" X2="125" Y2="313.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- -20到-25之间的小刻度 -->
<Line X1="40" Y1="326.25" X2="125" Y2="326.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="332.5" X2="125" Y2="332.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="338.75" X2="125" Y2="338.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- -25到-30之间的小刻度 -->
<Line X1="40" Y1="351.25" X2="125" Y2="351.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="357.5" X2="125" Y2="357.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="363.75" X2="125" Y2="363.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- -30到-35之间的小刻度 -->
<Line X1="40" Y1="376.25" X2="125" Y2="376.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="382.5" X2="125" Y2="382.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="388.75" X2="125" Y2="388.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- -35到-40之间的小刻度 -->
<Line X1="40" Y1="401.25" X2="125" Y2="401.25" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="407.5" X2="125" Y2="407.5" Stroke="Black" StrokeThickness="0.5"/>
<Line X1="40" Y1="413.75" X2="125" Y2="413.75" Stroke="Black" StrokeThickness="0.5"/>
<!-- 动态刻度线 -->
<ItemsControl ItemsSource="{Binding ScaleLines}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<Canvas/>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemContainerStyle>
<Style TargetType="ContentPresenter">
<Setter Property="Canvas.Left" Value="0"/>
<Setter Property="Canvas.Top" Value="0"/>
</Style>
</ItemsControl.ItemContainerStyle>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Canvas>
<!-- 刻度线 -->
<Line X1="{Binding X1}" Y1="{Binding Y1}"
X2="{Binding X2}" Y2="{Binding Y2}"
Stroke="Black"
StrokeThickness="{Binding StrokeThickness}"/>
<!-- 刻度标签 -->
<TextBlock Canvas.Left="{Binding LabelX}"
Canvas.Top="{Binding LabelY}"
Text="{Binding Label}"
FontSize="10">
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="Visibility" Value="Collapsed"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsMainScale}" Value="True">
<Setter Property="Visibility" Value="Visible"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
</TextBlock>
</Canvas>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
<!-- 柱状图数据 - 紧贴左右边界 -->
<Rectangle Name="DataBar"

View File

@ -7,6 +7,22 @@ using System.Windows.Media;
namespace WpfApp.src.components
{
/// <summary>
/// 刻度线数据结构
/// </summary>
public class ScaleLine
{
public double Y1 { get; set; }
public double Y2 { get; set; }
public double X1 { get; set; }
public double X2 { get; set; }
public double StrokeThickness { get; set; }
public string Label { get; set; }
public double LabelX { get; set; }
public double LabelY { get; set; }
public bool IsMainScale { get; set; }
}
/// <summary>
/// SensorChart.xaml 的交互逻辑
/// </summary>
@ -22,6 +38,10 @@ namespace WpfApp.src.components
HeaderBackground = new SolidColorBrush(Color.FromRgb(230, 243, 255)); // #E6F3FF
Value = 0;
RedLineValues = new List<double>();
Level = SensorLevel.Medium; // 默认中等
// 生成初始刻度
GenerateScaleLines();
}
public event PropertyChangedEventHandler PropertyChanged;
@ -101,6 +121,23 @@ namespace WpfApp.src.components
control?.UpdateRedLinePositions();
}
// 传感器等级
public static readonly DependencyProperty LevelProperty =
DependencyProperty.Register("Level", typeof(SensorLevel), typeof(SensorChart),
new PropertyMetadata(SensorLevel.Medium, OnLevelChanged));
public SensorLevel Level
{
get { return (SensorLevel)GetValue(LevelProperty); }
set { SetValue(LevelProperty, value); }
}
private static void OnLevelChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
var control = d as SensorChart;
control?.OnLevelChanged();
}
#endregion
#region
@ -138,24 +175,140 @@ namespace WpfApp.src.components
}
}
private List<ScaleLine> _scaleLines = new List<ScaleLine>();
public List<ScaleLine> ScaleLines
{
get { return _scaleLines; }
private set
{
_scaleLines = value;
OnPropertyChanged(nameof(ScaleLines));
}
}
// 当前等级的最小值
public double MinValue
{
get
{
switch (Level)
{
case SensorLevel.Low: return -20;
case SensorLevel.Medium: return -40;
case SensorLevel.High: return -60;
default: return -40;
}
}
}
// 当前等级的最大值
public double MaxValue
{
get
{
switch (Level)
{
case SensorLevel.Low: return 20;
case SensorLevel.Medium: return 40;
case SensorLevel.High: return 60;
default: return 40;
}
}
}
#endregion
#region
/// <summary>
/// 等级改变时的处理
/// </summary>
private void OnLevelChanged()
{
OnPropertyChanged(nameof(Level));
OnPropertyChanged(nameof(MinValue));
OnPropertyChanged(nameof(MaxValue));
GenerateScaleLines();
UpdateBarPosition();
UpdateRedLinePositions();
}
/// <summary>
/// 生成刻度线
/// </summary>
private void GenerateScaleLines()
{
var lines = new List<ScaleLine>();
double minVal = MinValue;
double maxVal = MaxValue;
// 根据等级确定刻度间隔
double mainInterval = (maxVal - minVal) / 8; // 8个主刻度间隔
double subInterval = mainInterval / 4; // 每个主刻度间隔4个小刻度
// 生成主刻度线
for (int i = 0; i <= 8; i++)
{
double value = maxVal - i * mainInterval;
double y = ValueToY(value);
lines.Add(new ScaleLine
{
X1 = 40,
Y1 = y,
X2 = 130,
Y2 = y,
StrokeThickness = 1,
Label = value.ToString("0"),
LabelX = 135,
LabelY = y - 5,
IsMainScale = true
});
// 生成小刻度线(除了最后一个主刻度)
if (i < 8)
{
for (int j = 1; j < 4; j++)
{
double subValue = value - j * subInterval;
double subY = ValueToY(subValue);
lines.Add(new ScaleLine
{
X1 = 40,
Y1 = subY,
X2 = 125,
Y2 = subY,
StrokeThickness = 0.5,
Label = "",
LabelX = 0,
LabelY = 0,
IsMainScale = false
});
}
}
}
ScaleLines = lines;
}
/// <summary>
/// 将数值转换为Y坐标位置
/// 刻度范围40 到 -40对应Y坐标20 到 420
/// 根据当前等级动态计算刻度范围
/// </summary>
/// <param name="value">数值</param>
/// <returns>Y坐标</returns>
private double ValueToY(double value)
{
// 限制数值范围
value = Math.Max(-40, Math.Min(40, value));
double minVal = MinValue;
double maxVal = MaxValue;
// 线性映射40对应Y=20-40对应Y=420
// Y = 220 - value * 5
return 220 - value * 5;
// 限制数值范围
value = Math.Max(minVal, Math.Min(maxVal, value));
// 线性映射maxVal对应Y=20minVal对应Y=420
double range = maxVal - minVal;
return 220 - ((value - minVal) / range - 0.5) * 400;
}
/// <summary>
@ -204,7 +357,8 @@ namespace WpfApp.src.components
/// <param name="value">数值</param>
/// <param name="redLines">红线数值列表</param>
/// <param name="headerColor">标题背景色</param>
public void SetSensorData(string name, double value, List<double> redLines = null, Color? headerColor = null)
/// <param name="level">传感器等级</param>
public void SetSensorData(string name, double value, List<double> redLines = null, Color? headerColor = null, SensorLevel? level = null)
{
SensorName = name;
Value = value;
@ -218,6 +372,11 @@ namespace WpfApp.src.components
{
HeaderBackground = new SolidColorBrush(headerColor.Value);
}
if (level.HasValue)
{
Level = level.Value;
}
}
/// <summary>

View File

@ -0,0 +1,12 @@
namespace WpfApp.src.components
{
/// <summary>
/// 传感器等级枚举
/// </summary>
public enum SensorLevel
{
Low, // 低等:-20到20
Medium, // 中等:-40到40
High // 高等:-60到60
}
}