diff --git a/maui/src/Charts/Area/Partial/CartesianChartArea.cs b/maui/src/Charts/Area/Partial/CartesianChartArea.cs index 1559fb51..5bb4cab4 100644 --- a/maui/src/Charts/Area/Partial/CartesianChartArea.cs +++ b/maui/src/Charts/Area/Partial/CartesianChartArea.cs @@ -77,8 +77,9 @@ internal void CalculateSbsPosition() var groupingKeys = new Dictionary(); - foreach (CartesianSeries xAxisRegSeries in cartesianSeries.ActualXAxis.RegisteredSeries.Cast()) + foreach (var xAxisRegItem in cartesianSeries.ActualXAxis.RegisteredSeries) { + if (xAxisRegItem is not CartesianSeries xAxisRegSeries) continue; if (xAxisRegSeries.IsSideBySide) { if (!xAxisRegSeries.IsSbsValueCalculated && xAxisRegSeries.ActualXAxis != null) @@ -102,7 +103,8 @@ internal void CalculateSbsPosition() { if (!stackingSeries.IsSbsValueCalculated && _seriesGroup != null) { - string groupID = _seriesGroup.FirstOrDefault(x => x.Value.Any(s => s.GroupingLabel == stackingSeries.GroupingLabel && s.GetType() == stackingSeries.GetType())).Key; + var stackingSeriesType = stackingSeries.GetType(); + string groupID = _seriesGroup.FirstOrDefault(x => x.Value.Any(s => s.GroupingLabel == stackingSeries.GroupingLabel && s.GetType() == stackingSeriesType)).Key; StackingSeriesBase stackingSeriesBase; int size = SideBySideSeriesPosition.Count > 0 && groupingKeys.Count > 0 && groupingKeys.TryGetValue(groupID, out var groupValue) ? SideBySideSeriesPosition[groupValue].Count : 0; @@ -183,9 +185,10 @@ internal void UpdateSBS() double totalWidth = GetTotalWidth() / SideBySideSeriesPosition.Count; double startPosition = 0, end = 0; + var seriesPositionValues = SideBySideSeriesPosition.Values.ToList(); for (int i = 0; i < SideBySideSeriesPosition.Count; i++) { - var seriesGroup = SideBySideSeriesPosition.Values.ToList()[i]; + var seriesGroup = seriesPositionValues[i]; double sbsMaxWidth = GetSBSMaxWidth(seriesGroup); foreach (ChartSeries chartSeries in seriesGroup) @@ -370,10 +373,11 @@ double GetTotalWidth() if (SideBySideSeriesPosition != null) { + var positionValues = SideBySideSeriesPosition.Values.ToList(); for (int i = 0; i < SideBySideSeriesPosition.Count; i++) { double maxWidth = 0; - foreach (ChartSeries sideBySideSeries in SideBySideSeriesPosition.Values.ToList()[i]) + foreach (ChartSeries sideBySideSeries in positionValues[i]) { CartesianSeries cartesianSeries = (CartesianSeries)sideBySideSeries; double width = cartesianSeries.GetActualWidth(); @@ -432,10 +436,12 @@ internal void UpdateStackingSeries() if (_seriesGroup.TryGetValue(stackingGroup, out List? seriesList)) { - if (seriesList.Any(x => x.ActualXAxis != stackingXAxis || x.ActualYAxis != stackingYAxis) || (seriesList[0].GetType() != stackingSeries.GetType() && stackingGroup != stackingSeries.GroupingLabel)) + var stackingSeriesType = stackingSeries.GetType(); + var stackingSeriesTypeName = stackingSeriesType.Name; + if (seriesList.Any(x => x.ActualXAxis != stackingXAxis || x.ActualYAxis != stackingYAxis) || (seriesList[0].GetType() != stackingSeriesType && stackingGroup != stackingSeries.GroupingLabel)) { string key = _seriesGroup.FirstOrDefault(x => x.Value.Any(y => - y.GetType().Name == stackingSeries.GetType().Name && + y.GetType().Name == stackingSeriesTypeName && y.GroupingLabel == "" && y.ActualYAxis?.RegisteredSeries.Contains(stackingSeries) == true)).Key; diff --git a/maui/src/Charts/Axis/CategoryAxis.cs b/maui/src/Charts/Axis/CategoryAxis.cs index c45125f2..be23854c 100644 --- a/maui/src/Charts/Axis/CategoryAxis.cs +++ b/maui/src/Charts/Axis/CategoryAxis.cs @@ -32,17 +32,20 @@ protected sealed override DoubleRange ApplyRangePadding(DoubleRange range, doubl internal void GroupData() { List groupingValues = []; + var groupingValuesSet = new HashSet(); List groupedDatas = []; - foreach (CartesianSeries series in RegisteredSeries.Cast()) + foreach (var item in RegisteredSeries) { + if (item is not CartesianSeries series) continue; + if (series.ActualXValues is List xValues) { if (groupedDatas.Count != 0) { for (int j = 0; j <= xValues.Count - 1; j++) { - if (!groupingValues.Contains(xValues[j])) + if (groupingValuesSet.Add(xValues[j])) { groupingValues.Add(xValues[j]); } @@ -51,11 +54,20 @@ internal void GroupData() else { groupingValues.AddRange(xValues); + foreach (var xVal in xValues) + { + groupingValuesSet.Add(xVal); + } } } else if (series.ActualXValues != null) { - groupingValues.AddRange(from val in (series.ActualXValues as List) select val.ToString()); + foreach (var val in (series.ActualXValues as List)!) + { + var str = val.ToString(); + groupingValues.Add(str); + groupingValuesSet.Add(str); + } } if (groupingValues.Count != groupedDatas.Count) @@ -65,16 +77,23 @@ internal void GroupData() } var distinctXValues = groupingValues.Distinct().ToList(); + var indexMap = new Dictionary(distinctXValues.Count); + for (int i = 0; i < distinctXValues.Count; i++) + { + indexMap[distinctXValues[i]] = i; + } - foreach (CartesianSeries series in RegisteredSeries.Cast()) + foreach (var item in RegisteredSeries) { + if (item is not CartesianSeries series) continue; + if (series.ActualXValues is List list) { - series.GroupedXValuesIndexes = (from val in list select (double)distinctXValues.IndexOf(val)).ToList(); + series.GroupedXValuesIndexes = list.Select(val => (double)indexMap[val]).ToList(); } else if (series.ActualXValues != null) { - series.GroupedXValuesIndexes = (from val in series.ActualXValues as List select (double)distinctXValues.IndexOf(val.ToString())).ToList(); + series.GroupedXValuesIndexes = (series.ActualXValues as List)!.Select(val => (double)indexMap[val.ToString()]).ToList(); } series.GroupedXValues = distinctXValues; @@ -160,8 +179,8 @@ internal override void GenerateVisibleLabels() internal string GetLabelContent(ChartSeries? chartSeries, int pos, string labelFormat) #pragma warning restore IDE0060 // Remove unused parameter { - var labelContent = string.Empty; int count = 0; + System.Text.StringBuilder? labelBuilder = null; foreach (var series in RegisteredSeries) { @@ -221,9 +240,16 @@ internal string GetLabelContent(ChartSeries? chartSeries, int pos, string labelF label = GetActualLabelContent(xValue, labelFormat); } - if (!string.IsNullOrEmpty(label.ToString()) && !labelContent.Equals(label, StringComparison.Ordinal) && ArrangeByIndex) + if (!string.IsNullOrEmpty(label.ToString()) && ArrangeByIndex) { - labelContent = count > 0 && !string.IsNullOrEmpty(labelContent) ? labelContent + ", " + label : label.ToString(); + if (labelBuilder == null) + { + labelBuilder = new System.Text.StringBuilder(label); + } + else if (count > 0 && !labelBuilder.ToString().Equals(label, StringComparison.Ordinal)) + { + labelBuilder.Append(", ").Append(label); + } } if (!ArrangeByIndex) @@ -236,7 +262,7 @@ internal string GetLabelContent(ChartSeries? chartSeries, int pos, string labelF } } - return labelContent; + return labelBuilder?.ToString() ?? string.Empty; } #endregion @@ -257,9 +283,10 @@ internal string GetLabelContent(ChartSeries? chartSeries, int pos, string labelF if (IsPolarArea) { - foreach (PolarSeries series in visibleSeries.Cast()) + foreach (var item in visibleSeries) { - if (series != null && series.ActualXAxis == this && series.PointsCount > dataCount) + if (item is not PolarSeries series) continue; + if (series.ActualXAxis == this && series.PointsCount > dataCount) { selectedSeries = series; dataCount = series.PointsCount; @@ -268,10 +295,10 @@ internal string GetLabelContent(ChartSeries? chartSeries, int pos, string labelF } else { - - foreach (CartesianSeries series in visibleSeries.Cast()) + foreach (var item in visibleSeries) { - if (series != null && series.ActualXAxis == this && series.PointsCount > dataCount) + if (item is not CartesianSeries series) continue; + if (series.ActualXAxis == this && series.PointsCount > dataCount) { selectedSeries = series; dataCount = series.PointsCount; diff --git a/maui/src/Charts/Behaviors/ChartTrackballBehavior.cs b/maui/src/Charts/Behaviors/ChartTrackballBehavior.cs index b3fd0289..ecfa85a6 100644 --- a/maui/src/Charts/Behaviors/ChartTrackballBehavior.cs +++ b/maui/src/Charts/Behaviors/ChartTrackballBehavior.cs @@ -975,7 +975,11 @@ void GenerateTrackball(float pointX, float pointY) { if (CartesianChart != null && (CartesianChart is IChart chart)) { - PreviousPointInfos = new List(PointInfos); + if (PreviousPointInfos == null) + PreviousPointInfos = new List(PointInfos.Count); + else + PreviousPointInfos.Clear(); + PreviousPointInfos.AddRange(PointInfos); PointInfos.Clear(); _isAnySideBySideSeries = false; _isAnyContinuesSeries = false; @@ -988,8 +992,9 @@ void GenerateTrackball(float pointX, float pointY) foreach (ChartAxis chartAxis in xAxes) { - foreach (CartesianSeries series in chartAxis.RegisteredSeries.Cast()) + foreach (var axisItem in chartAxis.RegisteredSeries) { + if (axisItem is not CartesianSeries series) continue; if (series.IsVisible && series.ShowTrackballLabel && series.PointsCount > 0) { List nearestDataPoints = series.FindNearestChartPoints(pointX, pointY); @@ -1177,7 +1182,8 @@ void GenerateTrackballGroupedLabel() void GenerateAxisTrackballInfos(float leastX) { - _previousAxisPointInfos = new List(_axisPointInfos); + _previousAxisPointInfos = new List(_axisPointInfos.Count); + _previousAxisPointInfos.AddRange(_axisPointInfos); _axisPointInfos.Clear(); if (CartesianChart == null) diff --git a/maui/src/Charts/Segment/ErrorBarSegment.cs b/maui/src/Charts/Segment/ErrorBarSegment.cs index aee89e45..7eca0ace 100644 --- a/maui/src/Charts/Segment/ErrorBarSegment.cs +++ b/maui/src/Charts/Segment/ErrorBarSegment.cs @@ -83,10 +83,30 @@ internal override void SetData(IList xList, IList yList) break; } - double xMin = xValues.Where(v => !double.IsNaN(v)).DefaultIfEmpty(0).Min(); - double xMax = xValues.Where(v => !double.IsNaN(v)).DefaultIfEmpty(0).Max(); - double yMin = yValues.Where(v => !double.IsNaN(v)).DefaultIfEmpty(0).Min(); - double yMax = yValues.Where(v => !double.IsNaN(v)).DefaultIfEmpty(0).Max(); + double xMin = double.MaxValue, xMax = double.MinValue; + double yMin = double.MaxValue, yMax = double.MinValue; + for (int idx = 0; idx < xValues.Count; idx++) + { + double xv = xValues[idx]; + if (!double.IsNaN(xv)) + { + if (xv < xMin) xMin = xv; + if (xv > xMax) xMax = xv; + } + } + for (int idx = 0; idx < yValues.Count; idx++) + { + double yv = yValues[idx]; + if (!double.IsNaN(yv)) + { + if (yv < yMin) yMin = yv; + if (yv > yMax) yMax = yv; + } + } + if (xMin == double.MaxValue) xMin = 0; + if (xMax == double.MinValue) xMax = 0; + if (yMin == double.MaxValue) yMin = 0; + if (yMax == double.MinValue) yMax = 0; double leftPointMin = xMin - _horizontalErrorValue; double leftPointMax = xMax - _horizontalErrorValue; diff --git a/maui/src/Charts/Series/RangeColumnSeries.cs b/maui/src/Charts/Series/RangeColumnSeries.cs index b1993cdd..36beaf76 100644 --- a/maui/src/Charts/Series/RangeColumnSeries.cs +++ b/maui/src/Charts/Series/RangeColumnSeries.cs @@ -376,7 +376,7 @@ internal override double GetActualSpacing() if (!double.IsNaN(lowValueContent)) { - tooltipInfo.Text += "/" + (lowValueContent == 0 ? lowValueContent.ToString("0.##") : lowValueContent.ToString("#.##")); + tooltipInfo.Text = string.Concat(tooltipInfo.Text, "/", lowValueContent == 0 ? lowValueContent.ToString("0.##") : lowValueContent.ToString("#.##")); } return tooltipInfo;