C#:如果尝试对一个null值使用Math.Floor方法

在C#中,如果尝试对一个null值使用Math.Floor方法,会引发ArgumentNullException异常。这是因为Math.Floor方法期望其参数是一个数值类型(如int, double, float等),而不能接受null值。

为了安全地处理这种情况,你可以在调用Math.Floor之前检查变量是否为null。如果变量可能为null,可以将其转换为可空类型(如int?, double?, float?),这样它们就可以存储null值而不会引发异常。以下是一些处理这种情况的方法:
方法1:使用可空类型
double? value = null; // 或者从某个方法获取的值

if (value.HasValue)
{
    double floorValue = Math.Floor(value.Value);
    Console.WriteLine(floorValue);
}
else
{
    Console.WriteLine("值是null,无法计算floor");
}

方法2:使用条件运算符

如果你确定在大多数情况下值不会是null,但仍然想要优雅地处理可能的null情况,可以使用条件运算符:
double? value = null; // 或者从某个方法获取的值
double floorValue = value.HasValue ? Math.Floor(value.Value) : 0; // 或者其他默认值
Console.WriteLine(floorValue);

方法3:使用扩展方法避免空检查

你也可以创建一个扩展方法来封装对null的处理:
public static class MathExtensions
{
    public static double FloorOrDefault(this double? value, double defaultValue = 0)
    {
        return value.HasValue ? Math.Floor(value.Value) : defaultValue;
    }
}

// 使用方法
double? value = null; // 或者从某个方法获取的值
double floorValue = value.FloorOrDefault(); // 如果value为null,则返回0
Console.WriteLine(floorValue);

这些方法中的任何一种都可以帮助你安全地处理可能为null的数值,避免在调用Math.Floor时出现。

