diff --git a/maui/src/Calendar/Views/CalendarView/MonthView/MonthView.cs b/maui/src/Calendar/Views/CalendarView/MonthView/MonthView.cs
index 1649f2ad..9d2a2387 100644
--- a/maui/src/Calendar/Views/CalendarView/MonthView/MonthView.cs
+++ b/maui/src/Calendar/Views/CalendarView/MonthView/MonthView.cs
@@ -880,6 +880,24 @@ internal void SetFocusOnViewChanged()
#region Private Methods
+ ///
+ /// Finds the special date icon details for a given date using a loop to avoid delegate allocation.
+ ///
+ /// The date to look up.
+ /// The matching CalendarIconDetails or null.
+ CalendarIconDetails? GetSpecialDateIcon(DateTime dateTime)
+ {
+ for (int i = 0; i < _specialDates.Count; i++)
+ {
+ if (CalendarViewHelper.IsSameDate(_calendarViewInfo.View, _specialDates[i].Date, dateTime, _calendarViewInfo.Identifier))
+ {
+ return _specialDates[i];
+ }
+ }
+
+ return null;
+ }
+
///
/// Method to find the range is present in current view or not.
///
@@ -1635,7 +1653,7 @@ void DrawMonthCells(ICanvas canvas, bool isRTL, float weekNumberWidth, float mon
// The current date is today date and not a range then need to considered the today text style.
bool isTodayDate = todayDate.Date.Equals(dateTime.Date);
//// Stores the special dates icon details for drawing.
- CalendarIconDetails? calendarSpecialDayIconDetails = _specialDates.FirstOrDefault(details => CalendarViewHelper.IsSameDate(_calendarViewInfo.View, details.Date, dateTime, _calendarViewInfo.Identifier));
+ CalendarIconDetails? calendarSpecialDayIconDetails = GetSpecialDateIcon(dateTime);
CalendarTextStyle textStyle = GetMonthCellStyle(dateTime, isTodayDate, isLeadingAndTrailingDates, isBlackoutDate, isDisabledDate, _calendarViewInfo.ShowOutOfRangeDates, calendarSpecialDayIconDetails != null, ref fillColor, cellBackground, trailingLeadingDateBackground, weekendsBackground, todayBackground, disabledDatesBackground, specialDatesBackground, cultureCalendar);
//// If background color is not transparent then the background color for month cell is applied.
if (fillColor != Colors.Transparent)
@@ -3080,7 +3098,7 @@ private void OnDateSemanticsNodeClicked(SemanticsNode node)
string blackOutDate = CalendarViewHelper.IsDateInDateCollection(dateTime, _disabledDates) ? SfCalendarResources.GetLocalizedString("Blackout Date") : string.Empty;
string disabledDate = CalendarViewHelper.IsDisabledDate(dateTime, _calendarViewInfo.View, _calendarViewInfo.EnablePastDates, _calendarViewInfo.MinimumDate, _calendarViewInfo.MaximumDate, _calendarViewInfo.SelectionMode, _calendarViewInfo.RangeSelectionDirection, _selectedRange, _calendarViewInfo.AllowViewNavigation, _calendarViewInfo.Identifier) ? SfCalendarResources.GetLocalizedString("Disabled Date") : string.Empty;
- CalendarIconDetails? calendarSpecialDayIconDetails = _specialDates.FirstOrDefault(details => CalendarViewHelper.IsSameDate(_calendarViewInfo.View, details.Date, dateTime, _calendarViewInfo.Identifier));
+ CalendarIconDetails? calendarSpecialDayIconDetails = GetSpecialDateIcon(dateTime);
string specialDate = calendarSpecialDayIconDetails == null ? string.Empty : SfCalendarResources.GetLocalizedString("Special Date");
string dateType = string.IsNullOrEmpty(specialDate) ? !string.IsNullOrEmpty(blackOutDate) ? blackOutDate : disabledDate : specialDate;
string dateText = isGregorianCalendar ? dateTime.ToString("dddd, dd/MMMM/yyyy") + dateType : dateTime.ToString("dddd, dd/MMMM/yyyy", cultureInfo) + dateType;
diff --git a/maui/src/Charts/Axis/Layouts/CartesianAxisLayout.cs b/maui/src/Charts/Axis/Layouts/CartesianAxisLayout.cs
index 71cfc5df..c4452b84 100644
--- a/maui/src/Charts/Axis/Layouts/CartesianAxisLayout.cs
+++ b/maui/src/Charts/Axis/Layouts/CartesianAxisLayout.cs
@@ -499,10 +499,17 @@ void InternalCreateSegments(ChartSeries series)
static ChartAxis? GetAxisByName(string name, ObservableCollection? axes)
{
- var item = (from x in axes where x.Name == name select x).ToList();
- if (item != null && item.Count > 0)
+ if (axes == null)
+ {
+ return null;
+ }
+
+ foreach (var axis in axes)
{
- return item[0];
+ if (axis.Name == name)
+ {
+ return axis;
+ }
}
return null;
diff --git a/maui/src/Charts/Segment/PolarAreaSegment.cs b/maui/src/Charts/Segment/PolarAreaSegment.cs
index e5d0c130..9db27245 100644
--- a/maui/src/Charts/Segment/PolarAreaSegment.cs
+++ b/maui/src/Charts/Segment/PolarAreaSegment.cs
@@ -212,13 +212,14 @@ void DrawPath(ICanvas canvas, List? fillPoints, List? strokePoints
List GenerateInteriorPoints(float animationValue)
{
- var fillPoints = new List();
-
if (Series is not PolarSeries series)
{
- return fillPoints;
+ return new List();
}
+ int capacity = (_pointsCount + 2) * 2;
+ var fillPoints = new List(capacity);
+
if (series.ActualXAxis != null && series.ActualYAxis != null && _xValues != null && _yValues != null)
{
PointF pointF = series.TransformVisiblePoint(_xValues[0], _yValues[0], animationValue);
@@ -252,13 +253,14 @@ List GenerateInteriorPoints(float animationValue)
List GenerateStrokePoints(float animationValue)
{
- var strokePoints = new List();
-
if (Series is not PolarSeries series)
{
- return strokePoints;
+ return new List();
}
+ int capacity = (_pointsCount + 2) * 2;
+ var strokePoints = new List(capacity);
+
if (series.ActualXAxis != null && series.ActualYAxis != null && _xValues != null && _yValues != null)
{
PointF startPoint = series.TransformVisiblePoint(_xValues[0], _yValues[0], animationValue);
diff --git a/maui/src/Charts/Series/ChartSeries.cs b/maui/src/Charts/Series/ChartSeries.cs
index 62d44d92..8ead0fa9 100644
--- a/maui/src/Charts/Series/ChartSeries.cs
+++ b/maui/src/Charts/Series/ChartSeries.cs
@@ -1582,6 +1582,7 @@ internal void DataLabelSettings_PropertyChanged(object? sender, System.Component
if (e.PropertyName == nameof(dataLabelSettings.LabelStyle))
{
+ dataLabelSettings.LabelStyle.PropertyChanged -= LabelStyle_PropertyChanged;
dataLabelSettings.LabelStyle.PropertyChanged += LabelStyle_PropertyChanged;
dataLabelSettings.LabelStyle.Parent = Parent;
}
diff --git a/maui/src/Picker/SfTimePicker.cs b/maui/src/Picker/SfTimePicker.cs
index 39532836..df168151 100644
--- a/maui/src/Picker/SfTimePicker.cs
+++ b/maui/src/Picker/SfTimePicker.cs
@@ -893,6 +893,23 @@ internal void UpdateFormat()
#region Private Methods
+ ///
+ /// Checks if the collection contains a string that parses to the specified integer value,
+ /// avoiding LINQ Select/Contains allocation overhead.
+ ///
+ static bool ContainsParsedValue(ObservableCollection collection, int value)
+ {
+ for (int i = 0; i < collection.Count; i++)
+ {
+ if (int.TryParse(collection[i], out int parsed) && parsed == value)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
///
/// Method trigged whenever the base panel selection is changed.
///
@@ -1002,7 +1019,7 @@ void UpdateHourColumn(PickerSelectionChangedEventArgs e, string hourFormat, Time
}
int minute = 0;
- if (_minuteColumn.ItemsSource != null && _minuteColumn.ItemsSource is ObservableCollection minuteCollection && minuteCollection.Select(m => int.Parse(m)).Contains(previousSelectedTime.Value.Minutes))
+ if (_minuteColumn.ItemsSource != null && _minuteColumn.ItemsSource is ObservableCollection minuteCollection && ContainsParsedValue(minuteCollection, previousSelectedTime.Value.Minutes))
{
minute = previousSelectedTime.Value.Minutes;
}
@@ -1015,7 +1032,7 @@ void UpdateHourColumn(PickerSelectionChangedEventArgs e, string hourFormat, Time
}
int second = 0;
- if (_secondColumn.ItemsSource != null && _secondColumn.ItemsSource is ObservableCollection secondCollection && secondCollection.Select(m => int.Parse(m)).Contains(previousSelectedTime.Value.Seconds))
+ if (_secondColumn.ItemsSource != null && _secondColumn.ItemsSource is ObservableCollection secondCollection && ContainsParsedValue(secondCollection, previousSelectedTime.Value.Seconds))
{
second = previousSelectedTime.Value.Seconds;
}
@@ -1064,7 +1081,7 @@ void UpdateMinuteColumn(PickerSelectionChangedEventArgs e, TimeSpan? previousSel
}
int second = 0;
- if (_secondColumn.ItemsSource != null && _secondColumn.ItemsSource is ObservableCollection secondCollection && secondCollection.Select(m => int.Parse(m)).Contains(previousSelectedTime.Value.Seconds))
+ if (_secondColumn.ItemsSource != null && _secondColumn.ItemsSource is ObservableCollection secondCollection && ContainsParsedValue(secondCollection, previousSelectedTime.Value.Seconds))
{
second = previousSelectedTime.Value.Seconds;
}
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/PerformanceOptimizationTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/PerformanceOptimizationTests.cs
new file mode 100644
index 00000000..4d99b23e
--- /dev/null
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/PerformanceOptimizationTests.cs
@@ -0,0 +1,161 @@
+using Syncfusion.Maui.Toolkit.Charts;
+
+namespace Syncfusion.Maui.Toolkit.UnitTest.Charts
+{
+ public class PerformanceOptimizationTests
+ {
+ #region CartesianAxisLayout - GetAxisByName
+
+ [Fact]
+ public void CartesianChart_NamedAxes_AssociateCorrectly()
+ {
+ var chart = new SfCartesianChart();
+ var xAxis = new NumericalAxis { Name = "xAxis1" };
+ var yAxis = new NumericalAxis { Name = "yAxis1" };
+
+ chart.XAxes.Add(xAxis);
+ chart.YAxes.Add(yAxis);
+
+ var series = new LineSeries
+ {
+ XAxisName = "xAxis1",
+ YAxisName = "yAxis1"
+ };
+ chart.Series.Add(series);
+
+ Assert.Equal("xAxis1", xAxis.Name);
+ Assert.Equal("yAxis1", yAxis.Name);
+ Assert.Equal("xAxis1", series.XAxisName);
+ Assert.Equal("yAxis1", series.YAxisName);
+ }
+
+ [Fact]
+ public void CartesianChart_MultipleNamedAxes_FindsCorrectAxis()
+ {
+ var chart = new SfCartesianChart();
+ var xAxis1 = new NumericalAxis { Name = "primary" };
+ var xAxis2 = new NumericalAxis { Name = "secondary" };
+ var yAxis1 = new NumericalAxis { Name = "left" };
+ var yAxis2 = new NumericalAxis { Name = "right" };
+
+ chart.XAxes.Add(xAxis1);
+ chart.XAxes.Add(xAxis2);
+ chart.YAxes.Add(yAxis1);
+ chart.YAxes.Add(yAxis2);
+
+ var series1 = new LineSeries
+ {
+ XAxisName = "primary",
+ YAxisName = "left"
+ };
+ var series2 = new LineSeries
+ {
+ XAxisName = "secondary",
+ YAxisName = "right"
+ };
+
+ chart.Series.Add(series1);
+ chart.Series.Add(series2);
+
+ Assert.Equal(2, chart.XAxes.Count);
+ Assert.Equal(2, chart.YAxes.Count);
+ Assert.Equal("secondary", series2.XAxisName);
+ Assert.Equal("right", series2.YAxisName);
+ }
+
+ [Fact]
+ public void CartesianChart_NullAxisName_DoesNotThrow()
+ {
+ var chart = new SfCartesianChart();
+ var xAxis = new NumericalAxis();
+ var yAxis = new NumericalAxis();
+
+ chart.XAxes.Add(xAxis);
+ chart.YAxes.Add(yAxis);
+
+ var series = new LineSeries();
+ chart.Series.Add(series);
+
+ Assert.Null(series.XAxisName);
+ Assert.Null(series.YAxisName);
+ }
+
+ #endregion
+
+ #region DataLabelSettings - LabelStyle PropertyChanged handler
+
+ [Fact]
+ public void DataLabelSettings_LabelStyle_CanBeReassigned()
+ {
+ var settings = new CartesianDataLabelSettings();
+ var labelStyle1 = new ChartDataLabelStyle { TextColor = Colors.Red };
+ var labelStyle2 = new ChartDataLabelStyle { TextColor = Colors.Blue };
+
+ settings.LabelStyle = labelStyle1;
+ Assert.Equal(Colors.Red, settings.LabelStyle.TextColor);
+
+ settings.LabelStyle = labelStyle2;
+ Assert.Equal(Colors.Blue, settings.LabelStyle.TextColor);
+ }
+
+ [Fact]
+ public void DataLabelSettings_LabelStyle_ReassignmentDoesNotThrow()
+ {
+ var series = new ColumnSeries();
+ var settings = new CartesianDataLabelSettings();
+ series.DataLabelSettings = settings;
+
+ var exception = Record.Exception(() =>
+ {
+ for (int i = 0; i < 10; i++)
+ {
+ settings.LabelStyle = new ChartDataLabelStyle
+ {
+ TextColor = Colors.Red,
+ FontSize = 12 + i
+ };
+ }
+ });
+
+ Assert.Null(exception);
+ }
+
+ #endregion
+
+ #region PolarAreaSegment - List capacity optimization
+
+ [Fact]
+ public void PolarAreaSeries_AddedToChart_DoesNotThrow()
+ {
+ var chart = new SfPolarChart();
+ var primaryAxis = new NumericalAxis();
+ var secondaryAxis = new NumericalAxis();
+ chart.PrimaryAxis = primaryAxis;
+ chart.SecondaryAxis = secondaryAxis;
+
+ var series = new PolarAreaSeries();
+ chart.Series.Add(series);
+
+ Assert.Single(chart.Series);
+ Assert.IsType(chart.Series[0]);
+ }
+
+ [Fact]
+ public void PolarAreaSeries_WithData_InitializesCorrectly()
+ {
+ var chart = new SfPolarChart();
+ chart.PrimaryAxis = new NumericalAxis();
+ chart.SecondaryAxis = new NumericalAxis();
+
+ var series = new PolarAreaSeries
+ {
+ IsClosed = true
+ };
+ chart.Series.Add(series);
+
+ Assert.True(series.IsClosed);
+ }
+
+ #endregion
+ }
+}
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerPerformanceTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerPerformanceTests.cs
new file mode 100644
index 00000000..3270cfbc
--- /dev/null
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerPerformanceTests.cs
@@ -0,0 +1,87 @@
+using Syncfusion.Maui.Toolkit.Picker;
+
+namespace Syncfusion.Maui.Toolkit.UnitTest
+{
+ public class TimePickerPerformanceTests : PickerBaseUnitTest
+ {
+ [Theory]
+ [InlineData(1)]
+ [InlineData(5)]
+ [InlineData(15)]
+ public void TimePicker_MinuteInterval_SetAndGet(int interval)
+ {
+ var picker = new SfTimePicker
+ {
+ MinuteInterval = interval
+ };
+
+ Assert.Equal(interval, picker.MinuteInterval);
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(5)]
+ [InlineData(15)]
+ public void TimePicker_SecondInterval_SetAndGet(int interval)
+ {
+ var picker = new SfTimePicker
+ {
+ SecondInterval = interval
+ };
+
+ Assert.Equal(interval, picker.SecondInterval);
+ }
+
+ [Fact]
+ public void TimePicker_SelectedTimePreserved_WhenIntervalsChange()
+ {
+ var picker = new SfTimePicker
+ {
+ SelectedTime = new TimeSpan(10, 30, 45)
+ };
+
+ Assert.Equal(new TimeSpan(10, 30, 45), picker.SelectedTime);
+
+ picker.MinuteInterval = 5;
+
+ // The selected time should still be accessible
+ Assert.NotNull(picker.SelectedTime);
+ }
+
+ [Theory]
+ [InlineData("10:30:00")]
+ [InlineData("23:59:59")]
+ [InlineData("00:00:00")]
+ public void TimePicker_SelectedTime_SetAndRetrieve(string timeStr)
+ {
+ var expected = TimeSpan.Parse(timeStr);
+ var picker = new SfTimePicker
+ {
+ SelectedTime = expected
+ };
+
+ Assert.Equal(expected, picker.SelectedTime);
+ }
+
+ [Fact]
+ public void TimePicker_MultipleIntervalChanges_DoNotThrow()
+ {
+ var picker = new SfTimePicker
+ {
+ SelectedTime = new TimeSpan(12, 15, 30)
+ };
+
+ var exception = Record.Exception(() =>
+ {
+ picker.MinuteInterval = 5;
+ picker.SecondInterval = 10;
+ picker.MinuteInterval = 15;
+ picker.SecondInterval = 30;
+ picker.MinuteInterval = 1;
+ picker.SecondInterval = 1;
+ });
+
+ Assert.Null(exception);
+ }
+ }
+}