695 lines
26 KiB
C#
695 lines
26 KiB
C#
using OfficeOpenXml;
|
||
using Sunny.UI;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Windows;
|
||
using System.Windows.Controls;
|
||
using System.Windows.Media;
|
||
using System.Windows.Shapes;
|
||
using System.Windows.Threading;
|
||
|
||
namespace PLCDataMonitor
|
||
{
|
||
public partial class CurvePage : UserControl
|
||
{
|
||
private Size _lastCanvasSize = Size.Empty;
|
||
private System.Collections.Concurrent.ConcurrentQueue<Tuple<DateTime, double, double>> _sharedDataQueue;
|
||
private DispatcherTimer _updateTimer;
|
||
private int _currentTimeRange = 60;
|
||
public Action<double, double> UpdateFrictionData;
|
||
|
||
// 绘图参数
|
||
private double _canvasWidth = 0;
|
||
private double _canvasHeight = 0;
|
||
private double _axisMargin = 80; // 坐标轴边距
|
||
private double _axisStartX = 0;
|
||
private double _axisEndX = 0;
|
||
private double _axisStartY = 0;
|
||
private double _axisEndY = 0;
|
||
private double _axisWidth = 0;
|
||
private double _axisHeight = 0;
|
||
|
||
// 缓存上一次的绘图范围,避免重复绘制
|
||
private double _lastTotalSeconds = 0;
|
||
private double _lastMinValue = 0;
|
||
private double _lastMaxValue = 0;
|
||
|
||
public CurvePage(System.Collections.Concurrent.ConcurrentQueue<Tuple<DateTime, double, double>> sharedDataQueue)
|
||
{
|
||
InitializeComponent();
|
||
_sharedDataQueue = sharedDataQueue ?? new System.Collections.Concurrent.ConcurrentQueue<Tuple<DateTime, double, double>>();
|
||
UpdateFrictionData = AddFrictionData;
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
private void UserControl_Loaded(object sender, RoutedEventArgs e)
|
||
{
|
||
// 初始计算画布大小
|
||
UpdateCanvasSize();
|
||
|
||
_updateTimer = new DispatcherTimer
|
||
{
|
||
Interval = TimeSpan.FromMilliseconds(100)
|
||
};
|
||
_updateTimer.Tick += UpdateTimer_Tick;
|
||
_updateTimer.Start();
|
||
|
||
// 监听尺寸变化
|
||
this.SizeChanged += UserControl_SizeChanged;
|
||
}
|
||
|
||
private void UserControl_SizeChanged(object sender, SizeChangedEventArgs e)
|
||
{
|
||
UpdateCanvasSize();
|
||
}
|
||
|
||
private void UpdateCanvasSize()
|
||
{
|
||
// 等待布局完成
|
||
Dispatcher.BeginInvoke(new Action(() =>
|
||
{
|
||
double newWidth = CurveCanvas.ActualWidth;
|
||
double newHeight = CurveCanvas.ActualHeight;
|
||
|
||
// 只有当画布大小实际变化时才更新
|
||
if (Math.Abs(newWidth - _lastCanvasSize.Width) > 1 ||
|
||
Math.Abs(newHeight - _lastCanvasSize.Height) > 1)
|
||
{
|
||
_canvasWidth = newWidth;
|
||
_canvasHeight = newHeight;
|
||
|
||
if (_canvasWidth > 0 && _canvasHeight > 0)
|
||
{
|
||
// 关键修改:根据画布大小动态计算坐标轴边距
|
||
// 边距应该与画布大小成比例,而不是固定值
|
||
_axisMargin = Math.Min(_canvasWidth, _canvasHeight) * 0.08; // 8%的边距
|
||
_axisMargin = Math.Max(_axisMargin, 40); // 最小40像素
|
||
_axisMargin = Math.Min(_axisMargin, 100); // 最大100像素
|
||
|
||
_axisStartX = _axisMargin;
|
||
_axisEndX = _canvasWidth - _axisMargin;
|
||
_axisStartY = _axisMargin;
|
||
_axisEndY = _canvasHeight - _axisMargin;
|
||
_axisWidth = _axisEndX - _axisStartX;
|
||
_axisHeight = _axisEndY - _axisStartY;
|
||
|
||
_lastCanvasSize = new Size(_canvasWidth, _canvasHeight);
|
||
|
||
// 标记需要重绘坐标轴
|
||
_lastTotalSeconds = 0;
|
||
_lastMinValue = 0;
|
||
_lastMaxValue = 0;
|
||
}
|
||
}
|
||
}), DispatcherPriority.Render);
|
||
}
|
||
|
||
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
|
||
{
|
||
if (_updateTimer != null)
|
||
{
|
||
_updateTimer.Stop();
|
||
_updateTimer.Tick -= UpdateTimer_Tick;
|
||
_updateTimer = null;
|
||
}
|
||
}
|
||
|
||
public void AddFrictionData(double f1, double f2)
|
||
{
|
||
// 数据已通过共享队列添加
|
||
}
|
||
|
||
private void UpdateTimer_Tick(object sender, EventArgs e)
|
||
{
|
||
// 关键修改:每次都要检查画布大小
|
||
if (_canvasWidth <= 0 || _canvasHeight <= 0 ||
|
||
Math.Abs(CurveCanvas.ActualWidth - _canvasWidth) > 1 ||
|
||
Math.Abs(CurveCanvas.ActualHeight - _canvasHeight) > 1)
|
||
{
|
||
UpdateCanvasSize();
|
||
}
|
||
if (_canvasWidth <= 0 || _canvasHeight <= 0) return;
|
||
|
||
if (_sharedDataQueue == null || _sharedDataQueue.Count == 0)
|
||
{
|
||
Friction1Polyline?.Points.Clear();
|
||
Friction2Polyline?.Points.Clear();
|
||
ClearAllDynamicElements();
|
||
return;
|
||
}
|
||
|
||
// 获取数据
|
||
var allData = _sharedDataQueue.ToList();
|
||
if (allData.Count < 2) return;
|
||
|
||
// 计算时间范围
|
||
DateTime firstTime = allData.First().Item1;
|
||
DateTime lastTime = allData.Last().Item1;
|
||
double totalSeconds = Math.Max(1, (lastTime - firstTime).TotalSeconds);
|
||
|
||
// 清理超出时间范围的数据
|
||
if (totalSeconds > _currentTimeRange)
|
||
{
|
||
var maxDataCount = (int)(_currentTimeRange * 10);
|
||
// 使用 TryDequeue 安全地移除多余项
|
||
while (_sharedDataQueue.Count > maxDataCount)
|
||
{
|
||
_sharedDataQueue.TryDequeue(out _);
|
||
}
|
||
allData = _sharedDataQueue.ToList();
|
||
|
||
if (allData.Count > 0)
|
||
{
|
||
firstTime = allData.First().Item1;
|
||
lastTime = allData.Last().Item1;
|
||
totalSeconds = Math.Max(1, (lastTime - firstTime).TotalSeconds);
|
||
}
|
||
}
|
||
|
||
// 计算Y轴范围
|
||
double minValue = double.MaxValue;
|
||
double maxValue = double.MinValue;
|
||
|
||
foreach (var data in allData)
|
||
{
|
||
minValue = Math.Min(minValue, Math.Min(data.Item2, data.Item3));
|
||
maxValue = Math.Max(maxValue, Math.Max(data.Item2, data.Item3));
|
||
}
|
||
|
||
// 确保包含0点
|
||
minValue = Math.Min(minValue, 0);
|
||
maxValue = Math.Max(maxValue, 0);
|
||
|
||
// 设置合理的范围
|
||
if (maxValue - minValue < 10)
|
||
{
|
||
minValue = -10;
|
||
maxValue = 10;
|
||
}
|
||
else
|
||
{
|
||
double margin = (maxValue - minValue) * 0.1;
|
||
minValue -= margin;
|
||
maxValue += margin;
|
||
}
|
||
|
||
double yRange = maxValue - minValue;
|
||
|
||
// 检查是否需要重绘坐标轴(只有当范围变化较大时)
|
||
bool shouldRedrawAxis = ShouldRedrawAxis(totalSeconds, minValue, maxValue);
|
||
|
||
if (shouldRedrawAxis)
|
||
{
|
||
ClearAllDynamicElements();
|
||
DrawAxes();
|
||
DrawYAxisGridAndLabels(minValue, maxValue);
|
||
DrawXAxisTimeTicks(totalSeconds);
|
||
_lastTotalSeconds = totalSeconds;
|
||
_lastMinValue = minValue;
|
||
_lastMaxValue = maxValue;
|
||
}
|
||
|
||
// 总是绘制曲线(数据点会变化)
|
||
DrawCurves(allData, firstTime, totalSeconds, minValue, maxValue);
|
||
}
|
||
|
||
|
||
private void DrawYAxisGridAndLabels(double minValue, double maxValue)
|
||
{
|
||
if (CurveCanvas == null) return;
|
||
|
||
double yRange = maxValue - minValue;
|
||
if (yRange <= 0) return;
|
||
|
||
// 计算合适的刻度间隔
|
||
double tickInterval = CalculateNiceInterval(yRange);
|
||
double startValue = Math.Ceiling(minValue / tickInterval) * tickInterval;
|
||
|
||
// 使用HashSet记录已绘制的刻度值,避免重复
|
||
HashSet<double> drawnValues = new HashSet<double>();
|
||
|
||
// 计算标签字体大小,根据画布大小动态调整
|
||
double fontSize = Math.Max(10, Math.Min(16, _canvasHeight * 0.02));
|
||
|
||
// 绘制网格线和标签
|
||
for (double value = startValue; value <= maxValue; value += tickInterval)
|
||
{
|
||
// 避免浮点数精度问题导致重复
|
||
double roundedValue = Math.Round(value, 3);
|
||
if (drawnValues.Contains(roundedValue))
|
||
continue;
|
||
drawnValues.Add(roundedValue);
|
||
|
||
// 计算Y坐标
|
||
double y = _axisEndY - (_axisHeight * (value - minValue) / yRange);
|
||
y = Math.Clamp(y, _axisStartY, _axisEndY);
|
||
|
||
// 绘制水平网格线
|
||
var gridLine = new Line
|
||
{
|
||
X1 = _axisStartX,
|
||
Y1 = y,
|
||
X2 = _axisEndX,
|
||
Y2 = y,
|
||
Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#EEEEEE")),
|
||
StrokeThickness = 1,
|
||
Tag = "DynamicElement"
|
||
};
|
||
CurveCanvas.Children.Add(gridLine);
|
||
|
||
// 绘制Y轴刻度标签 - 关键修改:动态计算标签位置
|
||
var label = new TextBlock
|
||
{
|
||
Text = value.ToString("F0"),
|
||
FontSize = fontSize,
|
||
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#7F8C8D")),
|
||
Tag = "DynamicElement"
|
||
};
|
||
|
||
// 测量文本宽度以正确放置
|
||
label.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||
double labelWidth = label.DesiredSize.Width;
|
||
|
||
Canvas.SetLeft(label, _axisStartX - labelWidth - 10); // 根据文本宽度调整位置
|
||
Canvas.SetTop(label, y - label.DesiredSize.Height / 2);
|
||
CurveCanvas.Children.Add(label);
|
||
}
|
||
|
||
// 绘制0点特殊标记
|
||
if (minValue <= 0 && maxValue >= 0)
|
||
{
|
||
double zeroY = _axisEndY - (_axisHeight * (0 - minValue) / yRange);
|
||
zeroY = Math.Clamp(zeroY, _axisStartY, _axisEndY);
|
||
|
||
// 0点特殊标记线
|
||
var zeroLine = new Line
|
||
{
|
||
X1 = _axisStartX - 8,
|
||
Y1 = zeroY,
|
||
X2 = _axisStartX,
|
||
Y2 = zeroY,
|
||
Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#7F8C8D")),
|
||
StrokeThickness = 2,
|
||
Tag = "DynamicElement"
|
||
};
|
||
CurveCanvas.Children.Add(zeroLine);
|
||
|
||
var zeroText = new TextBlock
|
||
{
|
||
Text = "0",
|
||
FontSize = fontSize,
|
||
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#7F8C8D")),
|
||
FontWeight = FontWeights.Bold,
|
||
Tag = "DynamicElement"
|
||
};
|
||
|
||
zeroText.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||
double zeroTextWidth = zeroText.DesiredSize.Width;
|
||
|
||
Canvas.SetLeft(zeroText, _axisStartX - zeroTextWidth - 15);
|
||
Canvas.SetTop(zeroText, zeroY - zeroText.DesiredSize.Height / 2);
|
||
CurveCanvas.Children.Add(zeroText);
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
private bool ShouldRedrawAxis(double totalSeconds, double minValue, double maxValue)
|
||
{
|
||
// 如果这是第一次绘制
|
||
if (_lastTotalSeconds == 0 && _lastMinValue == 0 && _lastMaxValue == 0)
|
||
return true;
|
||
|
||
// 检查时间范围变化是否超过10%
|
||
double timeDiff = Math.Abs(totalSeconds - _lastTotalSeconds);
|
||
if (timeDiff > _lastTotalSeconds * 0.1)
|
||
return true;
|
||
|
||
// 检查Y轴范围变化是否超过10%
|
||
double yRangeDiff = Math.Abs((maxValue - minValue) - (_lastMaxValue - _lastMinValue));
|
||
if (yRangeDiff > (_lastMaxValue - _lastMinValue) * 0.1)
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
// 修改 DrawAxes 方法,动态调整标题位置
|
||
private void DrawAxes()
|
||
{
|
||
if (CurveCanvas == null) return;
|
||
|
||
// 计算字体大小,根据画布大小动态调整
|
||
double axisFontSize = Math.Max(12, Math.Min(18, _canvasHeight * 0.025));
|
||
|
||
// 绘制X轴
|
||
var xAxis = new Line
|
||
{
|
||
X1 = _axisStartX,
|
||
Y1 = _axisEndY,
|
||
X2 = _axisEndX,
|
||
Y2 = _axisEndY,
|
||
Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#2C3E50")),
|
||
StrokeThickness = Math.Max(1, _canvasHeight * 0.003), // 根据画布大小调整粗细
|
||
Tag = "DynamicElement"
|
||
};
|
||
CurveCanvas.Children.Add(xAxis);
|
||
|
||
// 绘制Y轴
|
||
var yAxis = new Line
|
||
{
|
||
X1 = _axisStartX,
|
||
Y1 = _axisStartY,
|
||
X2 = _axisStartX,
|
||
Y2 = _axisEndY,
|
||
Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#2C3E50")),
|
||
StrokeThickness = Math.Max(1, _canvasHeight * 0.003), // 根据画布大小调整粗细
|
||
Tag = "DynamicElement"
|
||
};
|
||
CurveCanvas.Children.Add(yAxis);
|
||
|
||
// X轴标题
|
||
var xAxisTitle = new TextBlock
|
||
{
|
||
Text = "时间(秒)",
|
||
FontSize = axisFontSize,
|
||
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#7F8C8D")),
|
||
FontWeight = FontWeights.Bold,
|
||
Tag = "DynamicElement"
|
||
};
|
||
|
||
xAxisTitle.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||
double xTitleWidth = xAxisTitle.DesiredSize.Width;
|
||
|
||
Canvas.SetLeft(xAxisTitle, (_axisStartX + _axisEndX) / 2 - xTitleWidth / 2);
|
||
Canvas.SetTop(xAxisTitle, _axisEndY + 25);
|
||
CurveCanvas.Children.Add(xAxisTitle);
|
||
|
||
// Y轴标题
|
||
var yAxisTitle = new TextBlock
|
||
{
|
||
Text = "摩擦力 (N)",
|
||
FontSize = axisFontSize,
|
||
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#7F8C8D")),
|
||
FontWeight = FontWeights.Bold,
|
||
Tag = "DynamicElement"
|
||
};
|
||
yAxisTitle.RenderTransform = new RotateTransform(-90);
|
||
|
||
yAxisTitle.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||
double yTitleHeight = yAxisTitle.DesiredSize.Height;
|
||
|
||
Canvas.SetLeft(yAxisTitle, 15);
|
||
Canvas.SetTop(yAxisTitle, (_axisStartY + _axisEndY) / 2 - yTitleHeight / 2);
|
||
CurveCanvas.Children.Add(yAxisTitle);
|
||
}
|
||
|
||
|
||
|
||
private void DrawCurves(List<Tuple<DateTime, double, double>> allData, DateTime firstTime,
|
||
double totalSeconds, double minValue, double maxValue)
|
||
{
|
||
if (allData.Count == 0) return;
|
||
|
||
double yRange = maxValue - minValue;
|
||
if (yRange <= 0) return;
|
||
|
||
// 创建新的点集合
|
||
PointCollection points1 = new PointCollection();
|
||
PointCollection points2 = new PointCollection();
|
||
|
||
foreach (var data in allData)
|
||
{
|
||
// 计算X坐标(时间,单位:秒) - 使用实际时间差
|
||
double timeDiff = (data.Item1 - firstTime).TotalSeconds;
|
||
|
||
// 确保timeDiff不超过totalSeconds
|
||
timeDiff = Math.Min(timeDiff, totalSeconds);
|
||
|
||
double x = _axisStartX + (_axisWidth * timeDiff / totalSeconds);
|
||
x = Math.Clamp(x, _axisStartX, _axisEndX);
|
||
|
||
// 计算Y坐标(摩擦力)
|
||
double y1 = _axisEndY - (_axisHeight * (data.Item2 - minValue) / yRange);
|
||
double y2 = _axisEndY - (_axisHeight * (data.Item3 - minValue) / yRange);
|
||
|
||
y1 = Math.Clamp(y1, _axisStartY, _axisEndY);
|
||
y2 = Math.Clamp(y2, _axisStartY, _axisEndY);
|
||
|
||
points1.Add(new Point(x, y1));
|
||
points2.Add(new Point(x, y2));
|
||
}
|
||
|
||
// 更新曲线
|
||
if (Friction1Polyline != null)
|
||
{
|
||
Friction1Polyline.Points = points1;
|
||
}
|
||
|
||
if (Friction2Polyline != null)
|
||
{
|
||
Friction2Polyline.Points = points2;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
|
||
private double CalculateNiceInterval(double range)
|
||
{
|
||
if (range <= 0) return 10;
|
||
|
||
double[] niceIntervals = { 1, 2, 5, 10, 20, 50, 100, 200, 500, 1000 };
|
||
double targetInterval = range / 8; // 大约8个刻度
|
||
|
||
double bestInterval = 10;
|
||
double minDiff = double.MaxValue;
|
||
|
||
foreach (double interval in niceIntervals)
|
||
{
|
||
double numTicks = range / interval;
|
||
if (numTicks >= 4 && numTicks <= 12) // 4-12个刻度比较合适
|
||
{
|
||
double diff = Math.Abs(interval - targetInterval);
|
||
if (diff < minDiff)
|
||
{
|
||
minDiff = diff;
|
||
bestInterval = interval;
|
||
}
|
||
}
|
||
}
|
||
|
||
return bestInterval;
|
||
}
|
||
|
||
private double CalculateNiceTimeInterval(double totalSeconds)
|
||
{
|
||
if (totalSeconds <= 0) return 10;
|
||
|
||
// 可能的刻度间隔值(按从小到大排序)
|
||
double[] possibleIntervals = { 1, 2, 5, 10, 15, 20, 30, 60, 120, 300, 600, 900, 1200, 1800 };
|
||
|
||
// 我们希望有大约5-10个刻度
|
||
double targetTicks = 8;
|
||
double targetInterval = totalSeconds / targetTicks;
|
||
|
||
// 找到第一个大于等于目标间隔的合适间隔
|
||
foreach (double interval in possibleIntervals)
|
||
{
|
||
// 检查这个间隔是否会产生合理的刻度数(4-12个)
|
||
double numTicks = totalSeconds / interval;
|
||
if (numTicks >= 4 && numTicks <= 12 && interval >= targetInterval)
|
||
{
|
||
return interval;
|
||
}
|
||
}
|
||
|
||
// 如果没找到合适的,返回最后一个间隔
|
||
return possibleIntervals[possibleIntervals.Length - 1];
|
||
}
|
||
|
||
|
||
private void DrawXAxisTimeTicks(double totalSeconds)
|
||
{
|
||
if (CurveCanvas == null || totalSeconds <= 0) return;
|
||
|
||
// 计算合适的刻度间隔
|
||
double interval = CalculateNiceTimeInterval(totalSeconds);
|
||
|
||
// 关键修改:使用浮点数循环,但要精确控制
|
||
double currentTime = 0;
|
||
int tickCount = 0;
|
||
|
||
// 先计算实际需要绘制的刻度数
|
||
int maxTicks = 15; // 最多15个刻度,避免重叠
|
||
|
||
while (currentTime <= totalSeconds && tickCount < maxTicks)
|
||
{
|
||
double x = _axisStartX + (_axisWidth * currentTime / totalSeconds);
|
||
x = Math.Clamp(x, _axisStartX, _axisEndX);
|
||
|
||
// 绘制刻度线
|
||
var tickLine = new Line
|
||
{
|
||
X1 = x,
|
||
Y1 = _axisEndY,
|
||
X2 = x,
|
||
Y2 = _axisEndY + Math.Max(6, _canvasHeight * 0.02),
|
||
Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#BDC3C7")),
|
||
StrokeThickness = 1.5,
|
||
Tag = "DynamicElement"
|
||
};
|
||
CurveCanvas.Children.Add(tickLine);
|
||
|
||
// 绘制时间标签 - 关键修改:显示实际时间值,与报表一致
|
||
string timeText = FormatTimeLabel(currentTime, totalSeconds);
|
||
var tickText = new TextBlock
|
||
{
|
||
Text = timeText,
|
||
FontSize = 14,
|
||
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#7F8C8D")),
|
||
Tag = "DynamicElement"
|
||
};
|
||
|
||
// 测量文本宽度以居中显示
|
||
tickText.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||
double textWidth = tickText.DesiredSize.Width;
|
||
|
||
Canvas.SetLeft(tickText, x - textWidth / 2);
|
||
Canvas.SetTop(tickText, _axisEndY + Math.Max(10, _canvasHeight * 0.025));
|
||
CurveCanvas.Children.Add(tickText);
|
||
|
||
// 增加时间和刻度计数
|
||
currentTime += interval;
|
||
tickCount++;
|
||
}
|
||
|
||
// 关键修改:确保最后一个刻度显示总时间(与报表一致)
|
||
if (totalSeconds > 0)
|
||
{
|
||
// 检查是否已经绘制了最后一个刻度
|
||
bool hasLastTick = false;
|
||
if (currentTime - interval < totalSeconds && Math.Abs(currentTime - totalSeconds) > 0.001)
|
||
{
|
||
hasLastTick = false;
|
||
}
|
||
|
||
if (!hasLastTick)
|
||
{
|
||
double lastX = _axisEndX;
|
||
// 关键:使用实际的总时间,与报表一致
|
||
string lastText = FormatTimeLabel(totalSeconds, totalSeconds);
|
||
|
||
var lastTickLine = new Line
|
||
{
|
||
X1 = lastX,
|
||
Y1 = _axisEndY,
|
||
X2 = lastX,
|
||
Y2 = _axisEndY + Math.Max(6, _canvasHeight * 0.02),
|
||
Stroke = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#BDC3C7")),
|
||
StrokeThickness = 1.5,
|
||
Tag = "DynamicElement"
|
||
};
|
||
CurveCanvas.Children.Add(lastTickLine);
|
||
|
||
var lastTickText = new TextBlock
|
||
{
|
||
Text = lastText,
|
||
FontSize = 14,
|
||
Foreground = new SolidColorBrush((Color)ColorConverter.ConvertFromString("#7F8C8D")),
|
||
Tag = "DynamicElement"
|
||
};
|
||
|
||
lastTickText.Measure(new Size(double.PositiveInfinity, double.PositiveInfinity));
|
||
double lastTextWidth = lastTickText.DesiredSize.Width;
|
||
|
||
Canvas.SetLeft(lastTickText, lastX - lastTextWidth / 2);
|
||
Canvas.SetTop(lastTickText, _axisEndY + Math.Max(10, _canvasHeight * 0.025));
|
||
CurveCanvas.Children.Add(lastTickText);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 修改 FormatTimeLabel 方法,显示实际数值
|
||
private string FormatTimeLabel(double seconds, double totalSeconds)
|
||
{
|
||
// 关键修改:显示实际的小数值,与报表一致
|
||
if (totalSeconds < 60)
|
||
{
|
||
// 如果小于1秒,显示2位小数;否则显示1位小数
|
||
if (seconds < 1)
|
||
return $"{seconds:F2}秒";
|
||
return $"{seconds:F1}秒";
|
||
}
|
||
else if (totalSeconds < 3600)
|
||
{
|
||
double minutes = seconds / 60;
|
||
return $"{minutes:F1}分";
|
||
}
|
||
else
|
||
{
|
||
double hours = seconds / 3600;
|
||
return $"{hours:F2}小时";
|
||
}
|
||
}
|
||
private void ClearAllDynamicElements()
|
||
{
|
||
if (CurveCanvas == null || CurveCanvas.Children == null) return;
|
||
|
||
var elementsToRemove = new List<UIElement>();
|
||
foreach (object element in CurveCanvas.Children)
|
||
{
|
||
if (element is FrameworkElement fe && fe.Tag != null && fe.Tag.ToString() == "DynamicElement")
|
||
{
|
||
elementsToRemove.Add(fe);
|
||
}
|
||
}
|
||
|
||
foreach (var element in elementsToRemove)
|
||
{
|
||
CurveCanvas.Children.Remove(element);
|
||
}
|
||
}
|
||
|
||
private void TimeRangeComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e)
|
||
{
|
||
//if (TimeRangeComboBox.SelectedItem is ComboBoxItem item && int.TryParse(item.Tag.ToString(), out int newRange))
|
||
//{
|
||
// _currentTimeRange = newRange;
|
||
//}
|
||
}
|
||
|
||
private void ClearCurveButton_Click(object sender, RoutedEventArgs e)
|
||
{
|
||
ClearCurve();
|
||
}
|
||
|
||
public void ClearCurve()
|
||
{
|
||
if (_sharedDataQueue != null)
|
||
{
|
||
// ConcurrentQueue doesn't support Clear(), dequeue all items safely
|
||
while (_sharedDataQueue.TryDequeue(out _)) { }
|
||
}
|
||
|
||
if (Friction1Polyline != null)
|
||
{
|
||
Friction1Polyline.Points.Clear();
|
||
}
|
||
|
||
if (Friction2Polyline != null)
|
||
{
|
||
Friction2Polyline.Points.Clear();
|
||
}
|
||
|
||
ClearAllDynamicElements();
|
||
_lastTotalSeconds = 0;
|
||
_lastMinValue = 0;
|
||
_lastMaxValue = 0;
|
||
}
|
||
}
|
||
} |