using Newtonsoft.Json.Linq; using Sunny.UI; using System; using System.Collections.Generic; using System.Diagnostics; using System.Drawing; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Threading.Tasks; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; // using static System.Windows.Forms.VisualStyles.VisualStyleElement; namespace HHVideoPlayer.Model.ChartModel { public class ChartLib { public static void loadXY(LineChart chartdb, string path) { List<string> Records = File.ReadLines(path).ToList(); foreach (var item in Records) { if (item.Contains(",")) { DateTime x = DateTime.ParseExact(item.Split(",").First(), "HH:mm:ss.ffff", CultureInfo.InvariantCulture, DateTimeStyles.None); chartdb.xDB.Add(x); chartdb.yDB.Add(Convert.ToDouble(item.Split(",").Last())); } } chartdb.startTime = chartdb.xDB[0]; chartdb.endTime = chartdb.xDB[chartdb.xDB.Count - 1]; //Debug.WriteLine($"{chartdb.xDB[0]} --{chartdb.xDB[chartdb.xDB.Count-1]}"); return; } } public class ChartConfig { public string ChartName { get; set; } = "DefaultChart"; public string ChartTitle { get; set; } = "数据图表"; public Color SeriesColor { get; set; } = Color.Blue; public bool ScrollBarEnable { get; set; } = true; public int ScrollBarSize { get; set; } = 14; public double ViewSize { get; set; } = 60; // 默认显示60秒 } public class LineChart { public Chart Chart_ { get; } public String ShohType = "OverView"; // Follow or OverView public int _index = 0; public List<DateTime> xDB = new List<DateTime>(); public List<Double> yDB = new List<Double>(); public DateTime startTime; public DateTime endTime; public System.Windows.Forms.Timer PlayTimer { get; } private bool _isPlaying = false; // 添加鼠标交互状态变量 private Point _dragStartPoint; private bool _isDragging; private double _startPosition; private double _startSize; // 1. 添加图表状态追踪变量 private bool _isViewValid = false; private bool _isDataLoaded = false; // 添加游标线和文本注解 private VerticalLineAnnotation _cursorLine; private TextAnnotation _cursorText; private bool _isCursorMoving = false; private Point _cursorDragStart; private double _cursorOriginalX; public int PointIndex { get { return _index; } } public string PointXValue { get{ if (_index <= xDB.Count - 1)return xDB[_index].ToString("HH:mm:ss.ff"); else return "";} } public string PointYValue { get { if (_index <= yDB.Count - 1) return Math.Round(yDB[_index], 2).ToString(); else return ""; } } public LineChart(Chart chart, string showtype = "Follow") { Chart_ = chart; _index = 0; ShohType = showtype; InitChart(); PlayTimer = new System.Windows.Forms.Timer { Interval = 1 }; // 20 FPS (50ms) //Initialize(); InitializeCursorLine(); BindFunc(); } private void BindFunc() { PlayTimer.Tick += (s, e) => PlayNextPoint(); // 绑定事件 // 启用鼠标事件 Chart_.MouseDown += Chart_MouseDown; Chart_.MouseMove += Chart_MouseMove; Chart_.MouseUp += Chart_MouseUp; Chart_.MouseWheel += Chart_MouseWheel; Chart_.MouseLeave += Chart_MouseLeave; //Chart_.AxisViewChanged += OnAxisViewChanged; //Chart_.AxisScrollBarClicked += OnScrollBarClicked; //_cursorLine.PositionChanging += CursorLine_PositionChanging; //_cursorLine.PositionChanged += CursorLine_PositionChanged; } public void InitChart() { CreateChart(); // 添加双缓冲支持(防止闪烁和空白) typeof(Chart).InvokeMember("DoubleBuffered", System.Reflection.BindingFlags.SetProperty | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance, null, Chart_, new object[] { true }); // 添加安全刷新标记 _isViewValid = false; _isDataLoaded = false; // X轴滚动到最新位置 Chart_.ChartAreas[0].AxisX.ScaleView.Scroll(ScrollType.Last); // timer.Interval = 10; AddSeries("line", Color.Red); InitializeCursorLine(); // 确保游标线被初始化 //Debug.WriteLine("InitChart"); } private void InitializeCursorLine() { // 创建垂直游标线 _cursorLine = new VerticalLineAnnotation { Name = "CursorLine", AxisX = Chart_.ChartAreas[0].AxisX, AxisY = Chart_.ChartAreas[0].AxisY, LineColor = Color.Red, LineWidth = 2, IsInfinitive = true, // 贯穿整个图表 AllowMoving = true, ClipToChartArea = Chart_.ChartAreas[0].Name, AnchorDataPoint = null }; // 创建坐标文本显示 _cursorText = new TextAnnotation { Name = "CursorText", AxisX = Chart_.ChartAreas[0].AxisX, AxisY = Chart_.ChartAreas[0].AxisY, ForeColor = Color.Red, Font = new Font("Arial", 10, FontStyle.Bold), AnchorAlignment = ContentAlignment.TopLeft, AnchorOffsetX = 5, AnchorOffsetY = 5, Text = "Time: N/A\nValue: N/A", Visible = false }; // 添加游标线和文本到图表 Chart_.Annotations.Add(_cursorLine); Chart_.Annotations.Add(_cursorText); // 设置初始位置(如果数据已加载) if (xDB != null && xDB.Count > 0) { _cursorLine.X = xDB[0].ToOADate(); UpdateCursorPosition(_cursorLine.X); } } private void AddSeries(string seriersName, Color serierscolor) { Chart_.Series.Clear(); Series series = new Series(seriersName); //图表类型 设置为样条图曲线 //series.ChartType = SeriesChartType.Line; //FastLine series.ChartType = SeriesChartType.FastLine; series.IsXValueIndexed = true; // 自动设置索引 series.XValueType = ChartValueType.Time; // X轴为时间类型 // 数据点标记设置 series.MarkerStyle = MarkerStyle.Circle; // 圆形标记 series.MarkerColor = Color.Black; // 标记颜色 //设置点的大小 series.MarkerSize = 5; //设置曲线的颜色 series.Color = serierscolor; //设置曲线宽度 series.BorderWidth = 2; // 线宽 series.CustomProperties = "PointWidth=2"; // 点宽 series.IsValueShownAsLabel = true; // 显示数标签 Chart_.Series.Add(series); // 添加到图表 //Debug.WriteLine("AddSerie"); } private void CreateChart() { Chart_.ChartAreas.Clear(); //chart = new Chart(); //this.panel1.Controls.Add(chart); // Chart_.Dock = DockStyle.Fill; // 填充到整个面板 Chart_.Visible = true; ChartArea chartArea = new ChartArea() { CursorX = { IsUserEnabled = true , IsUserSelectionEnabled = true ,SelectionColor = Color.SkyBlue, IntervalType = DateTimeIntervalType.Auto }, CursorY = { IsUserEnabled = true , IsUserSelectionEnabled = true ,SelectionColor = Color.SkyBlue, AutoScroll = true, }, AxisX = { ScaleView = {Zoomable=false ,MinSizeType = DateTimeIntervalType.Seconds}, LabelStyle = { Format = "HH:mm:ss.ff", Angle = -90 ,IsEndLabelVisible = true}, ScrollBar = { ButtonStyle = ScrollBarButtonStyles.All }, // 设置主网格线(透明度50%)Interval=0 自动 MajorGrid = { Enabled = true, LineColor = Color.FromArgb(50, Color.LightGray) ,Interval=0}, // 设置次网格线(更浅的透明度30%) MinorGrid = {Enabled = true, LineColor = Color.FromArgb(30, Color.LightGray), LineDashStyle=ChartDashStyle.Dash }, Title = @"Time",IsLabelAutoFit = true, LabelAutoFitMinFontSize = 5, Interval = 10, IntervalAutoMode = IntervalAutoMode.FixedCount, IntervalType = DateTimeIntervalType.NotSet, TextOrientation = TextOrientation.Auto, LineWidth = 2,LineColor = Color.Black, Enabled = AxisEnabled.True,Crossing = 0, //IsAuto }, AxisY ={ ScaleView = {Zoomable=false }, // 设置主网格线(透明度50%) MajorGrid = { Enabled = true , LineColor = Color.FromArgb(50, Color.LightGray), Interval=0}, // 设置次网格线(更浅的透明度30%) MinorGrid = {Enabled = true, LineColor = Color.FromArgb(30, Color.LightGray), LineDashStyle=ChartDashStyle.Dash }, Title = @"Value", LineWidth = 2, LineColor = Color.Black, Enabled = AxisEnabled.True }, Position = { Height = 85,Width = 95, X = 0,Y = 20 }, BackSecondaryColor = Color.White, //渐变背景色 BackGradientStyle = GradientStyle.TopBottom, //渐变方式 BackHatchStyle = ChartHatchStyle.None, //背景阴影 BorderDashStyle = ChartDashStyle.NotSet, //边框线样式 BorderWidth = 1, //边框宽度 BorderColor = Color.Black, }; Chart_.ChartAreas.Add(chartArea); Chart_.BackGradientStyle = GradientStyle.TopBottom; //图表的边框颜色、 Chart_.BorderlineColor = Color.FromArgb(26, 59, 105); //图表的边框线条样式 Chart_.BorderlineDashStyle = ChartDashStyle.Solid; //图表边框线条的宽度 Chart_.BorderlineWidth = 2; //图表边框的皮肤 Chart_.BorderSkin.SkinStyle = BorderSkinStyle.Emboss; Chart_.Paint += (s, e) => ValidateChartState(); //Debug.WriteLine("CreateChart"); } // 4. 添加图表状态验证方法 private void ValidateChartState() { if (!_isViewValid) // || !_isDataLoaded { //Debug.WriteLine("检测到无效视图状态,重新初始化图表..."); this.RefreshChart(); } } // 6. 增强RefreshChart方法 public void RefreshChart() { if (!_isDataLoaded) return; try { Chart_.SuspendLayout(); var series = Chart_.Series[0]; var points = series.Points; // 保存当前视图状态 var area = Chart_.ChartAreas[0]; double viewMin = area.AxisX.ScaleView.ViewMinimum; double viewMax = area.AxisX.ScaleView.ViewMaximum; bool isZoomed = !double.IsNaN(viewMin) && !double.IsNaN(viewMax); // 清除并重新添加数据 series.Points.Clear(); foreach (var (x, y) in xDB.Zip(yDB, (dt, val) => (dt, val))) { series.Points.AddXY(x, y); } // 恢复视图状态 if (isZoomed) { area.AxisX.ScaleView.Zoom(viewMin, viewMax); } else { area.AxisX.ScaleView.ZoomReset(); } _isViewValid = true; } finally { Chart_.ResumeLayout(); Chart_.Refresh(); } } public void PlayNextPoint(int pointsep = 10) { // 前置检查 if (Chart_.Series.Count <= 0 || xDB.Count != yDB.Count || _index >= xDB.Count) return; // 如果游标线未被拖动,则更新游标位置 if (!_isCursorMoving && ShohType == "Follow") { if (_index < xDB.Count) { double xValue = xDB[_index].ToOADate(); _cursorLine.X = xValue; UpdateCursorPosition(xValue); } } // 获取当前系列 Series series = Chart_.Series[0]; // 添加当前点(按间隔筛选) if (_index % pointsep == 0) { DateTime nowTime = xDB[_index]; double value = yDB[_index]; series.Points.AddXY(nowTime, value); _isViewValid = true; // 视图滚动处理 UpdateViewForCurrentMode(pointsep = 10); } // 移动到下一个点 _index++; // 重置检查(放在最后确保完成当前点处理) if (_index >= xDB.Count) { _index = 0; } // Debug.WriteLine("CreateChart"); } private void UpdateViewForCurrentMode(int pointsep =10) { var area = Chart_.ChartAreas[0]; var axisX = area.AxisX; var series = Chart_.Series[0]; if (ShohType == "OverView") { //// 固定左侧为最早时间点 //DateTime minTime = xDB[0]; //axisX.Minimum = minTime.ToOADate(); //// 扩展右侧到最新时间点 //DateTime maxTime = xDB[_index]; //axisX.Maximum = maxTime.ToOADate(); //// 强制缩放以显示完整范围 //axisX.ScaleView.Zoom(axisX.Minimum, axisX.Maximum); const int viewSize = 250; int viewPoinr = _index / pointsep; Chart_.ChartAreas[0].AxisX.ScaleView.Position = 1; if (viewPoinr > viewSize) { double max = Chart_.ChartAreas[0].AxisX.Maximum; max = (viewPoinr / viewSize + 1) * viewSize; Chart_.ChartAreas[0].AxisX.Interval = max / viewSize; Chart_.ChartAreas[0].AxisX.ScaleView.Size = viewPoinr * 1.1; } else { Chart_.ChartAreas[0].AxisX.ScaleView.Size = viewSize; } //axisX.ScaleView.Position = 0; //axisX.IntervalAutoMode = IntervalAutoMode.VariableCount; //if (series.Points.Count > 10) //{ // axisX.Interval = series.Points.Count / 10.0; //} } else if (ShohType == "Follow") { const int viewSize = 50; axisX.Interval = 1; axisX.ScaleView.Size = viewSize; // 计算滚动位置(确保显示最新点) if (series.Points.Count > viewSize) { axisX.ScaleView.Position = series.Points.Count - viewSize; } else { axisX.ScaleView.Position = 0; } } } public void ResetPlayback() { _index = 0; if (Chart_.Series.Count > 0) { Chart_.Series[0].Points.Clear(); } // 修复:重置视图范围 AdjustView(); // 重置游标线到起始位置 if (xDB.Count > 0) { _cursorLine.X = xDB[0].ToOADate(); UpdateCursorPosition(_cursorLine.X); } //Debug.WriteLine("ResetPlayback"); } private void AdjustView0() { if (xDB.Count < 2) return; var area = Chart_.ChartAreas[0]; DateTime minTime = xDB[0]; DateTime maxTime = xDB[xDB.Count -1]; area.AxisX.Minimum = startTime.ToOADate(); area.AxisX.Maximum = endTime.ToOADate(); // 使用索引器获取最后一个元素 area.AxisX.ScaleView.ZoomReset(); } public void TogglePlay() { // 修复1:如果已经播放完毕,重置索引 if (_index >= xDB.Count) { ResetPlayback(); } _isPlaying = !_isPlaying; PlayTimer.Enabled = _isPlaying; // 调试输出 Debug.WriteLine($"TogglePlay 播放状态: {_isPlaying}, 当前索引: {_index}, 数据总数: {xDB.Count}"); } // 鼠标按下事件:开始拖动 private void Chart_MouseDown(object sender, MouseEventArgs e) { _isViewValid = true; // 标记视图有效 if (e.Button == MouseButtons.Left) { var chartArea = Chart_.ChartAreas[0]; _dragStartPoint = e.Location; _startPosition = chartArea.AxisX.ScaleView.Position; _startSize = chartArea.AxisX.ScaleView.Size; _isDragging = true; Chart_.Cursor = Cursors.SizeWE; // 设置水平拖动光标 } //Debug.WriteLine($"Chart_MouseDown"); } // 鼠标移动事件:处理拖动 private void Chart_MouseMove(object sender, MouseEventArgs e) { // 处理游标线移动 HandleCursorMovement(e); // 仅当游标线未被拖动时处理 if (!_isCursorMoving && e.Button == MouseButtons.None) { // 获取鼠标位置的X坐标 var chartArea1 = Chart_.ChartAreas[0]; double xValue = chartArea1.AxisX.PixelPositionToValue(e.X); // 更新游标线位置 _cursorLine.X = xValue; UpdateCursorPosition(xValue); } _isViewValid = true; // 标记视图有效 if (!_isDragging) return; var chartArea = Chart_.ChartAreas[0]; double positionChange = (e.X - _dragStartPoint.X) * chartArea.AxisX.ScaleView.Size / Chart_.Width; double newPosition = _startPosition - positionChange; // 保护边界 if (newPosition < chartArea.AxisX.Minimum) newPosition = chartArea.AxisX.Minimum; double maxPosition = chartArea.AxisX.Maximum - chartArea.AxisX.ScaleView.Size; if (newPosition > maxPosition) newPosition = maxPosition; chartArea.AxisX.ScaleView.Position = newPosition; //Debug.WriteLine($"Chart_MouseMove"); } // 鼠标释放事件:结束拖动 private void Chart_MouseUp(object sender, MouseEventArgs e) { _isViewValid = true; // 标记视图有效 if (e.Button == MouseButtons.Left && _isDragging) { _isDragging = false; Chart_.Cursor = Cursors.Default; } // 结束游标线拖动 if (_isCursorMoving && e.Button == MouseButtons.Left) { _isCursorMoving = false; Chart_.Cursor = Cursors.Default; } //Debug.WriteLine($"Chart_MouseUpe"); } // 鼠标离开时 private void Chart_MouseLeave(object sender, EventArgs e) { // 鼠标离开时隐藏游标线文本 if (!_isCursorMoving) { _cursorText.Visible = false; } } // 鼠标滚轮事件:缩放功能 private void Chart_MouseWheel2(object sender, MouseEventArgs e) { _isViewValid = true; // 标记视图有效 var chartArea = Chart_.ChartAreas[0]; // 确保轴范围已初始化 if (double.IsNaN(chartArea.AxisX.Minimum) || double.IsNaN(chartArea.AxisX.Maximum) || xDB.Count == 0) { return; // 避免在未初始化状态下操作 } try { // 获取当前视图范围(OLE自动化日期格式) double currentPosition = chartArea.AxisX.ScaleView.Position; double currentSize = chartArea.AxisX.ScaleView.Size; // 计算鼠标在X轴上的比例位置 double mousePositionRatio = (double)e.X / Chart_.Width; // 将鼠标位置转换为轴上的实际 double axisPosition = chartArea.AxisX.Minimum + mousePositionRatio * (chartArea.AxisX.Maximum - chartArea.AxisX.Minimum); // 缩放因子 double zoomFactor = e.Delta > 0 ? 0.9 : 1.1; // 计算新的大小(确保在合理范围内) double newSize = Math.Max(0.001, Math.Min( chartArea.AxisX.Maximum - chartArea.AxisX.Minimum, currentSize * zoomFactor )); // 计算新位置(确保在数据范围内) double newPosition = Math.Max( chartArea.AxisX.Minimum, Math.Min( chartArea.AxisX.Maximum - newSize, axisPosition - (mousePositionRatio * newSize) ) ); // 应用新的视图(使用安全的日期范围) chartArea.AxisX.ScaleView.Zoom( newPosition, newPosition + newSize ); } catch (ArgumentException ex) { Debug.WriteLine($"缩放错误: {ex.Message}"); // 恢复安全视图 ResetViewToSafeRange(); } //Debug.WriteLine($"Chart_MouseWheel2"); } private void Chart_MouseWheel(object sender, MouseEventArgs e) { _isViewValid = true; // 标记视图有效 var chartArea = Chart_.ChartAreas[0]; // 0. 初始化检查:确保有数据和有效的轴范围 if (xDB.Count == 0) return; // 1. 确保轴范围已初始化(处理未初始化情况) if (double.IsNaN(chartArea.AxisX.Minimum) || double.IsNaN(chartArea.AxisX.Maximum)) { // 初始化范围为完整数据集 chartArea.AxisX.Minimum = xDB[0].ToOADate(); chartArea.AxisX.Maximum = xDB[xDB.Count-1].ToOADate(); // C# 8.0索引语法 } // 2. 可靠获取当前视图大小(三种备选方案) double currentSize; // 方案A:直接获取ScaleView.Size if (!double.IsNaN(chartArea.AxisX.ScaleView.Size) && chartArea.AxisX.ScaleView.Size > 0) { currentSize = chartArea.AxisX.ScaleView.Size; } // 方案B:计算视图大小(适用于ScaleView.Size为NaN的情况) else if (!double.IsNaN(chartArea.AxisX.ScaleView.ViewMinimum) && !double.IsNaN(chartArea.AxisX.ScaleView.ViewMaximum)) { currentSize = chartArea.AxisX.ScaleView.ViewMaximum - chartArea.AxisX.ScaleView.ViewMinimum; } // 方案C:使用完整数据范围(最后备选) else { currentSize = chartArea.AxisX.Maximum - chartArea.AxisX.Minimum; } // 3. 计算鼠标位置对应的实际 double mousePositionRatio = (double)e.X / Chart_.Width; double axisMousePosition = chartArea.AxisX.Minimum + mousePositionRatio * (chartArea.AxisX.Maximum - chartArea.AxisX.Minimum); // 4. 计算缩放因子 double zoomFactor = e.Delta > 0 ? 0.9 : 1.1; // 滚轮向上=放大(0.9),向下=缩小(1.1) // 5. 计算新视图大小(添加边界保护) double minAllowedSize = (chartArea.AxisX.Maximum - chartArea.AxisX.Minimum) * 0.001; // 最小显示0.1%范围 double newSize = Math.Max(minAllowedSize, currentSize * zoomFactor); newSize = Math.Min(newSize, chartArea.AxisX.Maximum - chartArea.AxisX.Minimum); // 6. 计算新视图位置(保持鼠标点位置不变) double newPosition = axisMousePosition - (mousePositionRatio * newSize); // 7. 应用边界约束 newPosition = Math.Max(chartArea.AxisX.Minimum, newPosition); newPosition = Math.Min(chartArea.AxisX.Maximum - newSize, newPosition); // 8. 应用缩放 try { chartArea.AxisX.ScaleView.Zoom(newPosition, newPosition + newSize); } catch (ArgumentException ex) { //Debug.WriteLine($"缩放错误: {ex.Message}"); // 恢复安全范围 ResetViewToSafeRange(); } } // 添加安全范围重置方法 private void ResetViewToSafeRange() { if (xDB.Count == 0) return; var area = Chart_.ChartAreas[0]; DateTime minDate = xDB.Min(); DateTime maxDate = xDB.Max(); area.AxisX.Minimum = minDate.ToOADate(); area.AxisX.Maximum = maxDate.ToOADate(); area.AxisX.ScaleView.ZoomReset(); //Debug.WriteLine($"ResetViewToSafeRange"); } // 修改 AdjustView 方法(确保初始化范围) private void AdjustView(bool fullView = false) { if (xDB.Count < 2) return; var area = Chart_.ChartAreas[0]; // 3. 设置轴范围(使用OLE日期) double minDate = xDB[0].ToOADate(); double maxDate = xDB[xDB.Count - 1].ToOADate(); // 使用最后一个元素 // 4. 设置前清除缩放状态(关键步骤) //area.AxisX.ScaleView.ZoomReset(); // 5. 设置最小最大 area.AxisX.Minimum = minDate; area.AxisX.Maximum = maxDate; // 7. 强制刷新 Chart_.Refresh(); // 如果是完整视图模式,重置缩放 if (fullView) { area.AxisX.ScaleView.ZoomReset(); ShohType = "OverView"; } //Debug.WriteLine($"AdjustView"); } // 缩放时动态调整网格线 暂时不用, 用了有影响 private void OnAxisViewChanged(object sender, ViewEventArgs e) { // 当视图改变时确保范围安全 //var axis = e.Axis; //if (axis.AxisName == AxisName.X) //{ // SetSafeXRange( // DateTime.FromOADate(e.NewPositionStart), // DateTime.FromOADate(e.NewPositionEnd) // ); //} if (e.Axis.AxisName == AxisName.X || e.Axis.AxisName == AxisName.Y) { UpdateGridDensity(); } } // 根据缩放级别动态更新网格密度 暂时不用, 用了有影响 private void UpdateGridDensity() { var area = Chart_.ChartAreas[0]; var axisX = area.AxisX; var axisY = area.AxisY; // 计算X轴网格密度(基于可见范围) double xRange = axisX.ScaleView.ViewMaximum - axisX.ScaleView.ViewMinimum; double xInterval = CalculateOptimalInterval(xRange); // 计算Y轴网格密度 double yRange = axisY.ScaleView.ViewMaximum - axisY.ScaleView.ViewMinimum; double yInterval = CalculateOptimalInterval(yRange); // 应用网格间隔 axisX.MajorGrid.Interval = xInterval; axisX.MinorGrid.Interval = xInterval / 5; // 次要网格更密集 axisY.MajorGrid.Interval = yInterval; axisY.MinorGrid.Interval = yInterval / 5; // 更新标签间隔(防止标签重叠) axisX.LabelStyle.Interval = xInterval * 2; axisY.LabelStyle.Interval = yInterval * 2; //Debug.WriteLine($"UpdateGridDensity"); } // 计算最优网格间隔(基于对数缩放) 暂时不用, 用了有影响 private double CalculateOptimalInterval1(double range) { if (range <= 0) return 1; // 计算数量级(10的幂) int magnitude = (int)Math.Floor(Math.Log10(range)); double baseInterval = Math.Pow(10, magnitude); // 确定最佳间隔(1, 2, 5序列) if (range / baseInterval < 2) return baseInterval / 5; else if (range / baseInterval < 5) return baseInterval / 2; else if (range / baseInterval < 10) return baseInterval; else if (range / baseInterval < 20) return baseInterval * 2; else return baseInterval * 5; } // 批量加载数据点 (替代原来的逐点添加) public void LoadAllData() { try { _isDataLoaded = true; _isViewValid = true; this._index = 0; Chart_.SuspendLayout(); Chart_.Series[0].Points.SuspendUpdates(); InitChart(); for (int i = 0; i < xDB.Count; i++) { // 批量添加点(每100点刷新一次) if (i % 100 == 0 || i == xDB.Count - 1) { Chart_.Series[0].Points.AddXY(xDB[i], yDB[i]); //Chart_.Refresh(); //Application.DoEvents(); // 允许UI处理事件 } } //this.RefreshChart(); _isDataLoaded = true; _isViewValid = true; } finally { Chart_.Series[0].Points.ResumeUpdates(); Chart_.ResumeLayout(); Chart_.Refresh(); } if (xDB.Count > 0) { _cursorLine.X = xDB[0].ToOADate(); UpdateCursorPosition(_cursorLine.X); } return; } // 手动实现游标线拖拽功能 private void HandleCursorMovement(MouseEventArgs e) { if (_cursorLine == null) return; var chartArea = Chart_.ChartAreas[0]; double cursorX = _cursorLine.X; double cursorY = chartArea.AxisY.PositionToValue(e.Y); // 计算游标线在屏幕上的位置 int cursorScreenX = (int)chartArea.AxisX.ValueToPixelPosition(cursorX); // 检查是否点击在游标线上(±5像素范围内) if (Math.Abs(e.X - cursorScreenX) <= 5) { // 开始拖动 if (e.Button == MouseButtons.Left) { _isCursorMoving = true; _cursorDragStart = e.Location; _cursorOriginalX = cursorX; Chart_.Cursor = Cursors.SizeWE; } } // 拖动中 if (_isCursorMoving && e.Button == MouseButtons.Left) { double deltaX = chartArea.AxisX.PixelPositionToValue(e.X) - chartArea.AxisX.PixelPositionToValue(_cursorDragStart.X); double newX = _cursorOriginalX + deltaX; // 确保游标线在数据范围内 double minX = chartArea.AxisX.Minimum; double maxX = chartArea.AxisX.Maximum; if (newX < minX) newX = minX; if (newX > maxX) newX = maxX; // 更新游标线位置 _cursorLine.X = newX; UpdateCursorPosition(newX); // 重绘图表 Chart_.Refresh(); } } private void CursorLine_PositionChanging(object sender, AnnotationPositionChangingEventArgs e) { _isCursorMoving = true; UpdateCursorPosition(e.NewLocationX); } private void CursorLine_PositionChanged(object sender, EventArgs e) { _isCursorMoving = false; } private void UpdateCursorPosition(double xPosition) { if (xDB == null || yDB == null || xDB.Count == 0) { if (_cursorText != null) _cursorText.Visible = false; return; } // 将X位置转换为DateTime DateTime cursorTime = DateTime.FromOADate(xPosition); // 查找最接近的数据点 int nearestIndex = FindNearestPointIndex(cursorTime); if (nearestIndex < 0 || nearestIndex >= xDB.Count) return; // 更新文本显示 string timeStr = xDB[nearestIndex].ToString("HH:mm:ss.fff"); double value = yDB[nearestIndex]; _cursorText.Text = $"Time: {timeStr}\nValue: {Math.Round(value, 2)}"; // 设置文本位置(在游标线右侧) _cursorText.X = xPosition; // 根据Y轴范围确定文本位置 var chartArea = Chart_.ChartAreas[0]; double yRange = chartArea.AxisY.Maximum - chartArea.AxisY.Minimum; _cursorText.Y = chartArea.AxisY.Maximum - yRange * 0.05; _cursorText.Visible = true; } private int FindNearestPointIndex(DateTime targetTime) { if (xDB == null || xDB.Count == 0) return -1; // 二分查找最近的点 int low = 0; int high = xDB.Count - 1; while (low <= high) { int mid = (low + high) / 2; int compare = targetTime.CompareTo(xDB[mid]); if (compare == 0) return mid; if (compare < 0) high = mid - 1; else low = mid + 1; } // 确定最近的点 if (high < 0) return 0; if (low >= xDB.Count) return xDB.Count - 1; double diffLow = Math.Abs((targetTime - xDB[low]).TotalMilliseconds); double diffHigh = Math.Abs((targetTime - xDB[high]).TotalMilliseconds); return diffLow < diffHigh ? low : high; } private int FindNearestPointIndex2(DateTime targetTime) { if (xDB == null || xDB.Count == 0) return -1; // 使用线性查找(对于大数据量,可以切换为二分查找) int nearestIndex = 0; double minDiff = double.MaxValue; for (int i = 0; i < xDB.Count; i++) { double diff = Math.Abs((targetTime - xDB[i]).TotalMilliseconds); if (diff < minDiff) { minDiff = diff; nearestIndex = i; } } return nearestIndex; } // 添加方法以编程方式设置游标位置 public void SetCursorPosition(DateTime time) { if (xDB == null || xDB.Count == 0) return; _cursorLine.X = time.ToOADate(); UpdateCursorPosition(_cursorLine.X); Chart_.Refresh(); } // 添加方法获取当前游标位置 public DateTime GetCursorTime() { return DateTime.FromOADate(_cursorLine.X); } public double GetCursorValue() { int index = FindNearestPointIndex(GetCursorTime()); if (index < 0 || index >= yDB.Count) return double.NaN; return yDB[index]; } // 修改现有的AdjustView方法 // 修复视图调整方法 备用 private void AdjustView2(bool fullView = false) { if (xDB.Count == 0) return; var area = Chart_.ChartAreas[0]; double minX = xDB[0].ToOADate(); double maxX = xDB.Last().ToOADate(); // 设置X轴范围 area.AxisX.Minimum = minX; area.AxisX.Maximum = maxX; if (fullView) { area.AxisX.ScaleView.ZoomReset(); ShohType = "OverView"; } else if (ShohType == "Follow") { double viewSize = area.AxisX.ScaleView.Size; if (double.IsNaN(viewSize) || viewSize <= 0) viewSize = (maxX - minX) * 0.1; // 默认显示10%范围 double newPosition = Math.Max(minX, maxX - viewSize); area.AxisX.ScaleView.Position = newPosition; } // 自动调整Y轴范围 double minY = yDB.Min(); double maxY = yDB.Max(); double margin = (maxY - minY) * 0.1; // 10%边距 area.AxisY.Minimum = minY - margin; area.AxisY.Maximum = maxY + margin; } // 修复网格密度计算方法 用了有影响 private double CalculateOptimalInterval(double range) { if (range <= 0) return 1; double logVal = Math.Log10(range); int magnitude = (int)Math.Floor(logVal); double fraction = logVal - magnitude; double[] intervals = { 1, 2, 5, 10 }; double baseInterval = Math.Pow(10, magnitude); // 选择最合适的间隔 if (fraction < 0.3) return baseInterval * intervals[0]; if (fraction < 0.6) return baseInterval * intervals[1]; return baseInterval * intervals[2]; } // 8. 添加安全缩放重置方法 暂时无用 public void SafeResetView() { var area = Chart_.ChartAreas[0]; // 禁用自动范围 //area.AxisX.IsAutoMaximum = false; //area.AxisX.IsAutoMinimum = false; // 重置缩放视图 try { area.AxisX.ScaleView.ZoomReset(); } catch { // 故障恢复 if (xDB.Count > 0) { double min = xDB[0].ToOADate(); double max = xDB[xDB.Count - 1].ToOADate(); // 设置最小最大 area.AxisX.Minimum = min; area.AxisX.Maximum = max; // 强制设置视图 area.AxisX.ScaleView.Zoom(min, max); } } _isViewValid = true; } } } - $exception {"“AnnotationCollection”中已存在名为“CursorLine”的图表元素。"} System.ArgumentException
11-13
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值