diff --git a/maui/samples/Gallery/SampleList/CartesianChartSamplesList.xml b/maui/samples/Gallery/SampleList/CartesianChartSamplesList.xml
index 422c3557..b6612407 100644
--- a/maui/samples/Gallery/SampleList/CartesianChartSamplesList.xml
+++ b/maui/samples/Gallery/SampleList/CartesianChartSamplesList.xml
@@ -57,7 +57,7 @@
-
+
@@ -166,14 +166,15 @@
-
+
+
-
+
diff --git a/maui/samples/Gallery/SampleList/CircularChartSamplesList.xml b/maui/samples/Gallery/SampleList/CircularChartSamplesList.xml
index 006b4ee1..e48d772e 100644
--- a/maui/samples/Gallery/SampleList/CircularChartSamplesList.xml
+++ b/maui/samples/Gallery/SampleList/CircularChartSamplesList.xml
@@ -1,7 +1,7 @@
-
+
@@ -12,12 +12,17 @@
-
+
+
diff --git a/maui/samples/Gallery/SampleList/PickerSamplesList.xml b/maui/samples/Gallery/SampleList/PickerSamplesList.xml
index c1960833..edc44ab5 100644
--- a/maui/samples/Gallery/SampleList/PickerSamplesList.xml
+++ b/maui/samples/Gallery/SampleList/PickerSamplesList.xml
@@ -5,32 +5,36 @@
-
+
-
+
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/Cards/GettingStarted/View/CardView.xaml b/maui/samples/Gallery/Samples/Cards/GettingStarted/View/CardView.xaml
index bdd03e3e..1d6050eb 100644
--- a/maui/samples/Gallery/Samples/Cards/GettingStarted/View/CardView.xaml
+++ b/maui/samples/Gallery/Samples/Cards/GettingStarted/View/CardView.xaml
@@ -31,12 +31,11 @@
Grid.Column="{OnPlatform Default=0, WinUI=1, MacCatalyst=1}"
VerticalOptions="{OnPlatform Default=Center, WinUI=Center, MacCatalyst=Center}"
WidthRequest="{OnPlatform WinUI=400, MacCatalyst=450, Default=350}"
- HeightRequest="{OnPlatform MacCatalyst=700}"
HorizontalOptions="{OnPlatform WinUI=Center, MacCatalyst=Center, Default=Fill}">
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Annotation/ShapeAnnotationSample.xaml b/maui/samples/Gallery/Samples/CartesianChart/Annotation/ShapeAnnotationSample.xaml
index 11ef35ca..457b020e 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Annotation/ShapeAnnotationSample.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Annotation/ShapeAnnotationSample.xaml
@@ -15,7 +15,7 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Annotation/ViewAnnotationSample.xaml b/maui/samples/Gallery/Samples/CartesianChart/Annotation/ViewAnnotationSample.xaml
index 18c82624..97335334 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Annotation/ViewAnnotationSample.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Annotation/ViewAnnotationSample.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.ViewAnnotationSample"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:AnnotationViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaChart.xaml
index ec9fbf97..2dfdf9be 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.AreaChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:AreaSeriesViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaChart_DynamicUpdate.xaml b/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaChart_DynamicUpdate.xaml
index 8bc2b212..7cbcf784 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaChart_DynamicUpdate.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaChart_DynamicUpdate.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.AreaChart_DynamicUpdate"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaSeriesViewModel.cs b/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaSeriesViewModel.cs
index 24fa7c76..3863fe15 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaSeriesViewModel.cs
+++ b/maui/samples/Gallery/Samples/CartesianChart/AreaChart/AreaSeriesViewModel.cs
@@ -25,7 +25,7 @@ public AreaSeriesViewModel()
new ChartDataModel("2012", 3.126, 2.69, 2.44, 2.16),
new ChartDataModel("2013", 3.34, 3.01, 2.77, 2.51),
new ChartDataModel("2014", 3.58, 3.22, 2.91, 2.61),
- ];
+ ];
}
}
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Axis/AxisCrossing.xaml b/maui/samples/Gallery/Samples/CartesianChart/Axis/AxisCrossing.xaml
index b5849c83..0b84843c 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Axis/AxisCrossing.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Axis/AxisCrossing.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.AxisCrossing"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:NumericalAxisViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Axis/CategoryAxis.xaml b/maui/samples/Gallery/Samples/CartesianChart/Axis/CategoryAxis.xaml
index ef7af10b..74456663 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Axis/CategoryAxis.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Axis/CategoryAxis.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.CategoryAxisChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:CategoryAxisViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeAxis.xaml b/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeAxis.xaml
index 5907cafd..4345ef0e 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeAxis.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeAxis.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.DateTimeAxisChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DateTimeAxisViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeCategoryAxis.xaml b/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeCategoryAxis.xaml
new file mode 100644
index 00000000..69f964c3
--- /dev/null
+++ b/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeCategoryAxis.xaml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeCategoryAxis.xaml.cs b/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeCategoryAxis.xaml.cs
new file mode 100644
index 00000000..32ebf923
--- /dev/null
+++ b/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeCategoryAxis.xaml.cs
@@ -0,0 +1,9 @@
+namespace Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart;
+
+public partial class DateTimeCategoryAxis : SampleView
+{
+ public DateTimeCategoryAxis()
+ {
+ InitializeComponent();
+ }
+}
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeCategoryAxisViewModel.cs b/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeCategoryAxisViewModel.cs
new file mode 100644
index 00000000..3da2a97f
--- /dev/null
+++ b/maui/samples/Gallery/Samples/CartesianChart/Axis/DateTimeCategoryAxisViewModel.cs
@@ -0,0 +1,33 @@
+
+using System.Collections.ObjectModel;
+
+namespace Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart
+{
+ public partial class DateTimeCategoryAxisViewModel : BaseViewModel
+ {
+ public ObservableCollection SalesRevenue { get; set; }
+ public ObservableCollection ChartColors { get; set; }
+ public DateTimeCategoryAxisViewModel()
+ {
+ SalesRevenue =
+ [
+ new ChartDataModel() { Date = new DateTime(2017, 12, 24), Value = 40 },
+ new ChartDataModel() { Date = new DateTime(2017, 12, 25), Value = 60 },
+ new ChartDataModel() { Date = new DateTime(2017, 12, 31), Value = 57 },
+ new ChartDataModel() { Date = new DateTime(2018, 01, 01), Value = 64 },
+ new ChartDataModel() { Date = new DateTime(2018, 01, 02), Value = 60 },
+ new ChartDataModel() { Date = new DateTime(2018, 01, 14), Value = 50 },
+ ];
+
+ ChartColors =
+ [
+ new SolidColorBrush(Color.FromArgb("#95DB9C")),
+ new SolidColorBrush(Color.FromArgb("#B95375")),
+ new SolidColorBrush(Color.FromArgb("#56BBAF")),
+ new SolidColorBrush(Color.FromArgb("#606D7F")),
+ new SolidColorBrush(Color.FromArgb("#E99941")),
+ new SolidColorBrush(Color.FromArgb("#327DBE")),
+ ];
+ }
+ }
+}
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Axis/LogarithmicAxis.xaml b/maui/samples/Gallery/Samples/CartesianChart/Axis/LogarithmicAxis.xaml
index 3736ec8d..08a070f2 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Axis/LogarithmicAxis.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Axis/LogarithmicAxis.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.LogarithmicAxisChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:LogarithmicAxisViewModel">
@@ -14,14 +15,15 @@
+
-
+
-
-
+
+
@@ -74,8 +76,8 @@
-
-
+
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Axis/NumericalAxis.xaml b/maui/samples/Gallery/Samples/CartesianChart/Axis/NumericalAxis.xaml
index 630ddd84..35a863c1 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Axis/NumericalAxis.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Axis/NumericalAxis.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.NumericalAxisChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:NumericalAxisViewModel">
@@ -12,16 +13,22 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
+
-
+
@@ -60,7 +67,7 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Bar/BarBackToBack.xaml b/maui/samples/Gallery/Samples/CartesianChart/Bar/BarBackToBack.xaml
index 42ed6fb5..2eeda36d 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Bar/BarBackToBack.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Bar/BarBackToBack.xaml
@@ -3,9 +3,10 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.BarBackToBack"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:BarSeriesViewModel">
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Bar/BarChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/Bar/BarChart.xaml
index 84d7d94f..2269da2c 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Bar/BarChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Bar/BarChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.BarChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:BarSeriesViewModel">
@@ -12,13 +13,17 @@
-
-
+
+
-
-
+
+
+
+
+
+ IsTransposed="True" x:Name="Chart1" HorizontalOptions="Fill" VerticalOptions="Fill">
@@ -46,7 +51,7 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_DynamicUpdate.xaml b/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_DynamicUpdate.xaml
index 5c859cac..2deeac77 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_DynamicUpdate.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_DynamicUpdate.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.Bar_DynamicUpdate"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
@@ -12,7 +13,7 @@
-
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_RoundedEdge.xaml b/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_RoundedEdge.xaml
index b44feded..4931df04 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_RoundedEdge.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_RoundedEdge.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.BarChart_RoundedEdge"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:BarSeriesViewModel">
@@ -12,7 +13,7 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_WidthCustomization.xaml b/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_WidthCustomization.xaml
index fb9e41bf..95d27539 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_WidthCustomization.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Bar/Bar_WidthCustomization.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.Bar_WidthCustomization"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:BarSeriesViewModel">
@@ -12,13 +13,19 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
+
@@ -47,14 +54,14 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/BoxAndWhisker/BoxAndWhisker.xaml b/maui/samples/Gallery/Samples/CartesianChart/BoxAndWhisker/BoxAndWhisker.xaml
index a469d319..a3737f4f 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/BoxAndWhisker/BoxAndWhisker.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/BoxAndWhisker/BoxAndWhisker.xaml
@@ -4,20 +4,27 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.BoxAndWhisker"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:BoxAndWhiskerViewModel">
-
+
-
+
-
+
-
+
-
-
+
+
+
+
+
+
+
+
@@ -35,9 +42,9 @@
ShowOutlier="True"
ShowMedian="True">
-
+
-
+
-
+
@@ -53,7 +60,6 @@
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
@@ -14,15 +15,15 @@
-
+
-
+
-
+ FontSize="16" HorizontalOptions="Fill" VerticalOptions="Center" HorizontalTextAlignment="Center"
+ LineBreakMode="WordWrap" Padding="0,0,0,5"/>
+
-
+
@@ -42,11 +43,11 @@
SizeValuePath="Size"
EnableTooltip="False" Fill="{AppThemeBinding Light={StaticResource series1Light}, Dark={StaticResource series1Dark}}"
EnableAnimation="True"
- ItemsSource="{Binding DynamicBubbleMotionAnimation}"
+ ItemsSource="{Binding DynamicBubbleMotionAnimation}"
ShowDataLabels="False"
- XBindingPath="Value1" YBindingPath="Value"/>
+ XBindingPath="Value1" YBindingPath="Value"/>
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/BubbleChart/DefaultBubbleChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/BubbleChart/DefaultBubbleChart.xaml
index 8f1ca8e1..50e065dd 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/BubbleChart/DefaultBubbleChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/BubbleChart/DefaultBubbleChart.xaml
@@ -4,7 +4,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.DefaultBubbleChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:CartesianBubbleViewModel">
@@ -13,24 +14,29 @@
-
+
-
+
-
+ FontSize="16" HorizontalOptions="Fill" VerticalOptions="Center" HorizontalTextAlignment="Center"
+ LineBreakMode="WordWrap" Padding="0,0,0,5"/>
+
-
+
-
-
+
+
+
+
+
-
+
+
@@ -41,16 +47,16 @@
-
-
-
-
+
+
+
@@ -72,17 +78,16 @@
PaletteBrushes="{AppThemeBinding Light={StaticResource PaletteBrushesLight}, Dark={StaticResource PaletteBrushesDark}}"
EnableTooltip="True" TooltipTemplate="{StaticResource tooltiptemplate}"
ItemsSource="{Binding GDPGrowthCollection}" ShowDataLabels="False"
- XBindingPath="Value" YBindingPath="High"/>
+ XBindingPath="Value" YBindingPath="High"/>
-
+
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/BubbleChart/MultipleColorBubbleSeries.xaml b/maui/samples/Gallery/Samples/CartesianChart/BubbleChart/MultipleColorBubbleSeries.xaml
index 60f58406..b52e2691 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/BubbleChart/MultipleColorBubbleSeries.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/BubbleChart/MultipleColorBubbleSeries.xaml
@@ -4,24 +4,29 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.MultipleColorBubbleSeries"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:CartesianBubbleViewModel">
-
+
-
+
-
+
-
+
-
-
-
+
+
+
+
+
+
-
+
+
@@ -31,19 +36,19 @@
-
+
-
-
-
-
-
+
+
+
+
+
@@ -90,7 +95,7 @@
@@ -112,8 +117,8 @@
-
-
+
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Column/ColumnChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/Column/ColumnChart.xaml
index f786f737..e5151f6d 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Column/ColumnChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Column/ColumnChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.ColumnChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:ColumnSeriesViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Column/Column_Customization.xaml b/maui/samples/Gallery/Samples/CartesianChart/Column/Column_Customization.xaml
index fed4e005..fe9c7bf5 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Column/Column_Customization.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Column/Column_Customization.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.Column_Customization"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:ColumnViewModelEXT" >
@@ -12,11 +13,15 @@
-
-
+
+
-
-
+
+
+
+
+
+
@@ -43,7 +48,7 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Column/Column_DynamicUpdate.xaml b/maui/samples/Gallery/Samples/CartesianChart/Column/Column_DynamicUpdate.xaml
index 493d4385..139fc01e 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Column/Column_DynamicUpdate.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Column/Column_DynamicUpdate.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.Column_DynamicUpdate"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Column/Column_RoundedEdges.xaml b/maui/samples/Gallery/Samples/CartesianChart/Column/Column_RoundedEdges.xaml
index d7bd8ada..0fac9641 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Column/Column_RoundedEdges.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Column/Column_RoundedEdges.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.Column_RoundedEdges"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:ColumnSeriesViewModel">
@@ -43,7 +44,7 @@
+ PaletteBrushes="{AppThemeBinding Light={StaticResource AlterBrushesLight}, Dark={StaticResource AlterBrushesDark}}" XBindingPath="Value" YBindingPath="Size">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Column/Column_WidthCustomization.xaml b/maui/samples/Gallery/Samples/CartesianChart/Column/Column_WidthCustomization.xaml
index 17414439..cde13d6e 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Column/Column_WidthCustomization.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Column/Column_WidthCustomization.xaml
@@ -3,13 +3,18 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.Column_WidthCustomization"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:ColumnSeriesViewModel">
-
-
+
+
-
-
+
+
+
+
+
+
@@ -30,7 +35,7 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/DataLabel/DataLabelTemplate.xaml b/maui/samples/Gallery/Samples/CartesianChart/DataLabel/DataLabelTemplate.xaml
index 7e3eeaee..f47a4fa3 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/DataLabel/DataLabelTemplate.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/DataLabel/DataLabelTemplate.xaml
@@ -3,16 +3,22 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.DataLabelTemplate"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DataLabelViewModel">
-
+
-
+
-
-
-
+
+
+
+
+
+
+
+
@@ -22,10 +28,10 @@
@@ -60,18 +66,18 @@
+ ShowDataLabels="True"
+ ItemsSource="{Binding RevenueCollection}"
+ XBindingPath="Year"
+ YBindingPath="TotalGrossInBillion"
+ LabelTemplate="{StaticResource labelTemplate}">
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/EmptyPoints/EmptyPointSupport.xaml b/maui/samples/Gallery/Samples/CartesianChart/EmptyPoints/EmptyPointSupport.xaml
index 65ef4f83..68467f7b 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/EmptyPoints/EmptyPointSupport.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/EmptyPoints/EmptyPointSupport.xaml
@@ -4,7 +4,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.EmptyPointSupport"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:EmptyPointViewModel">
@@ -104,9 +105,8 @@
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:ErrorBarViewModel">
@@ -12,15 +13,15 @@
-
+
-
+
-
+
-
@@ -38,16 +39,14 @@
-
-
+
+
@@ -66,23 +65,22 @@
-
+
-
+
-
+
-
-
+
@@ -139,7 +135,7 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/ErrorBar/ErrorBarChart.xaml.cs b/maui/samples/Gallery/Samples/CartesianChart/ErrorBar/ErrorBarChart.xaml.cs
index f9dee264..7cb7d26f 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/ErrorBar/ErrorBarChart.xaml.cs
+++ b/maui/samples/Gallery/Samples/CartesianChart/ErrorBar/ErrorBarChart.xaml.cs
@@ -16,9 +16,11 @@ public partial class ErrorBarChart : SampleView
#region Public Constructor
- public ErrorBarChart()
- {
- InitializeComponent();
+ public ErrorBarChart()
+ {
+ InitializeComponent();
+ errorBar.HorizontalErrorValue = 1;
+ errorBar.VerticalErrorValue = 5;
}
#endregion
@@ -141,7 +143,20 @@ private void directionPicker_SelectedIndexChanged(object sender, EventArgs e)
}
}
-
+ private void stepper_ValueChanged(object sender, ValueChangedEventArgs e)
+ {
+ var stepper = (Stepper)sender;
+ var selectedValue = stepper.StyleId;
+ switch (selectedValue)
+ {
+ case "horStepper":
+ errorBar.HorizontalErrorValue = stepper.Value;
+ break;
+ case "verStepper":
+ errorBar.VerticalErrorValue = stepper.Value;
+ break;
+ }
+ }
#endregion
diff --git a/maui/samples/Gallery/Samples/CartesianChart/ErrorBar/ErrorBarDefault.xaml b/maui/samples/Gallery/Samples/CartesianChart/ErrorBar/ErrorBarDefault.xaml
index ee6446aa..404f12d6 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/ErrorBar/ErrorBarDefault.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/ErrorBar/ErrorBarDefault.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.ErrorBarDefault"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:ErrorBarViewModel">
@@ -12,19 +13,22 @@
-
+
-
+
-
-
-
+
+
+
+
+
+
-
-
+
+
@@ -61,7 +65,7 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/FastLineChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/FastLineChart.xaml
index a8f98476..c3b01d71 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/FastLineChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/FastLineChart.xaml
@@ -3,10 +3,11 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.FastLineChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:FastLineSeriesViewModel">
+ VerticalOptions="Fill">
@@ -39,13 +40,13 @@
+ EnablePanning="True"/>
+ EnableAntiAliasing="True"
+ XBindingPath="Date" YBindingPath="Value"/>
diff --git a/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/RealTimeChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/RealTimeChart.xaml
index be12b05f..65de40d5 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/RealTimeChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/RealTimeChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.RealTimeChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:RealTimeChartViewModel">
@@ -14,7 +15,7 @@
+ VerticalOptions="Fill">
@@ -44,8 +45,8 @@
+ EnableAntiAliasing="True"
+ XBindingPath="Value" YBindingPath="Size">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/VerticalLiveChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/VerticalLiveChart.xaml
index 732c33ff..5369c030 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/VerticalLiveChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/FastLineChart/VerticalLiveChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.VerticalLiveChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:RealTimeVerticalChartViewModel">
@@ -13,8 +14,8 @@
+ x:Name="verticalLiveChart"
+ PaletteBrushes="{AppThemeBinding Light={StaticResource PaletteBrushesLight}, Dark={StaticResource PaletteBrushesDark}}" VerticalOptions="Fill">
@@ -42,8 +43,8 @@
+ EnableAntiAliasing="True"
+ XBindingPath="Value" YBindingPath="Size"/>
diff --git a/maui/samples/Gallery/Samples/CartesianChart/FastScatterChart/FastScatterChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/FastScatterChart/FastScatterChart.xaml
index f2b60578..8a86eb52 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/FastScatterChart/FastScatterChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/FastScatterChart/FastScatterChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.FastScatterChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:FastScatterSeriesViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/CandleChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/CandleChart.xaml
index 9d9c6e72..dc5dd1e8 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/CandleChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/CandleChart.xaml
@@ -4,16 +4,21 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.CandleChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:FinancialViewModel">
-
-
+
+
-
-
+
+
-
-
+
+
+
+
+
+
@@ -38,7 +43,6 @@
Low="Low"
Close="Size"
EnableTooltip="True"
- EnableSolidCandle="{Binding Source={x:Reference checkBox},Path=IsChecked}"
EnableAnimation="True">
@@ -46,7 +50,7 @@
-
+
-
+
-
-
+
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/CandleChart.xaml.cs b/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/CandleChart.xaml.cs
index 347a385b..359dc62b 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/CandleChart.xaml.cs
+++ b/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/CandleChart.xaml.cs
@@ -50,5 +50,14 @@ public override void OnAppearing()
YAxis.Title = new ChartAxisTitle() { Text = "Index Price" };
}
}
+
+ private void checkBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ var checkBox = (CheckBox)sender;
+ if (ViewModel != null)
+ {
+ Candle.EnableSolidCandle = checkBox.IsChecked;
+ }
+ }
}
}
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/OHLC.xaml b/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/OHLC.xaml
index 976ba194..9855e02b 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/OHLC.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/FinancialChart/OHLC.xaml
@@ -4,17 +4,21 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.OHLC"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:FinancialViewModel">
-
-
+
+
-
-
-
+
+
+
+
+
+
-
-
+
+
@@ -44,7 +48,7 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/HistogramChart/HistogramChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/HistogramChart/HistogramChart.xaml
index e19c14fd..4f486608 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/HistogramChart/HistogramChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/HistogramChart/HistogramChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.HistogramChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:HistogramSeriesViewModel">
@@ -12,30 +13,33 @@
-
-
+
+
-
-
-
+
+
+
+
+
+
-
+
-
+
-
-
+
+
-
+
@@ -50,11 +54,10 @@
-
-
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/HistogramChart/HistogramChart.xaml.cs b/maui/samples/Gallery/Samples/CartesianChart/HistogramChart/HistogramChart.xaml.cs
index 0f416a1a..a38ad072 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/HistogramChart/HistogramChart.xaml.cs
+++ b/maui/samples/Gallery/Samples/CartesianChart/HistogramChart/HistogramChart.xaml.cs
@@ -21,6 +21,14 @@ public override void OnAppearing()
}
}
+ private void checkBox_CheckedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ var checkBox = (CheckBox)sender;
+ if (viewModel != null)
+ {
+ series.ShowNormalDistributionCurve = checkBox.IsChecked;
+ }
+ }
public override void OnDisappearing()
{
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Interactions/AutoScrolling.xaml b/maui/samples/Gallery/Samples/CartesianChart/Interactions/AutoScrolling.xaml
index 757ac820..544f2657 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Interactions/AutoScrolling.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Interactions/AutoScrolling.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.AutoScrolling"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:AutoScrollingViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Interactions/TapToAdd.xaml b/maui/samples/Gallery/Samples/CartesianChart/Interactions/TapToAdd.xaml
index 7f56ff7e..1d123ac4 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Interactions/TapToAdd.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Interactions/TapToAdd.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.TapToAdd"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:TapToAddViewModel">
@@ -25,7 +26,7 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Legend/CartesianLegend.xaml b/maui/samples/Gallery/Samples/CartesianChart/Legend/CartesianLegend.xaml
index 76c9ecc7..cfea915e 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Legend/CartesianLegend.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Legend/CartesianLegend.xaml
@@ -3,11 +3,13 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.CartesianLegend"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:converter="clr-namespace:Syncfusion.Maui.ControlsGallery.Converters"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:converter="clr-namespace:Syncfusion.Maui.ControlsGallery.Converters"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ xmlns:core="clr-namespace:Syncfusion.Maui.Toolkit;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:LegendViewModel">
-
+
@@ -21,21 +23,21 @@
-
+
+
-
+
-
+ HorizontalTextAlignment="Start"/>
@@ -43,10 +45,10 @@
-
+
-
-
+
+
@@ -55,10 +57,10 @@
-
+
-
-
+
+
@@ -67,10 +69,10 @@
-
+
-
-
+
+
@@ -79,10 +81,10 @@
-
+
-
-
+
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/LineChart/DashedLineChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/LineChart/DashedLineChart.xaml
index 71481f32..333294eb 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/LineChart/DashedLineChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/LineChart/DashedLineChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.DashedLineChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:LineSeriesViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/LineChart/LineChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/LineChart/LineChart.xaml
index 04f434fa..9eaedb78 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/LineChart/LineChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/LineChart/LineChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.LineChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:LineSeriesViewModel">
@@ -13,7 +14,7 @@
diff --git a/maui/samples/Gallery/Samples/CartesianChart/LineChart/Line_DynamicUpdate.xaml b/maui/samples/Gallery/Samples/CartesianChart/LineChart/Line_DynamicUpdate.xaml
index d4b5e33e..34492941 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/LineChart/Line_DynamicUpdate.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/LineChart/Line_DynamicUpdate.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.Line_DynamicUpdate"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
@@ -12,7 +13,7 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/NeumorphismUI/NeumorphismColumnChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/NeumorphismUI/NeumorphismColumnChart.xaml
index 7a75a8c6..f5545da4 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/NeumorphismUI/NeumorphismColumnChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/NeumorphismUI/NeumorphismColumnChart.xaml
@@ -6,7 +6,8 @@
xmlns:control="clr-namespace:Syncfusion.Maui.ControlsGallery.CustomView;assembly=Syncfusion.Maui.ControlsGallery"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery" Margin="-10" BackgroundColor="#F5F5F5"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:NeumorphismViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/HorizontalPlotBand.xaml b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/HorizontalPlotBand.xaml
index 20d597c1..9bececda 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/HorizontalPlotBand.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/HorizontalPlotBand.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.HorizontalPlotBand"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:PlotBandViewModel">
@@ -12,14 +13,15 @@
-
+
+
-
-
-
+
+
+
@@ -30,7 +32,7 @@
-
+
@@ -72,7 +74,7 @@
-
diff --git a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/HorizontalPlotBandWindows.xaml b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/HorizontalPlotBandWindows.xaml
index cb63e74d..bb0dc1a4 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/HorizontalPlotBandWindows.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/HorizontalPlotBandWindows.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.HorizontalPlotBandWindows"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:PlotBandViewModel">
@@ -12,14 +13,15 @@
-
+
+
-
-
-
+
+
+
@@ -31,7 +33,7 @@
+ VerticalOptions="Center" FontSize="16" LineBreakMode="WordWrap"/>
@@ -71,7 +73,7 @@
-
diff --git a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/PlotBandRecurrence.xaml b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/PlotBandRecurrence.xaml
index f49beb8a..01ffa859 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/PlotBandRecurrence.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/PlotBandRecurrence.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.PlotBandRecurrence"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:PlotBandRecurrenceViewModel">
@@ -12,13 +13,18 @@
-
+
-
+
-
-
-
+
+
+
+
+
+
+
@@ -27,7 +33,7 @@
+ VerticalOptions="Center" FontSize="16" LineBreakMode="WordWrap" Margin="2,0,2,0"/>
@@ -39,8 +45,7 @@
AutoScrollingDelta="{OnPlatform iOS=4, Android=4}" AutoScrollingMode="Start">
-
+
@@ -50,8 +55,8 @@
-
+
@@ -85,7 +90,7 @@
-
+
-
+
@@ -101,11 +106,11 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/PlotBandRecurrence.xaml.cs b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/PlotBandRecurrence.xaml.cs
index 3bc0b0d2..8f615e37 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/PlotBandRecurrence.xaml.cs
+++ b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/PlotBandRecurrence.xaml.cs
@@ -1,16 +1,47 @@
namespace Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart
{
- public partial class PlotBandRecurrence : SampleView
- {
- public PlotBandRecurrence()
+ public partial class PlotBandRecurrence : SampleView
+ {
+ public PlotBandRecurrence()
+ {
+ InitializeComponent();
+ InitializePlatBandValues();
+
+ }
+
+ #region InitializePlatBandValues
+
+ private void InitializePlatBandValues()
{
- InitializeComponent();
+ dateTimePlotBand.IsVisible = false;
+ numericalPlotBand.IsVisible = true;
}
+
+ #endregion
public override void OnDisappearing()
+ {
+ base.OnDisappearing();
+ plotBandRecurrenceChart.Handler?.DisconnectHandler();
+ }
+
+ private void axis_CheckedChanged(object sender, CheckedChangedEventArgs e)
{
- base.OnDisappearing();
- plotBandRecurrenceChart.Handler?.DisconnectHandler();
+ var checkBox = (CheckBox)sender;
+ if (ViewModel != null)
+ {
+ var input = checkBox.StyleId;
+ switch (input)
+ {
+ case "xAxis":
+ dateTimePlotBand.IsVisible = checkBox.IsChecked;
+ break;
+ case "yAxis":
+ numericalPlotBand.IsVisible = checkBox.IsChecked;
+ break;
+
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/VerticalPlotBand.xaml b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/VerticalPlotBand.xaml
index 0d50b0c0..5554913b 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/VerticalPlotBand.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/VerticalPlotBand.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.VerticalPlotBand"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:PlotBandViewModel">
@@ -12,14 +13,15 @@
-
+
+
-
-
-
+
+
+
@@ -31,7 +33,7 @@
+ VerticalOptions="Center" FontSize="16" LineBreakMode="WordWrap"/>
diff --git a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/VerticalPlotBandWindows.xaml b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/VerticalPlotBandWindows.xaml
index 49f8faf9..33a72a2e 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/PlotBand/VerticalPlotBandWindows.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/PlotBand/VerticalPlotBandWindows.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.VerticalPlotBandWindows"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:PlotBandViewModel">
@@ -12,15 +13,16 @@
-
-
+
+
+
-
-
-
+
+
+
@@ -32,7 +34,7 @@
+ VerticalOptions="Center" FontSize="16" LineBreakMode="WordWrap"/>
diff --git a/maui/samples/Gallery/Samples/CartesianChart/RangeAreaChart/RangeAreaChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/RangeAreaChart/RangeAreaChart.xaml
index 3d2c505b..b575db2c 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/RangeAreaChart/RangeAreaChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/RangeAreaChart/RangeAreaChart.xaml
@@ -3,19 +3,26 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.RangeAreaChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:RangeAreaSeriesViewModel">
-
+
+
+
+
+
+
-
+
-
+
-
+
+
@@ -24,9 +31,9 @@
-
-
-
+
+
+
@@ -61,7 +68,7 @@
-
+
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/RangeBar/DefaultRangeBar.xaml b/maui/samples/Gallery/Samples/CartesianChart/RangeBar/DefaultRangeBar.xaml
index ffa0ed05..df4b01b0 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/RangeBar/DefaultRangeBar.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/RangeBar/DefaultRangeBar.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.DefaultRangeBar"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:RangeBarSerieViewModel">
@@ -12,19 +13,24 @@
-
+
-
+
-
+
+
+
+
+
+
-
+
-
+
-
@@ -65,7 +71,7 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/RangeBar/RangeBarAnimation.xaml b/maui/samples/Gallery/Samples/CartesianChart/RangeBar/RangeBarAnimation.xaml
index 4d0ae3c7..4546dc69 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/RangeBar/RangeBarAnimation.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/RangeBar/RangeBarAnimation.xaml
@@ -4,7 +4,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.RangeBarAnimation"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
@@ -13,15 +14,15 @@
-
+
-
+
-
+
-
@@ -36,12 +37,12 @@
-
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/RangeColumn/DefaultRangeColumn.xaml b/maui/samples/Gallery/Samples/CartesianChart/RangeColumn/DefaultRangeColumn.xaml
index 3d35011f..5cfd1654 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/RangeColumn/DefaultRangeColumn.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/RangeColumn/DefaultRangeColumn.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.DefaultRangeColumn"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:RangeColumnSerieViewModel">
@@ -12,19 +13,24 @@
-
+
-
+
-
+
-
+
-
+ FontSize="16" HorizontalOptions="Fill" VerticalOptions="Center" HorizontalTextAlignment="Center"
+ LineBreakMode="WordWrap" Padding="0,0,0,5"/>
+
-
+
+
+
+
+
@@ -64,7 +70,7 @@
-
+
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/RangeColumn/RangeColumnAnimation.xaml b/maui/samples/Gallery/Samples/CartesianChart/RangeColumn/RangeColumnAnimation.xaml
index 240cd8eb..0bc41122 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/RangeColumn/RangeColumnAnimation.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/RangeColumn/RangeColumnAnimation.xaml
@@ -4,7 +4,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.RangeColumnAnimation"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
@@ -13,15 +14,15 @@
-
+
-
+
-
+ FontSize="16" HorizontalOptions="Fill" VerticalOptions="Center" HorizontalTextAlignment="Center"
+ LineBreakMode="WordWrap" Padding="0,0,0,5"/>
+
-
@@ -37,10 +38,10 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Scatter/ScatterChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/Scatter/ScatterChart.xaml
index c88dbdc0..e68b48e9 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Scatter/ScatterChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Scatter/ScatterChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.ScatterChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:ScatterSeriesViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Scatter/Scatter_DynamicUpdate.xaml b/maui/samples/Gallery/Samples/CartesianChart/Scatter/Scatter_DynamicUpdate.xaml
index 12f4587a..ba189899 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Scatter/Scatter_DynamicUpdate.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Scatter/Scatter_DynamicUpdate.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.Scatter_DynamicUpdate"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Selection/Selection.xaml b/maui/samples/Gallery/Samples/CartesianChart/Selection/Selection.xaml
index 0cdd425c..bbf20be8 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Selection/Selection.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Selection/Selection.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.Selection"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:SelectionViewModel">
@@ -13,15 +14,16 @@
-
+
+
-
-
-
+
+
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Selection/Selection.xaml.cs b/maui/samples/Gallery/Samples/CartesianChart/Selection/Selection.xaml.cs
index 1b019f8e..0701b209 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Selection/Selection.xaml.cs
+++ b/maui/samples/Gallery/Samples/CartesianChart/Selection/Selection.xaml.cs
@@ -27,9 +27,15 @@ public class SelectionValueConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
{
- if (value != null)
+
+ if (value is ChartDataModel model && parameter != null)
{
- return ((DateTime)value).ToString("ddd-hh:mm");
+ string? param = parameter?.ToString();
+
+ if (param == "Date")
+ {
+ return model.Date.ToString("ddd-hh:mm");
+ }
}
return null;
}
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Selection/SeriesSelection.xaml b/maui/samples/Gallery/Samples/CartesianChart/Selection/SeriesSelection.xaml
index aa393edd..27102113 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Selection/SeriesSelection.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Selection/SeriesSelection.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.SeriesSelection"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:SeriesSelectionViewModel">
@@ -12,13 +13,13 @@
-
+
-
+
@@ -82,7 +83,7 @@
-
+
-
-
+
+
@@ -115,6 +122,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedArea/StackedAreaChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedArea/StackedAreaChart.xaml
index e8f9e5d2..34b27344 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedArea/StackedAreaChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedArea/StackedAreaChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StackedAreaChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedAreaViewModel">
@@ -12,17 +13,18 @@
-
+
-
+
+
-
+
-
-
+
+
@@ -30,10 +32,10 @@
-
+
-
-
+
+
@@ -41,10 +43,10 @@
-
+
-
-
+
+
@@ -52,10 +54,10 @@
-
+
-
-
+
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedArea100/StackedArea100Chart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedArea100/StackedArea100Chart.xaml
index 72453808..b739b51d 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedArea100/StackedArea100Chart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedArea100/StackedArea100Chart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StackedArea100Chart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedArea100ViewModel">
@@ -12,21 +13,26 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
-
+
-
-
+
+
@@ -34,10 +40,10 @@
-
+
-
-
+
+
@@ -45,10 +51,10 @@
-
+
-
-
+
+
@@ -56,10 +62,10 @@
-
+
-
-
+
+
@@ -122,7 +128,7 @@
EnableTooltip="True"
TooltipTemplate="{StaticResource template4}"/>
-
+
@@ -130,6 +136,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/GroupingStackedColumn.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/GroupingStackedColumn.xaml
index a4435776..038d0cfd 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/GroupingStackedColumn.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/GroupingStackedColumn.xaml
@@ -3,16 +3,17 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.GroupingStackedColumn"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedColumnViewModel">
+ FontSize="16" HorizontalOptions="Fill" VerticalOptions="Center" HorizontalTextAlignment="Center"
+ LineBreakMode="WordWrap" Padding="0,0,0,5"/>
-
+
@@ -41,7 +42,7 @@
EnableTooltip="True"
Fill="#45D669"
Spacing="0.07"
- LegendIcon="SeriesType"
+ LegendIcon="SeriesType"
GroupingLabel="GroupOne"
Label="Forecast Labor Cost"/>
@@ -53,7 +54,7 @@
Fill="#6EDE8A"
Spacing="0.07"
LegendIcon="SeriesType"
- GroupingLabel="GroupOne"
+ GroupingLabel="GroupOne"
Label="Forecast Material Cost"/>
@@ -63,9 +64,9 @@
EnableAnimation="True"
EnableTooltip="True"
Fill="#B7EFC5"
- Spacing="0.07"
+ Spacing="0.07"
LegendIcon="SeriesType"
- GroupingLabel="GroupOne"
+ GroupingLabel="GroupOne"
Label="Forecast Misc Cost"/>
@@ -75,9 +76,9 @@
EnableAnimation="True"
EnableTooltip="True"
Fill="#D65F45"
- LegendIcon="SeriesType"
+ LegendIcon="SeriesType"
Spacing="0.07"
- GroupingLabel="GroupTwo"
+ GroupingLabel="GroupTwo"
Label="Actual Labor Cost"/>
@@ -97,9 +98,9 @@
EnableAnimation="True"
EnableTooltip="True"
Fill="#EFC4B7"
- LegendIcon="SeriesType"
- Spacing="0.07"
- GroupingLabel="GroupTwo"
+ LegendIcon="SeriesType"
+ Spacing="0.07"
+ GroupingLabel="GroupTwo"
Label="Actual Misc Cost"/>
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/StackedBar.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/StackedBar.xaml
index 7452129d..848672e3 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/StackedBar.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/StackedBar.xaml
@@ -3,20 +3,26 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StackedBar"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedColumnViewModel">
-
-
+
+
-
-
+
+
-
+
+
+
+
+
+
-
+
@@ -67,7 +73,7 @@
LegendIcon="SeriesType"/>
-
+
@@ -75,6 +81,6 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/StackedColumnChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/StackedColumnChart.xaml
index ad169308..998a2e76 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/StackedColumnChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn/StackedColumnChart.xaml
@@ -4,7 +4,8 @@
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
xmlns:converter="clr-namespace:Syncfusion.Maui.ControlsGallery.Converters;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedColumnViewModel">
@@ -12,9 +13,10 @@
+
-
-
+
@@ -26,11 +28,15 @@
LineBreakMode="WordWrap" Padding="0,0,0,5"/>
-
-
+
+
-
-
+
+
+
+
+
+
@@ -76,7 +82,7 @@
TooltipTemplate="{StaticResource tooltipTemplate}"
LegendIcon="SeriesType"/>
-
+
@@ -84,7 +90,7 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/GroupingStackedColumn100.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/GroupingStackedColumn100.xaml
index 9156d67d..93849709 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/GroupingStackedColumn100.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/GroupingStackedColumn100.xaml
@@ -4,20 +4,26 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.GroupingStackedColumn100"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedColumn100ViewModel">
-
-
+
+
-
-
-
+
+
+
+
+
+
+
+
-
+
@@ -111,8 +117,8 @@
-
-
+
+
@@ -121,6 +127,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/StackedBar100Chart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/StackedBar100Chart.xaml
index 066c09fe..e50d4451 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/StackedBar100Chart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/StackedBar100Chart.xaml
@@ -4,14 +4,18 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StackedBar100Chart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedColumn100ViewModel">
-
-
+
+
-
-
-
+
+
+
+
+
+
@@ -67,8 +71,8 @@
LegendIcon="Rectangle"/>
-
-
+
+
@@ -77,6 +81,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/StackedColumn100Chart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/StackedColumn100Chart.xaml
index 1de1e950..2a91fa7d 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/StackedColumn100Chart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedColumn100/StackedColumn100Chart.xaml
@@ -4,7 +4,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StackedColumn100Chart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedColumn100ViewModel">
@@ -13,12 +14,15 @@
-
-
+
+
-
-
-
+
+
+
+
+
+
-
-
+
+
@@ -86,6 +90,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedLine/StackedLineChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedLine/StackedLineChart.xaml
index bcbeccce..823d1249 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedLine/StackedLineChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedLine/StackedLineChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StackedLineChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedLineViewModel">
@@ -14,23 +15,27 @@
-
+
-
+
-
-
-
+
+
+
+
+
+
-
+
+
-
+
-
-
+
+
@@ -38,10 +43,10 @@
-
+
-
-
+
+
@@ -49,10 +54,10 @@
-
+
-
-
+
+
@@ -60,10 +65,10 @@
-
+
-
-
+
+
@@ -131,8 +136,8 @@
-
-
+
+
@@ -141,6 +146,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StackedLine100/StackedLine100Chart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StackedLine100/StackedLine100Chart.xaml
index e3278936..b754ec0e 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StackedLine100/StackedLine100Chart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StackedLine100/StackedLine100Chart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StackedLine100Chart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StackedLine100ViewModel">
@@ -14,26 +15,30 @@
-
+
-
+
-
-
-
+
+
+
+
+
+
-
+
+
-
+
-
+
-
+
@@ -41,15 +46,15 @@
-
+
-
+
-
+
@@ -57,15 +62,15 @@
-
+
-
+
-
+
@@ -73,15 +78,15 @@
-
+
-
+
-
+
@@ -158,8 +163,8 @@
-
-
+
+
@@ -168,6 +173,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StepAreaChart/StepAreaChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StepAreaChart/StepAreaChart.xaml
index 7128cab9..7c482860 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StepAreaChart/StepAreaChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StepAreaChart/StepAreaChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StepAreaChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StepAreaSeriesViewModel">
@@ -13,33 +14,34 @@
-
+
-
+
-
+
-
+
-
+
-
+
-
+
-
-
+
+
@@ -47,10 +49,10 @@
-
+
-
-
+
+
@@ -85,6 +87,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StepAreaChart/StepAreaChart_DynamicUpdate.xaml b/maui/samples/Gallery/Samples/CartesianChart/StepAreaChart/StepAreaChart_DynamicUpdate.xaml
index 746c1960..27f9e9c9 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StepAreaChart/StepAreaChart_DynamicUpdate.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StepAreaChart/StepAreaChart_DynamicUpdate.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StepAreaChart_DynamicUpdate"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/DashedStepLineChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/DashedStepLineChart.xaml
index 2f297064..5f386969 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/DashedStepLineChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/DashedStepLineChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.DashedStepLineChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StepLineChartViewModel">
@@ -14,16 +15,21 @@
-
-
+
+
-
-
+
+
+
+
+
+
+
6
3
@@ -35,10 +41,10 @@
-
+
-
-
+
+
@@ -75,7 +81,7 @@
-
+
@@ -83,6 +89,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/StepLineChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/StepLineChart.xaml
index 9313d076..9d38f1cd 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/StepLineChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/StepLineChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StepLineChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StepLineChartViewModel">
@@ -12,11 +13,15 @@
-
-
+
+
-
-
+
+
+
+
+
+
@@ -59,7 +64,7 @@
-
+
@@ -67,7 +72,7 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/StepLine_DynamicUpdate.xaml b/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/StepLine_DynamicUpdate.xaml
index 0a905b76..92e8d738 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/StepLine_DynamicUpdate.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/StepLine_DynamicUpdate.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.StepLine_DynamicUpdate"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DynamicAnimationViewModel">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/VerticalStepLineChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/VerticalStepLineChart.xaml
index 206adbfb..c153940f 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/VerticalStepLineChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/StepLineChart/VerticalStepLineChart.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.VerticalStepLineChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:StepLineChartViewModel">
@@ -13,11 +14,15 @@
-
-
+
+
-
-
+
+
+
+
+
+
@@ -27,15 +32,16 @@
+
-
+
-
-
+
+
@@ -43,10 +49,10 @@
-
+
-
-
+
+
@@ -94,7 +100,7 @@
-
+
@@ -102,6 +108,6 @@
-
+
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Tooltip/CartesianTooltip.xaml b/maui/samples/Gallery/Samples/CartesianChart/Tooltip/CartesianTooltip.xaml
index b978d652..f8c4375d 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Tooltip/CartesianTooltip.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Tooltip/CartesianTooltip.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.CartesianTooltip"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:TooltipViewModel">
@@ -12,11 +13,15 @@
-
-
+
+
-
-
+
+
+
+
+
+
@@ -25,17 +30,17 @@
-
-
-
+
+
+
-
-
+
+
@@ -45,8 +50,8 @@
-
-
+
+
@@ -90,7 +95,7 @@
-
+
@@ -98,6 +103,6 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Tooltip/TooltipViewModel.cs b/maui/samples/Gallery/Samples/CartesianChart/Tooltip/TooltipViewModel.cs
index 7bfaa8f3..be06ecf8 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Tooltip/TooltipViewModel.cs
+++ b/maui/samples/Gallery/Samples/CartesianChart/Tooltip/TooltipViewModel.cs
@@ -23,32 +23,4 @@ public TooltipViewModel()
];
}
}
-
- public class TooltipValuesConverter : IValueConverter
- {
- public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
- {
- if (value is ChartDataModel model)
- {
- var param = parameter?.ToString();
-
- switch (param)
- {
- case "Value":
- return $": {model.Value}M";
- case "Size":
- return $": {model.Size}M";
- case "Value1":
- return model.Value1;
- }
- }
-
- return value;
- }
-
- public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
- {
- return value;
- }
- }
}
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Trackball/CartesianTrackball.xaml b/maui/samples/Gallery/Samples/CartesianChart/Trackball/CartesianTrackball.xaml
index eb9123b4..7fdd6fbe 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Trackball/CartesianTrackball.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Trackball/CartesianTrackball.xaml
@@ -4,7 +4,8 @@
xmlns:imageExtension="clr-namespace:Syncfusion.Maui.ControlsGallery.Converters;assembly=Syncfusion.Maui.ControlsGallery"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:CartesianTrackballViewModel">
@@ -13,12 +14,17 @@
-
-
+
+
+
+
+
+
+
-
-
+
+
@@ -35,7 +41,7 @@
-
+
@@ -65,7 +71,7 @@
-
@@ -105,8 +111,8 @@
-
-
+
+
@@ -114,7 +120,6 @@
+ public class TooltipValuesConverter : IValueConverter
{
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is ChartDataModel model)
+ {
+ switch (parameter?.ToString())
+ {
+ case "Name":
+ return model.Name;
+ case "Value":
+ return model.Value;
+ case "High":
+ return model.High;
+ case "Low":
+ return model.Low;
+ case "Size":
+ return model.Size;
+ case "Label":
+ return model.Label;
+ case "Data":
+ return model.Data;
+ case "Date":
+ return model.Date;
+ case "Year":
+ return model.Year;
+ case "Organic":
+ return model.Organic;
+ case "FairTrade":
+ return model.FairTrade;
+ case "VegAlternatives":
+ return model.VegAlternatives;
+ case "Others":
+ return model.Others;
+ case "Value1":
+ return model.Value1;
+ case "GrossLastYearDelta":
+ return model.GrossLastYearDelta;
+ case "Peru":
+ return model.Peru;
+ case "Ethiopia":
+ return model.Ethiopia;
+ case "Mali":
+ return model.Mali;
+ case "Canada":
+ return model.Canada;
+ case "Energy":
+ return model.Energy;
+ case "Department":
+ return model.Department;
+ case "TopMovie":
+ return model.TopMovie;
+ case "TotalGrossInBillion":
+ return model.TotalGrossInBillion;
+
+ }
+ }
+ else if (value is LineSeries series)
+ {
+ switch (parameter?.ToString())
+ {
+ case "Label":
+ return series.Label;
+ case "MarkerSettings.Stroke":
+ return series.MarkerSettings.Stroke;
+
+ }
+ var line = value as LineSeriesExt;
+ switch (parameter?.ToString())
+ {
+ case "PathData":
+ return line?.PathData;
+ }
+ }
+
+ return value;
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return value;
+ }
+ }
+
+ public class ChartColorModel : ObservableCollection
+ {
}
}
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Waterfall/WaterFallChart.xaml b/maui/samples/Gallery/Samples/CartesianChart/Waterfall/WaterFallChart.xaml
index cc41abcd..1d573386 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Waterfall/WaterFallChart.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Waterfall/WaterFallChart.xaml
@@ -3,13 +3,14 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.WaterFallChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:WaterFallSeriesViewModel">
+ FontSize="16" HorizontalOptions="Fill" VerticalOptions="Center" HorizontalTextAlignment="Center"
+ LineBreakMode="WordWrap" Padding="0,0,0,5"/>
+ SummaryBindingPath="IsSummary" ShowDataLabels="{OnPlatform Android=False,iOS=False,Default=True}" EnableTooltip="{OnPlatform Android=True,iOS=True,Default=False}">
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Waterfall/WaterFallChartIsTransposed.xaml b/maui/samples/Gallery/Samples/CartesianChart/Waterfall/WaterFallChartIsTransposed.xaml
index 214757d4..1f162ea6 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Waterfall/WaterFallChartIsTransposed.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Waterfall/WaterFallChartIsTransposed.xaml
@@ -3,14 +3,15 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.WaterFallChartIsTransposed"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:WaterFallSeriesViewModel">
-
+
@@ -18,7 +19,7 @@
-
+
@@ -29,7 +30,7 @@
+ ShowDataLabels="{OnPlatform Android=False,iOS=False,Default=True}" EnableTooltip="{OnPlatform Android=True,iOS=True,Default=False}">
@@ -51,7 +52,7 @@
-
+
@@ -62,7 +63,7 @@
+ ShowDataLabels="{OnPlatform Android=False,iOS=False,Default=True}" EnableTooltip="{OnPlatform Android=True,iOS=True,Default=False}">
@@ -77,7 +78,7 @@
-
+
@@ -99,8 +100,8 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Zoom/ChartZooming.xaml b/maui/samples/Gallery/Samples/CartesianChart/Zoom/ChartZooming.xaml
index a80a779a..93e6a337 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Zoom/ChartZooming.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Zoom/ChartZooming.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.ChartZooming"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:ZoomViewModel">
@@ -55,8 +56,7 @@
-
+
@@ -72,7 +72,6 @@
-
+
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Zoom/ChartZooming.xaml.cs b/maui/samples/Gallery/Samples/CartesianChart/Zoom/ChartZooming.xaml.cs
index 793d8912..055f09bc 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Zoom/ChartZooming.xaml.cs
+++ b/maui/samples/Gallery/Samples/CartesianChart/Zoom/ChartZooming.xaml.cs
@@ -33,5 +33,15 @@ private void ZoomModeChanged(object sender, EventArgs e)
zoomingBehavior.ZoomMode = ZoomMode.XY;
}
}
+
+ private void checkbox_CheckedChanged(object sender, CheckedChangedEventArgs e)
+ {
+ var checkBox = (CheckBox)sender;
+ if (ViewModel != null && (DeviceInfo.Platform == DevicePlatform.iOS || DeviceInfo.Platform == DevicePlatform.Android))
+ {
+ zoomingBehavior.EnableDirectionalZooming = checkBox.IsChecked;
+ }
+ }
+
}
}
diff --git a/maui/samples/Gallery/Samples/CartesianChart/Zoom/SelectionZooming.xaml b/maui/samples/Gallery/Samples/CartesianChart/Zoom/SelectionZooming.xaml
index ad47653f..25007303 100644
--- a/maui/samples/Gallery/Samples/CartesianChart/Zoom/SelectionZooming.xaml
+++ b/maui/samples/Gallery/Samples/CartesianChart/Zoom/SelectionZooming.xaml
@@ -3,7 +3,8 @@
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.SelectionZooming"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:ScatterSeriesViewModel">
diff --git a/maui/samples/Gallery/Samples/CircularChart/Doughnut/CenterElevation.xaml b/maui/samples/Gallery/Samples/CircularChart/Doughnut/CenterElevation.xaml
index 9ccaf410..ef8c47d3 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Doughnut/CenterElevation.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/Doughnut/CenterElevation.xaml
@@ -3,8 +3,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart.CenterElevation"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
- xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DoughnutSeriesViewModel">
@@ -25,7 +26,7 @@
-
+
@@ -34,7 +35,7 @@
-
@@ -42,14 +43,18 @@
-
-
+
+
+
+
+
-
-
+
diff --git a/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChart.xaml b/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChart.xaml
index 566a4876..bd2f63fb 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChart.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChart.xaml
@@ -2,8 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart.DoughnutChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
- xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DoughnutSeriesViewModel">
@@ -30,10 +31,14 @@
XBindingPath="Name" YBindingPath="Value" EnableAnimation="False" StrokeWidth="1"
Stroke="{AppThemeBinding Default={StaticResource ContentBackground}}" LegendIcon="SeriesType">
-
-
-
-
+
+
+
+
+
+
+
+
diff --git a/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChartWithCurvedEdge.xaml b/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChartWithCurvedEdge.xaml
new file mode 100644
index 00000000..9fc7851e
--- /dev/null
+++ b/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChartWithCurvedEdge.xaml
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChartWithCurvedEdge.xaml.cs b/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChartWithCurvedEdge.xaml.cs
new file mode 100644
index 00000000..c2cf32a9
--- /dev/null
+++ b/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutChartWithCurvedEdge.xaml.cs
@@ -0,0 +1,32 @@
+namespace Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart
+{
+ public partial class DoughnutChartWithCurvedEdge : SampleView
+ {
+ public DoughnutChartWithCurvedEdge()
+ {
+ InitializeComponent();
+ pathData.Data = doughnutViewModel.TruckPathData;
+ }
+
+ public override void OnAppearing()
+ {
+ base.OnAppearing();
+ if (IsCardView)
+ {
+ series.ShowDataLabels = false;
+
+#if IOS
+ chart.WidthRequest = 350;
+ chart.HeightRequest = 400;
+ chart.VerticalOptions = LayoutOptions.Start;
+#endif
+ }
+ }
+
+ public override void OnDisappearing()
+ {
+ base.OnDisappearing();
+ chart.Handler?.DisconnectHandler();
+ }
+ }
+}
\ No newline at end of file
diff --git a/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutSeriesViewModel.cs b/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutSeriesViewModel.cs
index 0c562aa6..123a080e 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutSeriesViewModel.cs
+++ b/maui/samples/Gallery/Samples/CircularChart/Doughnut/DoughnutSeriesViewModel.cs
@@ -1,4 +1,6 @@
using System.Collections.ObjectModel;
+using Microsoft.Maui.Controls.Shapes;
+using Syncfusion.Maui.Toolkit.Charts;
namespace Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart
{
@@ -8,12 +10,15 @@ public partial class DoughnutSeriesViewModel : BaseViewModel
public ObservableCollection SemiCircularData { get; set; }
public ObservableCollection CenterElevationData { get; set; }
public ObservableCollection GroupToData { get; set; }
+ public ObservableCollection CapStyleData { get; set; }
int _selectedIndex = 1;
string _name = "";
int _value1;
int _total = 357580;
+ public Geometry? TruckPathData { get; set; } = (Geometry?) (new PathGeometryConverter().ConvertFromInvariantString("M46.5 22.4813V22.4437C46.503 22.3992 46.4966 22.3545 46.4812 22.3125V22.2562C46.4812 22.2187 46.4625 22.1812 46.4625 22.1437V22.1063L46.425 21.9562H46.4062L43.7812 15.4313C43.5721 14.8602 43.1912 14.3679 42.691 14.0221C42.1907 13.6763 41.5956 13.4939 40.9875 13.5H34.5V12C34.5 11.6022 34.342 11.2206 34.0607 10.9393C33.7794 10.658 33.3978 10.5 33 10.5H4.5C3.70435 10.5 2.94129 10.8161 2.37868 11.3787C1.81607 11.9413 1.5 12.7044 1.5 13.5V34.5C1.5 35.2956 1.81607 36.0587 2.37868 36.6213C2.94129 37.1839 3.70435 37.5 4.5 37.5H6.9375C7.26795 38.7906 8.01855 39.9346 9.07096 40.7515C10.1234 41.5684 11.4177 42.0118 12.75 42.0118C14.0823 42.0118 15.3766 41.5684 16.429 40.7515C17.4814 39.9346 18.232 38.7906 18.5625 37.5H29.4375C29.7679 38.7906 30.5185 39.9346 31.571 40.7515C32.6234 41.5684 33.9177 42.0118 35.25 42.0118C36.5823 42.0118 37.8766 41.5684 38.929 40.7515C39.9814 39.9346 40.732 38.7906 41.0625 37.5H43.5C44.2956 37.5 45.0587 37.1839 45.6213 36.6213C46.1839 36.0587 46.5 35.2956 46.5 34.5V22.5V22.4813ZM34.5 16.5H40.9875L42.7875 21H34.5V16.5ZM4.5 13.5H31.5V25.5H4.5V13.5ZM12.75 39C12.1567 39 11.5766 38.8241 11.0833 38.4944C10.5899 38.1648 10.2054 37.6962 9.97836 37.1481C9.7513 36.5999 9.69189 35.9967 9.80764 35.4147C9.9234 34.8328 10.2091 34.2982 10.6287 33.8787C11.0482 33.4591 11.5828 33.1734 12.1647 33.0576C12.7467 32.9419 13.3499 33.0013 13.898 33.2284C14.4462 33.4554 14.9148 33.8399 15.2444 34.3333C15.5741 34.8266 15.75 35.4067 15.75 36C15.75 36.7956 15.4339 37.5587 14.8713 38.1213C14.3087 38.6839 13.5456 39 12.75 39ZM35.25 39C34.6567 39 34.0766 38.8241 33.5833 38.4944C33.0899 38.1648 32.7054 37.6962 32.4784 37.1481C32.2513 36.5999 32.1919 35.9967 32.3076 35.4147C32.4234 34.8328 32.7091 34.2982 33.1287 33.8787C33.5482 33.4591 34.0828 33.1734 34.6647 33.0576C35.2467 32.9419 35.8499 33.0013 36.398 33.2284C36.9462 33.4554 37.4148 33.8399 37.7444 34.3333C38.074 34.8266 38.25 35.4067 38.25 36C38.25 36.7956 37.9339 37.5587 37.3713 38.1213C36.8087 38.6839 36.0456 39 35.25 39Z"));
+
public int SelectedIndex
{
get { return _selectedIndex; }
@@ -79,7 +84,7 @@ public DoughnutSeriesViewModel()
new ChartDataModel("Facilities", 10),
new ChartDataModel("Taxes", 6),
new ChartDataModel("Insurance", 18)
- ];
+ ];
SemiCircularData =
[
@@ -119,6 +124,13 @@ public DoughnutSeriesViewModel()
new ChartDataModel("Mexico",1.00,0.01),
new ChartDataModel("Luxembourg",0.90,0.01),
];
+
+ CapStyleData=
+ [
+ new ChartDataModel("Delivered", 56),
+ new ChartDataModel("Cancelled", 27),
+ new ChartDataModel("Scheduled", 17),
+ ];
}
}
}
diff --git a/maui/samples/Gallery/Samples/CircularChart/Doughnut/GroupToDoughnutChart.xaml b/maui/samples/Gallery/Samples/CircularChart/Doughnut/GroupToDoughnutChart.xaml
index 750eff1a..819bc53c 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Doughnut/GroupToDoughnutChart.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/Doughnut/GroupToDoughnutChart.xaml
@@ -2,8 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart.GroupToDoughnutChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
- xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
@@ -14,29 +14,33 @@
-
-
+
+
-
-
+
+
+
+
+
+
-
+
-
+
+ TextColor="{AppThemeBinding Default={StaticResource ContentBackground}}" Grid.Column="0"
+ HorizontalOptions="Start" HorizontalTextAlignment="Start"
+ FontSize="Caption"
+ VerticalOptions="Center"/>
+ FontSize="Caption" Grid.Column="2"
+ TextColor="{AppThemeBinding Default={StaticResource ContentBackground}}" FontAttributes="Bold"
+ HorizontalOptions="End" HorizontalTextAlignment="End"
+ VerticalOptions="Center"/>
@@ -44,13 +48,13 @@
-
-
+
+
-
+
@@ -72,7 +76,7 @@
-
+
@@ -80,7 +84,7 @@
-
+
@@ -95,10 +99,10 @@
-
-
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:DoughnutSeriesViewModel">
diff --git a/maui/samples/Gallery/Samples/CircularChart/Interaction/Selection.xaml b/maui/samples/Gallery/Samples/CircularChart/Interaction/Selection.xaml
index 29965e74..b20a4c4c 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Interaction/Selection.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/Interaction/Selection.xaml
@@ -1,9 +1,10 @@
+ xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:SelectionViewModel">
diff --git a/maui/samples/Gallery/Samples/CircularChart/Interaction/Tooltip.xaml b/maui/samples/Gallery/Samples/CircularChart/Interaction/Tooltip.xaml
index 18317211..3b95272f 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Interaction/Tooltip.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/Interaction/Tooltip.xaml
@@ -1,9 +1,10 @@
+ xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:PieSeriesViewModel">
@@ -44,8 +45,8 @@
+ x:Name="pieSeries" StrokeWidth="2" Stroke="{AppThemeBinding Default={StaticResource ContentBackground}}" EnableAnimation="{Binding EnableAnimation}" ItemsSource="{Binding PieSeriesData}"
+ XBindingPath="Name" YBindingPath="Value" LegendIcon="SeriesType" PaletteBrushes="{AppThemeBinding Light={StaticResource PaletteBrushesLight1}, Dark={StaticResource PaletteBrushesDark1}}">
diff --git a/maui/samples/Gallery/Samples/CircularChart/Interaction/Tooltip.xaml.cs b/maui/samples/Gallery/Samples/CircularChart/Interaction/Tooltip.xaml.cs
index 79459a2b..d0ae98dc 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Interaction/Tooltip.xaml.cs
+++ b/maui/samples/Gallery/Samples/CircularChart/Interaction/Tooltip.xaml.cs
@@ -15,27 +15,4 @@ public override void OnDisappearing()
Chart.Handler?.DisconnectHandler();
}
}
-
- public class TooltipValueConverter : IValueConverter
- {
- public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
- {
- if (value is ChartDataModel model)
- {
- switch (parameter?.ToString())
- {
- case "Name":
- return model.Name;
- case "Value":
- return model.Value;
- }
- }
-
- return value;
- }
- public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
- {
- return value;
- }
- }
}
diff --git a/maui/samples/Gallery/Samples/CircularChart/Pie/GroupToPieChart.xaml b/maui/samples/Gallery/Samples/CircularChart/Pie/GroupToPieChart.xaml
index 40ebab78..761c8887 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Pie/GroupToPieChart.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/Pie/GroupToPieChart.xaml
@@ -2,8 +2,8 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart.GroupToPieChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
- xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
@@ -14,9 +14,9 @@
-
-
-
+
+
+
@@ -42,11 +42,15 @@
-
-
+
+
-
-
+
+
+
+
+
+
@@ -57,9 +61,9 @@
-
+
@@ -71,7 +75,7 @@
-
+
@@ -79,7 +83,7 @@
-
+
@@ -94,10 +98,10 @@
-
-
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:PieSeriesViewModel">
diff --git a/maui/samples/Gallery/Samples/CircularChart/Pie/SemiPieChart.xaml b/maui/samples/Gallery/Samples/CircularChart/Pie/SemiPieChart.xaml
index 8af99b0f..1d5f116d 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Pie/SemiPieChart.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/Pie/SemiPieChart.xaml
@@ -2,8 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart.SemiPieChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
- xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:PieSeriesViewModel">
diff --git a/maui/samples/Gallery/Samples/CircularChart/Pie/SmartDataLabels.xaml b/maui/samples/Gallery/Samples/CircularChart/Pie/SmartDataLabels.xaml
index 78ad1a49..4407e6ac 100644
--- a/maui/samples/Gallery/Samples/CircularChart/Pie/SmartDataLabels.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/Pie/SmartDataLabels.xaml
@@ -2,8 +2,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart.SmartDataLabels"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
- xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:PieSeriesViewModel">
@@ -14,16 +15,20 @@
-
-
+
+
-
-
+
+
+
+
+
+
-
+
@@ -38,7 +43,7 @@
-
-
+
@@ -62,7 +67,7 @@
-
+
@@ -76,7 +81,7 @@
diff --git a/maui/samples/Gallery/Samples/CircularChart/RadialBar/CustomizedRadialBarChart.xaml b/maui/samples/Gallery/Samples/CircularChart/RadialBar/CustomizedRadialBarChart.xaml
index 55f4143a..24189493 100644
--- a/maui/samples/Gallery/Samples/CircularChart/RadialBar/CustomizedRadialBarChart.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/RadialBar/CustomizedRadialBarChart.xaml
@@ -3,9 +3,11 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart.CustomizedRadialBarChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
- xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
xmlns:converter="clr-namespace:Syncfusion.Maui.ControlsGallery.Converters;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ xmlns:core="clr-namespace:Syncfusion.Maui.Toolkit;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:RadialBarSeriesViewModel">
@@ -18,6 +20,8 @@
+
+
@@ -34,38 +38,39 @@
YBindingPath="Value"
GapRatio="{OnPlatform WinUI='0.4',Android='0.4',iOS='0.5',MacCatalyst='0.4'}"
CapStyle="BothCurve" XBindingPath="Name"
- Radius="1" MaximumValue="100">
+ Radius="1" MaximumValue="100">
+ WidthRequest="{Binding CenterHoleSize}"
+ x:DataType="chart:RadialBarSeries">
+ Source={x:Reference series}}"/>
-
+
-
-
-
+
-
+
@@ -78,9 +83,9 @@
+ VerticalOptions="Fill" HorizontalOptions="Fill">
-
+ WidthRequest="{Binding CenterHoleSize}" x:DataType="chart:RadialBarSeries">
+ Converter={StaticResource innerRadiusConverter},Source={x:Reference series1}}"/>
-
+
-
@@ -111,15 +116,15 @@
-
+ Text="{Binding Item, Converter={StaticResource valueConverter},ConverterParameter='Value', StringFormat='\{0,0\}%'}">
-
diff --git a/maui/samples/Gallery/Samples/CircularChart/RadialBar/RadialBarChart.xaml b/maui/samples/Gallery/Samples/CircularChart/RadialBar/RadialBarChart.xaml
index fba1c0ca..bc9abb02 100644
--- a/maui/samples/Gallery/Samples/CircularChart/RadialBar/RadialBarChart.xaml
+++ b/maui/samples/Gallery/Samples/CircularChart/RadialBar/RadialBarChart.xaml
@@ -3,8 +3,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart.RadialBarChart"
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart"
- xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
- xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
+ xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
+ xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit"
+ x:DataType="local:RadialBarSeriesViewModel">
@@ -16,18 +17,18 @@
-
+
-
-
-
-
-
+
+
+
+
+
@@ -48,14 +49,10 @@
@@ -78,12 +75,12 @@
-
-
@@ -93,10 +90,10 @@
-
-
@@ -124,9 +121,9 @@
+ VerticalOptions="Center" Grid.Column="0"/>
@@ -136,9 +133,9 @@
+ VerticalOptions="Center" />
@@ -148,10 +145,10 @@
+ VerticalOptions="Center" />
+ VerticalOptions="Center" Margin="0,5,2,0" ValueChanged="slider_ValueChanged"
+ Minimum="90" Maximum="270">
diff --git a/maui/samples/Gallery/Samples/CircularChart/RadialBar/RadialBarChart.xaml.cs b/maui/samples/Gallery/Samples/CircularChart/RadialBar/RadialBarChart.xaml.cs
index 94f064c1..36f2b37c 100644
--- a/maui/samples/Gallery/Samples/CircularChart/RadialBar/RadialBarChart.xaml.cs
+++ b/maui/samples/Gallery/Samples/CircularChart/RadialBar/RadialBarChart.xaml.cs
@@ -111,6 +111,47 @@ private void trackFill_SelectedIndexChanged(object sender, EventArgs e)
}
}
}
+
+ private void slider_ValueChanged(object sender, ValueChangedEventArgs e)
+ {
+ var slider = (Slider)sender;
+ if (viewModel != null)
+ {
+ var value = slider.StyleId;
+ switch (value)
+ {
+ case "endAngle":
+ radialBarSeries.EndAngle = slider.Value;
+ break;
+ case "startAngle":
+ radialBarSeries.StartAngle = slider.Value;
+ break;
+ case "trackStrokeWidth":
+ radialBarSeries.TrackStrokeWidth = slider.Value;
+ break;
+ }
+ }
+ }
+
+ private void capStyle_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ var value = capStyle.SelectedIndex;
+ switch (value)
+ {
+ case 0:
+ radialBarSeries.CapStyle = Syncfusion.Maui.Toolkit.Charts.CapStyle.BothFlat;
+ break;
+ case 1:
+ radialBarSeries.CapStyle = Syncfusion.Maui.Toolkit.Charts.CapStyle.BothCurve;
+ break;
+ case 2:
+ radialBarSeries.CapStyle = Syncfusion.Maui.Toolkit.Charts.CapStyle.StartCurve;
+ break;
+ case 3:
+ radialBarSeries.CapStyle = Syncfusion.Maui.Toolkit.Charts.CapStyle.EndCurve;
+ break;
+ }
+ }
}
}
diff --git a/maui/samples/Gallery/Samples/CircularChart/ViewModel/BaseViewModel.cs b/maui/samples/Gallery/Samples/CircularChart/ViewModel/BaseViewModel.cs
index 5b1e29d6..cc4940b8 100644
--- a/maui/samples/Gallery/Samples/CircularChart/ViewModel/BaseViewModel.cs
+++ b/maui/samples/Gallery/Samples/CircularChart/ViewModel/BaseViewModel.cs
@@ -1,8 +1,9 @@
-using Syncfusion.Maui.Toolkit.Charts;
-using System.Collections.ObjectModel;
+using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Globalization;
using System.Windows.Input;
+using Syncfusion.Maui.Toolkit;
+using Syncfusion.Maui.Toolkit.Charts;
namespace Syncfusion.Maui.ControlsGallery.CircularChart.SfCircularChart
{
@@ -114,7 +115,6 @@ public BaseViewModel()
}
}
-
public class CornerRadiusConverter : IValueConverter
{
public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
@@ -137,4 +137,49 @@ public partial class ChartColorModel : ObservableCollection
{
}
+
+ public class TooltipValueConverter : IValueConverter
+ {
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is ChartDataModel model)
+ {
+ switch (parameter?.ToString())
+ {
+ case "Name":
+ return model.Name;
+ case "Value":
+ return model.Value;
+ case "Percentage":
+ return model.Percentage;
+ }
+ }
+
+ return value;
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return value;
+ }
+ }
+
+ public class ImageValueConverter : IValueConverter
+ {
+ public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ if (value is LegendItem legendItem)
+ {
+ var image = (legendItem.Item as ChartDataModel)?.Image;
+ return image;
+ }
+
+ return value;
+ }
+
+ public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
+ {
+ return value;
+ }
+ }
}
diff --git a/maui/samples/Gallery/Samples/DatePicker/GettingStarted/Behavior/GettingStartedBehavior.cs b/maui/samples/Gallery/Samples/DatePicker/GettingStarted/Behavior/GettingStartedBehavior.cs
index 9cb19afe..a21120dd 100644
--- a/maui/samples/Gallery/Samples/DatePicker/GettingStarted/Behavior/GettingStartedBehavior.cs
+++ b/maui/samples/Gallery/Samples/DatePicker/GettingStarted/Behavior/GettingStartedBehavior.cs
@@ -13,7 +13,7 @@ public class GettingStartedBehavior : Behavior
///
/// The show header switch.
///
- Switch? _showHeaderSwitch, _showColumnHeaderSwitch, _showFooterSwitch, _clearSelectionSwitch;
+ Switch? _showHeaderSwitch, _showColumnHeaderSwitch, _showFooterSwitch, _clearSelectionSwitch, _showEnableLoopingSwitch;
///
/// The date format combo box.
@@ -90,6 +90,7 @@ protected override void OnAttachedTo(SampleView sampleView)
_showColumnHeaderSwitch = sampleView.Content.FindByName("showColumnHeaderSwitch");
_showFooterSwitch = sampleView.Content.FindByName("showFooterSwitch");
_clearSelectionSwitch = sampleView.Content.FindByName("clearSelectionSwitch");
+ _showEnableLoopingSwitch = sampleView.Content.FindByName("enableLoopingSwitch");
_datePicker.SelectedTextStyle.TextColor = _isLightTheme ? Color.FromArgb("#FFFFFF") : Color.FromArgb("#381E72");
_formats = new ObservableCollection
internal interface ICalendarView
{
- ///
- /// Method to update when visible dates change.
- ///
- /// The visible dates collection.
- /// Checks whether the view is current view or not.
- void UpdateVisibleDatesChange(List visibleDates, bool isCurrentView);
+ ///
+ /// Method to update when visible dates change.
+ ///
+ /// The visible dates collection.
+ /// Checks whether the view is current view or not.
+ /// Gets the month view instance for current canvas.
+ void UpdateVisibleDatesChange(List visibleDates, bool isCurrentView, CustomSnapLayout customSnapLayout);
- ///
- /// Method to update selected date on visible date change.
- ///
- void UpdateSelectionValue();
+ ///
+ /// Method to update selected date on visible date change.
+ ///
+ void UpdateSelectionValue();
///
/// Method to update the selected dates.
diff --git a/maui/src/Calendar/LoopingPannel/CustomSnapManager.cs b/maui/src/Calendar/LoopingPannel/CustomSnapManager.cs
index 8bb2b7c5..e1bc8b75 100644
--- a/maui/src/Calendar/LoopingPannel/CustomSnapManager.cs
+++ b/maui/src/Calendar/LoopingPannel/CustomSnapManager.cs
@@ -750,7 +750,7 @@ void CalculateVisibleDateOnView()
for (int i = 0; i < Children.Count; i++)
{
List visibleDates = GetVisibleDatesForView(i);
- (Children[i] as ICalendarView)?.UpdateVisibleDatesChange(visibleDates, i == _currentChildIndex);
+ (Children[i] as ICalendarView)?.UpdateVisibleDatesChange(visibleDates, i == _currentChildIndex, this);
//// Condition to update the disabled date only for the current view.
if (_visibleDates == visibleDates)
{
@@ -996,7 +996,7 @@ void UpdateVisibleDatesOnChange(int currentIndex)
return;
}
- (Children[currentIndex] as ICalendarView)?.UpdateVisibleDatesChange(GetVisibleDatesForView(currentIndex), currentIndex == _currentChildIndex);
+ (Children[currentIndex] as ICalendarView)?.UpdateVisibleDatesChange(GetVisibleDatesForView(currentIndex), currentIndex == _currentChildIndex, this);
for (int i = 0; i < ChildCount; i++)
{
(Children[i] as YearView)?.InvalidateSemanticsNode(i == _currentChildIndex);
@@ -1358,6 +1358,11 @@ DateTime GetNextViewStartDate(DateTime date)
/// The visible dates collection.
void UpdateSpecialAndDisableDates(List visibleDates)
{
+ if (visibleDates.Count == 0)
+ {
+ return;
+ }
+
List? disabledDates = UpdateDisabledDates(visibleDates);
List? specialDates = UpdateSpecialDates(visibleDates);
//// Disable dates null means the disabled dates are already updated for current visible dates.
diff --git a/maui/src/Calendar/Model/SfCalendar.cs b/maui/src/Calendar/Model/SfCalendar.cs
index 76c93560..b63c6117 100644
--- a/maui/src/Calendar/Model/SfCalendar.cs
+++ b/maui/src/Calendar/Model/SfCalendar.cs
@@ -1,6 +1,7 @@
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Windows.Input;
+using Microsoft.Maui.Controls.Shapes;
namespace Syncfusion.Maui.Toolkit.Calendar
{
@@ -563,13 +564,26 @@ public partial class SfCalendar
defaultValueCreator: bindable => null,
propertyChanged: OnMonthViewHeaderTemplateChanged);
- ///
- /// Identifies the dependency property.
- ///
- ///
- /// The identifier for dependency property.
- ///
- public static readonly BindableProperty IsOpenProperty =
+ ///
+ /// Identifies the dependency propert.
+ ///
+ ///
+ /// The identifier for dependency property.
+ ///
+ public static readonly BindableProperty SelectionCellTemplateProperty =
+ BindableProperty.Create(
+ nameof(SelectionCellTemplate),
+ typeof(DataTemplate),
+ typeof(SfCalendar),
+ null);
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ ///
+ /// The identifier for dependency property.
+ ///
+ public static readonly BindableProperty IsOpenProperty =
BindableProperty.Create(
nameof(IsOpen),
typeof(bool),
@@ -2174,56 +2188,156 @@ public DataTemplate MonthViewHeaderTemplate
{
get { return (DataTemplate)GetValue(MonthViewHeaderTemplateProperty); }
set { SetValue(MonthViewHeaderTemplateProperty, value); }
- }
-
- ///
- /// Gets or sets a value indicating whether the calendar popup is open or not.
- ///
- /// The default value of is "False".
- ///
- /// It will be applicable to set the or .
- ///
- ///
- /// The following code example demonstrates, how to set IsOpen property for the control.
- /// # [C#](#tab/tabid-2)
- ///
- ///
- /// # [XAML](#tab/tabid-1)
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- ///
- /// ]]>
- ///
- ///
- public bool IsOpen
+ }
+
+ ///
+ /// Gets or sets the Selection cell template
+ ///
+ ///
+ /// The following code used to configure the month view header using the .
+ /// # [XAML](#tab/tabid-11)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ]]>
+ ///
+ /// The following code used to configure the month view header using the .
+ /// # [DataTemplateSelector](#tab/tabid-12)
+ ///
+ ///
+ /// {
+ /// // Create a Grid with two columns
+ /// Grid grid = new Grid
+ /// {
+ /// RowDefinitions =
+ /// {
+ /// new RowDefinition { Height = GridLength.Star },
+ /// new RowDefinition { Height = GridLength.Star }
+ /// },
+ /// Padding = new Thickness(1),
+ /// VerticalOptions = LayoutOptions.Center,
+ /// HeightRequest = 40
+ /// };
+ /// // Create a Border with rounded corners
+ /// Border border = new Border
+ /// {
+ /// BackgroundColor = Color.FromArgb("#4285F4"),
+ /// Stroke = Colors.Transparent,
+ /// StrokeShape = new RoundRectangle
+ /// {
+ /// CornerRadius = new CornerRadius(10)
+ /// }
+ /// };
+ /// // Create Label and bind to Date.Year
+ /// Label label = new Label
+ /// {
+ /// TextColor = Colors.White,
+ /// HorizontalTextAlignment = TextAlignment.Center,
+ /// VerticalTextAlignment = TextAlignment.Center,
+ /// HorizontalOptions = LayoutOptions.Center,
+ /// VerticalOptions = LayoutOptions.Center
+ /// };
+ /// label.SetBinding(Label.TextProperty, "Date.Day");
+ /// // Create Image
+ /// Image image = new Image
+ /// {
+ /// Source = "tick_image2.png",
+ /// HeightRequest = 17,
+ /// WidthRequest = 17,
+ /// HorizontalOptions = LayoutOptions.Center,
+ /// VerticalOptions = LayoutOptions.Center
+ /// };
+ /// // Add label and image to inner grid
+ /// grid.Add(label, 0, 0);
+ /// grid.Add(image, 0, 1);
+ /// // Set content of border
+ /// border.Content = grid;
+ /// // Add border to outer grid
+ /// //grid.Add(border);
+ /// return border;
+ /// });
+ /// }
+ ///}
+ ///
+ /// ]]>
+ ///
+ ///
+ public DataTemplate SelectionCellTemplate
+ {
+ get { return (DataTemplate)this.GetValue(SelectionCellTemplateProperty); }
+ set { this.SetValue(SelectionCellTemplateProperty, value); }
+ }
+
+ ///
+ /// Gets or sets a value indicating whether the calendar popup is open or not.
+ ///
+ /// The default value of is "False".
+ ///
+ /// It will be applicable to set the or .
+ ///
+ ///
+ /// The following code example demonstrates, how to set IsOpen property for the control.
+ /// # [C#](#tab/tabid-2)
+ ///
+ ///
+ /// # [XAML](#tab/tabid-1)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ]]>
+ ///
+ ///
+ public bool IsOpen
{
get { return (bool)GetValue(IsOpenProperty); }
set { SetValue(IsOpenProperty, value); }
diff --git a/maui/src/Calendar/Views/CalendarView/MonthView/MonthView.cs b/maui/src/Calendar/Views/CalendarView/MonthView/MonthView.cs
index 1ec7ae74..3e45703e 100644
--- a/maui/src/Calendar/Views/CalendarView/MonthView/MonthView.cs
+++ b/maui/src/Calendar/Views/CalendarView/MonthView/MonthView.cs
@@ -6,6 +6,7 @@
using Syncfusion.Maui.Toolkit.Graphics.Internals;
using PointerEventArgs = Syncfusion.Maui.Toolkit.Internals.PointerEventArgs;
using Globalization = System.Globalization;
+using Rect = Microsoft.Maui.Graphics.Rect;
namespace Syncfusion.Maui.Toolkit.Calendar
{
@@ -128,20 +129,35 @@ internal class MonthView : SfView, ITapGestureListener, IDoubleTapGestureListene
///
DateTime? _previousSelectedRangeDate;
- #endregion
+ ///
+ /// To store the selected date data template view
+ ///
+ View? _selectionCellTemplateView;
- #region Constructor
+ ///
+ /// To store the selected cell template previous view
+ ///
+ View? _previousSelectionCellTemplateView;
- ///
- /// Initializes a new instance of the class.
- ///
- /// The calendar view.
- /// The visible dates for the view.
- /// The selected date for the view.
- /// The disabled dates for the view.
- /// The special dates for the view.
- /// Defines whether the view is current view or not.
- internal MonthView(ICalendar calendarView, List visibleDates, DateTime? selectedDate, List disabledDates, List specialDatesDetails, bool isCurrentView)
+ ///
+ /// To store the previous month cell template view
+ ///
+ View? _previousMonthCellTemplateView;
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The calendar view.
+ /// The visible dates for the view.
+ /// The selected date for the view.
+ /// The disabled dates for the view.
+ /// The special dates for the view.
+ /// Defines whether the view is current view or not.
+ internal MonthView(ICalendar calendarView, List visibleDates, DateTime? selectedDate, List disabledDates, List specialDatesDetails, bool isCurrentView)
{
_isCurrentView = isCurrentView;
_calendarViewInfo = calendarView;
@@ -241,12 +257,13 @@ internal void OnSelection(Point point)
}
#endif
- ///
- /// Method to update when visible dates changed.
- ///
- /// The visible dates.
- /// Defines whether the view is current view or not.
- internal void UpdateVisibleDatesChange(List visibleDates, bool isCurrentView)
+ ///
+ /// Method to update when visible dates changed.
+ ///
+ /// The visible dates.
+ /// Defines whether the view is current view or not.
+ /// Gets the month view instance for current canvas.
+ internal void UpdateVisibleDatesChange(List visibleDates, bool isCurrentView, CustomSnapLayout customSnapLayout)
{
int numberOfVisibleWeeks = CalendarViewHelper.GetNumberOfWeeks(_calendarViewInfo.MonthView);
bool isNumberOfWeeksChanged = _numberOfWeeks != numberOfVisibleWeeks;
@@ -254,8 +271,137 @@ internal void UpdateVisibleDatesChange(List visibleDates, bool isCurre
int previousVisibleDatesCount = _visibleDates.Count;
//// This method only triggers when the previous visible dates are not equal to the current visible dates.
_visibleDates = visibleDates;
+
+ // Proceed only if a SelectionCellTemplate is defined in the calendar view info
+ if (_calendarViewInfo.SelectionCellTemplate != null && _calendarViewInfo.SelectionMode == CalendarSelectionMode.Single)
+ {
+ // If the custom snap layout is null, exit early (no layout to process)
+ if (customSnapLayout == null)
+ {
+ return;
+ }
+
+ // Iterate through each MonthViewLayout inside the customSnapLayout
+ foreach (MonthViewLayout layout in customSnapLayout.Children)
+ {
+ // Skip if the layout or its children are null
+ if (layout?.Children == null)
+ {
+ continue;
+ }
+
+ // Iterate through each view in the layout's children
+ foreach (var view in layout.Children)
+ {
+ // Check if the view is a MonthView and has child views
+ if (view is not MonthView month || month.Children == null)
+ {
+ continue;
+ }
+
+ // Get the reference to the current selectionCellTemplateView from the month view
+ var child = month._selectionCellTemplateView;
+
+ // Skip if the selection cell template view is not present
+ if (child == null)
+ {
+ continue;
+ }
+
+ // If a CellTemplate is defined for the MonthView
+ if (_calendarViewInfo.MonthView.CellTemplate != null)
+ {
+ // Find the index of the selection cell in the month view's children
+ int index = month.Children.IndexOf(child);
+
+ // Validate the index and ensure the cell at the index matches the selection view
+ if (month._monthCells != null && index >= 0 && index < month._monthCells.Count)
+ {
+ // Skip if the selection view is already correctly placed
+ if (month._monthCells[index] == child)
+ {
+ continue;
+ }
+
+ // Skip if the selected date is already within the visible dates of this month
+ if (month._visibleDates.Contains(_selectedDate!.Value))
+ {
+ continue;
+ }
+ }
+ }
+ else
+ {
+ // Skip if the child view is not the current selection cell template view
+ if (child != month._selectionCellTemplateView)
+ {
+ continue;
+ }
+ }
+
+ // Default values for visibility checks
+ bool isVisible = false;
+ bool checkVisibility = false;
+
+ // Proceed only for single selection mode and a selected date is available
+ if (_selectedDate.HasValue)
+ {
+ DateTime selectedDate = _selectedDate.Value;
+
+ // Check if the selected date is among the visible dates of the month
+ bool isInVisibleDates = month._visibleDates?.Contains(selectedDate) ?? false;
+
+ // Check if the selected date is allowed via the selection predicate
+ bool isSelectable = _calendarViewInfo.IsSelectableDayPredicate(selectedDate);
+
+ // Validate the selected date against calendar rules and settings
+ if (selectedDate >= _calendarViewInfo.MinimumDate && selectedDate <= _calendarViewInfo.MaximumDate &&
+ (!isInVisibleDates || _calendarViewInfo.EnablePastDates) && (isInVisibleDates && isSelectable) &&
+ (_calendarViewInfo.ShowTrailingAndLeadingDates || selectedDate.Month == _calendarViewInfo.DisplayDate.Month))
+ {
+ // Mark that visibility check is needed
+ checkVisibility = true;
+
+ // If using a cell template and selected date is not in visible dates, remove the template
+ if (_calendarViewInfo.MonthView.CellTemplate != null)
+ {
+ if (!isInVisibleDates)
+ {
+ MonthView.RemoveTemplateView(month, child);
+ continue;
+ }
+ }
+ else
+ {
+ isVisible = isInVisibleDates;
+ }
+ }
+ }
+
+ // If no visibility check passed (invalid selection or not visible), handle cleanup
+ if (!checkVisibility)
+ {
+ // If using a cell template, remove it
+ if (_calendarViewInfo.MonthView.CellTemplate != null)
+ {
+ // If the selection cell template on canvas following condition based that time should remove the selection cell template.
+ MonthView.RemoveTemplateView(month, child);
+ }
+ else
+ {
+ // Otherwise, just hide the view
+ child.IsVisible = isVisible;
+ }
+ }
+
+ // Exit inner loop after processing the first valid selection cell
+ break;
+ }
+ }
+ }
+
#if MACCATALYST || (!ANDROID && !IOS)
- _hoverView.UpdateVisibleDatesChange(visibleDates);
+ _hoverView.UpdateVisibleDatesChange(visibleDates);
#endif
//// In cell template need to draw the selection.
//// If cell template null then need to draw the visible dates and selection.
@@ -326,7 +472,17 @@ internal void UpdateDisableAndSpecialDateChange(List? disabledDates, L
isNeedToInvalidate = true;
}
- if (isNeedToInvalidate)
+ if (_selectionCellTemplateView != null && _calendarViewInfo.SelectionMode == CalendarSelectionMode.Single)
+ {
+ // If I select any date on current month and selectable predicate date is same as selected date.
+ if (_selectedDate != null && !_calendarViewInfo.IsSelectableDayPredicate((DateTime)_selectedDate))
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+ }
+
+ if (isNeedToInvalidate)
{
InvalidateDrawable();
}
@@ -337,7 +493,14 @@ internal void UpdateDisableAndSpecialDateChange(List? disabledDates, L
///
internal void UpdateSelectionValue()
{
- DateTime? previousSelectedDate = _selectedDate;
+ // If Selected date changed dynamically that time it can perform
+ if (_selectionCellTemplateView != null && _calendarViewInfo.SelectionMode == CalendarSelectionMode.Single)
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+
+ DateTime? previousSelectedDate = _selectedDate;
_selectedDate = _calendarViewInfo.SelectedDate;
if (_calendarViewInfo.SelectionMode != CalendarSelectionMode.Single || _selectedDate?.Date == previousSelectedDate?.Date)
{
@@ -505,7 +668,15 @@ internal void UpdateSelectedDatesOnAction(NotifyCollectionChangedEventArgs e)
///
internal void InvalidateMonthView()
{
- InvalidateDrawable();
+ // Check if the SelectionMode is not set to 'Single'.
+ // If the SelectionMode is not 'Single', hide the selection cell template view by calling the SelectionCellTemplateVisibility method.
+ if (_selectionCellTemplateView != null && _calendarViewInfo.SelectionMode != CalendarSelectionMode.Single)
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+
+ InvalidateDrawable();
}
///
@@ -526,7 +697,26 @@ internal void InvalidateViewMeasure()
///
internal void InvalidateView()
{
- InvalidateDrawable();
+ // Check if selectedDate and selectionCellTemplateView is not null before proceeding with the date restriction validation logic.
+ if (_selectionCellTemplateView != null && _selectedDate != null)
+ {
+ bool isInVisibleDates = _visibleDates?.Contains(_selectedDate.Value) ?? false;
+ bool isTemplateView = false;
+ if (_selectedDate >= _calendarViewInfo.MinimumDate && _selectedDate <= _calendarViewInfo.MaximumDate &&
+ (_calendarViewInfo.ShowTrailingAndLeadingDates || _selectedDate.Value.Month == _calendarViewInfo.DisplayDate.Month) &&
+ (!isInVisibleDates || _calendarViewInfo.EnablePastDates))
+ {
+ isTemplateView = isInVisibleDates;
+ }
+
+ if (!isTemplateView)
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+ }
+
+ InvalidateDrawable();
#if MACCATALYST || (!ANDROID && !IOS)
_hoverView.InvalidateDrawable();
#endif
@@ -554,7 +744,13 @@ internal void UpdateTemplateViews(bool isCurrentView)
}
else
{
- DrawingOrder = DrawingOrder.AboveContent;
+ // It's perform when i change show trailing and leading date dynamically
+ if (_calendarViewInfo.SelectionCellTemplate != null && _previousMonthCellTemplateView != null)
+ {
+ _previousMonthCellTemplateView = null;
+ }
+
+ DrawingOrder = DrawingOrder.AboveContent;
RemoveMonthCellsHandler();
_monthCells = new List();
GenerateMonthCells(isCurrentView);
@@ -721,7 +917,11 @@ static void DrawIcon(ICanvas canvas, RectF bounds, float iconSize, CalendarIconD
float rectXPosition = bounds.Left + ((bounds.Width - iconSize) / 2);
//// The rectYPosition = month cell bounds bottom(90) - iconSize(9)) - 5 = 85.5(rectYPosition).
//// 5 denotes the bottom padding between the icon and month cell.
- float rectYPosition = bounds.Bottom - iconSize - 5;
+ float bottomPadding = 5;
+#if WINDOWS || MACCATALYST
+ bottomPadding = 10;
+#endif
+ float rectYPosition = bounds.Bottom - iconSize - bottomPadding;
//// The iconRect = new RectF(95.5, 85.5, 9, 9).
RectF iconRect = new RectF(rectXPosition, rectYPosition, iconSize, iconSize);
canvas.FillColor = calendarSpecialDayIconDetails.Fill.ToColor();
@@ -793,8 +993,8 @@ static PathF CreateHeartPath(RectF rect)
heartPath.MoveTo(rect.X + (rect.Width * .5f), rect.Y + (rect.Height * .15f));
heartPath.CurveTo(rect.X + (rect.Width * 0.60f), rect.Y + (rect.Height * 0.05f), rect.X + (rect.Width * 0.9f), rect.Y + (rect.Height * 0.05f), rect.X + (rect.Width * .95f), rect.Y + (rect.Height * .25f));
heartPath.CurveTo(rect.X + (rect.Width * 1f), rect.Y + (rect.Height * .45f), rect.X + (rect.Width * .75f), rect.Y + (rect.Height * .70f), rect.X + (rect.Width * .50f), rect.Y + (rect.Height * .95f));
- heartPath.CurveTo(rect.X + (rect.Width * .25f), rect.Y + (rect.Height * .70f), rect.X, rect.Y + (rect.Height * .45f), rect.X + (rect.Width * .05f), rect.Y + (rect.Height * .25f));
- heartPath.CurveTo(rect.X + (rect.Width * 0.01f), rect.Y + (rect.Height * .05f), rect.X + (rect.Width * .4f), rect.Y + (rect.Height * 0.05f), rect.X + (rect.Width * .50f), rect.Y + (rect.Height * .15f));
+ heartPath.CurveTo(rect.X + (rect.Width * .25f), rect.Y + (rect.Height * .70f), rect.X, rect.Y + (rect.Height * .45f), rect.X + (rect.Width * .05f), rect.Y + (rect.Height * .25f));
+ heartPath.CurveTo(rect.X + (rect.Width * 0.01f), rect.Y + (rect.Height * .05f), rect.X + (rect.Width * .4f), rect.Y + (rect.Height * 0.05f), rect.X + (rect.Width * .50f), rect.Y + (rect.Height * .15f));
return heartPath;
}
@@ -885,6 +1085,42 @@ static PathF GetStarShapePath(RectF rect)
return null;
}
+ ///
+ /// This method is used to remove selection cell template and insert the normal month cell template
+ ///
+ /// Get the month details from current canvas
+ /// Get the view details from the month
+ static void RemoveTemplateView(MonthView month, View child)
+ {
+ int index = -1;
+#if !WINDOWS
+ if (month._monthCells != null && month._monthCells.Count > 0)
+ {
+ for (int i = 0; i < month._monthCells.Count; i++)
+ {
+ if (month.Children[i] != month._monthCells[i])
+ {
+ index = i;
+ break;
+ }
+ }
+ }
+#else
+ index = month.Children.IndexOf(child);
+#endif
+ if (index != -1)
+ {
+ month.Children.RemoveAt(index);
+ if (month._previousMonthCellTemplateView != null)
+ {
+ month.Insert(index, month._previousMonthCellTemplateView);
+ }
+
+ month._previousMonthCellTemplateView = null;
+ month._selectionCellTemplateView = null;
+ }
+ }
+
#if __IOS__ || __MACCATALYST__
///
@@ -900,12 +1136,12 @@ async void SetFocus(int delay)
#endif
- ///
- /// Method to find the range is start range or end range or in between range.
- ///
- /// The date time.
- /// The rage is startRange or endRange or inBetween range.
- SelectedRangeStatus? GetRangeSelectionStatus(DateTime? dateTime)
+ ///
+ /// Method to find the range is start range or end range or in between range.
+ ///
+ /// The date time.
+ /// The rage is startRange or endRange or inBetween range.
+ SelectedRangeStatus? GetRangeSelectionStatus(DateTime? dateTime)
{
if (_selectedRange == null)
{
@@ -1387,8 +1623,8 @@ 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);
- //// Restrict the special day icon highlight while the month cell have today highlight.
- CalendarIconDetails? calendarSpecialDayIconDetails = isTodayDate && todayHighlightColor != Colors.Transparent ? null : _specialDates.FirstOrDefault(details => CalendarViewHelper.IsSameDate(_calendarViewInfo.View, details.Date, dateTime, _calendarViewInfo.Identifier));
+ //// Stores the special dates icon details for drawing.
+ CalendarIconDetails? calendarSpecialDayIconDetails = _specialDates.FirstOrDefault(details => CalendarViewHelper.IsSameDate(_calendarViewInfo.View, details.Date, dateTime, _calendarViewInfo.Identifier));
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)
@@ -1418,8 +1654,9 @@ void DrawMonthCells(ICanvas canvas, bool isRTL, float weekNumberWidth, float mon
//// If it is selected date then the selection is drawn based on the selection shape.
if (isSelectedDate)
{
- textStyle = monthViewSettings.SelectionTextStyle;
- DrawSelectionShape(canvas, highlightBounds, selectionRadius, cornerRadius, selectedDateBackground, centerPosition);
+ bool useCustomTemplate = _calendarViewInfo.SelectionCellTemplate == null;
+ textStyle = useCustomTemplate ? monthViewSettings.SelectionTextStyle : (_calendarViewInfo.SelectionMode == CalendarSelectionMode.Single ? new CalendarTextStyle { TextColor = Colors.Transparent } : monthViewSettings.SelectionTextStyle);
+ DrawSelectionShape(canvas, highlightBounds, selectionRadius, cornerRadius, selectedDateBackground, centerPosition);
}
break;
@@ -1462,7 +1699,7 @@ void DrawMonthCells(ICanvas canvas, bool isRTL, float weekNumberWidth, float mon
string dateText = isGregorianCalendar ? dateTime.Day.ToString() : dateTime.ToString("dd", cultureInfo);
CalendarViewHelper.DrawText(canvas, dateText, textStyle, new RectF(xPosition, yPosition, monthCellWidth, monthCellHeight), HorizontalAlignment.Center, VerticalAlignment.Center);
- //// No need to draw the special day icon while the date is today date, selected date, dates and range.
+ //// No need to draw the special day icon while the date is selected date, dates and range.
if (calendarSpecialDayIconDetails != null && !isSelectedDate)
{
float iconSize = maximumSquareSize * 0.15f;
@@ -1810,16 +2047,108 @@ void DrawWeekNumbers(ICanvas canvas, bool isRTL, float weekNumberWidth, float mo
/// The center position of the month cell.
void DrawSelectionShape(ICanvas canvas, RectF highlightBounds, float selectionRadius, float cornerRadius, Color cellBackground, PointF centerPosition)
{
- canvas.FillColor = cellBackground;
- if (_calendarViewInfo.SelectionShape == CalendarSelectionShape.Rectangle)
- {
- RectF rectF = new RectF(highlightBounds.Left + HighlightPadding, highlightBounds.Top + HighlightPadding, highlightBounds.Width - (2 * HighlightPadding), highlightBounds.Height - (2 * HighlightPadding));
- canvas.FillRoundedRectangle(rectF, cornerRadius);
- }
- else
- {
- canvas.FillCircle(centerPosition, selectionRadius);
- }
+ // Check if a SelectionCellTemplate is defined, if the selection mode is Single,
+ // and if the current view is a Month view (i.e., all conditions must be true).
+ if (_calendarViewInfo.SelectionCellTemplate != null && _calendarViewInfo.SelectionMode == CalendarSelectionMode.Single && _calendarViewInfo.View == CalendarView.Month && _selectedDate != null)
+ {
+ // Create the selection cell template view based on the selected date and template settings.
+ // This calls a helper function to generate the view using the template.
+ // To get the month cell details for selected date
+ CalendarCellDetails details = GetMonthCellDetails(_selectedDate.Value.Month, _selectedDate.Value);
+ if (details != null)
+ {
+ _selectionCellTemplateView = CalendarViewHelper.CreateSelectionCellTemplate(_selectedDate, _calendarViewInfo.SelectionCellTemplate, _calendarViewInfo.MonthView, details, highlightBounds);
+ }
+
+ // Only proceed if the selection cell template view was successfully created (not null).
+ if (_selectionCellTemplateView != null)
+ {
+ // Check if a CellTemplate is defined for the MonthView
+ if (_calendarViewInfo.MonthView.CellTemplate != null)
+ {
+ // If there are no monthCells available, exit early (nothing to process)
+ if (_monthCells == null || _monthCells.Count == 0)
+ {
+ return;
+ }
+
+ // Try to find the index of the month cell that matches the currently selected date
+ int index = _monthCells.FindIndex(cell =>
+ {
+ var cellDetails = ((View)cell).BindingContext as CalendarCellDetails;
+ return cellDetails?.Date == _selectedDate;
+ });
+
+ // If a matching month cell was found
+ if (index != -1)
+ {
+ // Get the view corresponding to the found month cell
+ var currentCellView = (View)_monthCells[index];
+
+ // If there is a previously stored template view and its index is valid and different from the current one
+ if (_previousMonthCellTemplateView != null)
+ {
+ int previousMonthCellIndex = _monthCells.IndexOf(_previousMonthCellTemplateView);
+ if (previousMonthCellIndex != index && previousMonthCellIndex >= 0 && previousMonthCellIndex < _monthCells.Count)
+ {
+ // Restore the previous template view to its original position
+ Children.RemoveAt(previousMonthCellIndex);
+ Insert(previousMonthCellIndex, _previousMonthCellTemplateView);
+#if !WINDOWS
+ // Clear the previous template tracking variables
+ _previousMonthCellTemplateView = null;
+#endif
+ }
+ }
+
+#if WINDOWS
+ UpdateSelectionCellTemplate(index);
+#else
+ // If there is no currently tracked previous template (i.e., first time or after clearing)
+ if (_previousMonthCellTemplateView == null)
+ {
+ // Update the cell at the selected index with the new selection cell template view
+ UpdateSelectionCellTemplate(index);
+ }
+#endif
+ }
+ }
+ else
+ {
+ // Add the new selection view to the parent container (the current view).
+ Add(_selectionCellTemplateView);
+
+ // If there was a previously existing cell template view, remove it from the container.
+ // This ensures only one selection cell template is visible at a time.
+ if (_previousSelectionCellTemplateView != null)
+ {
+ Remove(_previousSelectionCellTemplateView);
+ }
+
+ // Update the previous cell template view reference to the current one.
+ // This allows the next time to know which view to remove (for reusability).
+ _previousSelectionCellTemplateView = _selectionCellTemplateView;
+ AbsoluteLayout.SetLayoutBounds(_selectionCellTemplateView, highlightBounds);
+ }
+
+#if ANDROID
+ this.InvalidateViewMeasure();
+#endif
+ }
+ }
+ else
+ {
+ canvas.FillColor = cellBackground;
+ if (_calendarViewInfo.SelectionShape == CalendarSelectionShape.Rectangle)
+ {
+ RectF rectF = new RectF(highlightBounds.Left + HighlightPadding, highlightBounds.Top + HighlightPadding, highlightBounds.Width - (2 * HighlightPadding), highlightBounds.Height - (2 * HighlightPadding));
+ canvas.FillRoundedRectangle(rectF, cornerRadius);
+ }
+ else
+ {
+ canvas.FillCircle(centerPosition, selectionRadius);
+ }
+ }
}
///
@@ -2301,16 +2630,59 @@ bool IsCurrentViewRangesUpdated(ObservableCollection? oldSele
return !CalendarViewHelper.IsSameDateRanges(_calendarViewInfo.View, currentViewOldSelectedRanges, currentViewNewSelectedRanges, _calendarViewInfo.Identifier);
}
- #endregion
-
- #region Override Methods
-
- ///
- /// Method to draw the month view.
- ///
- /// The draw canvas.
- /// The rectangle.
- protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
+ ///
+ /// Insert the selection cell template instead of selected month cell.
+ ///
+ /// Gets the selected month cell index
+ void UpdateSelectionCellTemplate(int index)
+ {
+ if (_monthCells != null && _selectionCellTemplateView != null)
+ {
+ // Store current selection info
+ _previousMonthCellTemplateView = (View)_monthCells[index];
+
+ // Replace with the selection view
+ Children.RemoveAt(index);
+ Insert(index, _selectionCellTemplateView);
+ }
+ }
+
+ ///
+ /// This method handle the selection cell template visibility for date restrictions
+ ///
+ void HideSelectionCellTemplateView()
+ {
+ if (_selectionCellTemplateView == null)
+ {
+ return;
+ }
+
+ // to check If the month cell template value not null
+ if (_calendarViewInfo.MonthView.CellTemplate != null)
+ {
+ MonthView.RemoveTemplateView(this, _selectionCellTemplateView);
+ }
+ else
+ {
+ // Check if the selectionCellTemplateView is not null and is already part of the view's children.
+ if (_selectionCellTemplateView != null && Children.Contains(_selectionCellTemplateView))
+ {
+ // Set the IsVisible property to false, effectively hiding the selectionCellTemplateView.
+ _selectionCellTemplateView.IsVisible = false;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Override Methods
+
+ ///
+ /// Method to draw the month view.
+ ///
+ /// The draw canvas.
+ /// The rectangle.
+ protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
{
//// Method to save the current state of the graphics in the canvas.
canvas.SaveState();
@@ -2721,7 +3093,14 @@ void ITapGestureListener.OnTap(TapEventArgs e)
if (_calendarViewInfo.CanToggleDaySelection && _selectedDate?.Date == tappedDate?.Date)
{
tappedDate = null;
- }
+
+ // If the selection cell template applied over click the same date while the CanToggleDaySelection value true.
+ if (_selectionCellTemplateView != null)
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+ }
_calendarViewInfo.UpdateSelectedDate(tappedDate);
break;
diff --git a/maui/src/Calendar/Views/CalendarView/MonthView/MonthViewLayout.cs b/maui/src/Calendar/Views/CalendarView/MonthView/MonthViewLayout.cs
index d7f4a7df..f37df214 100644
--- a/maui/src/Calendar/Views/CalendarView/MonthView/MonthViewLayout.cs
+++ b/maui/src/Calendar/Views/CalendarView/MonthView/MonthViewLayout.cs
@@ -202,20 +202,21 @@ void ICalendarView.SetFocusOnViewChanged()
_monthView.SetFocusOnViewChanged();
}
- ///
- /// Method to update when visible dates changed.
- ///
- /// The visible dates.
- /// Defines whether the view is current view or not.
- void ICalendarView.UpdateVisibleDatesChange(List visibleDates, bool isCurrentView)
- {
+ ///
+ /// Method to update when visible dates changed.
+ ///
+ /// The visible dates.
+ /// Defines whether the view is current view or not.
+ /// Gets the month view instance for current canvas.
+ void ICalendarView.UpdateVisibleDatesChange(List visibleDates, bool isCurrentView, CustomSnapLayout customSnapLayout)
+ {
if (_visibleDates == visibleDates)
{
return;
}
_visibleDates = visibleDates;
- _monthView.UpdateVisibleDatesChange(visibleDates, isCurrentView);
+ _monthView.UpdateVisibleDatesChange(visibleDates, isCurrentView, customSnapLayout);
_monthViewHeader?.UpdateVisibleDatesChange(visibleDates);
}
diff --git a/maui/src/Calendar/Views/CalendarView/YearView/YearView.cs b/maui/src/Calendar/Views/CalendarView/YearView/YearView.cs
index fd3a9cfb..c0917e2b 100644
--- a/maui/src/Calendar/Views/CalendarView/YearView/YearView.cs
+++ b/maui/src/Calendar/Views/CalendarView/YearView/YearView.cs
@@ -104,18 +104,33 @@ internal class YearView : SfView, ITapGestureListener, IDoubleTapGestureListener
///
DateTime? _previousSelectedDateRange;
- #endregion
-
- #region Constructor
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The Calendar Year view info.
- /// The visible dates collection.
- /// The disabled dates collection.
- /// Defines whether the view is current view or not.
- internal YearView(ICalendar calendarViewInfo, List visibleDates, List disabledDates, bool isCurrentView)
+ ///
+ /// To store the selected date data template view
+ ///
+ View? _selectionCellTemplateView;
+
+ ///
+ /// To store the selected cell template view
+ ///
+ View? _previousSelectionCellTemplateView;
+
+ ///
+ /// To store the previous year cell template view
+ ///
+ View? _previousYearCellTemplateView;
+
+ #endregion
+
+ #region Constructor
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The Calendar Year view info.
+ /// The visible dates collection.
+ /// The disabled dates collection.
+ /// Defines whether the view is current view or not.
+ internal YearView(ICalendar calendarViewInfo, List visibleDates, List disabledDates, bool isCurrentView)
{
_calendarViewInfo = calendarViewInfo;
_visibleDates = visibleDates;
@@ -162,9 +177,45 @@ internal void InvalidateSemanticsNode(bool isCurrentView)
InvalidateSemantics();
}
- #endregion
+ #endregion
- #region Private Methods
+ #region Private Methods
+
+ ///
+ /// This method is used to remove selection template and insert the normal year cell template
+ ///
+ /// Get the year details from current canvas
+ /// Get the view details from the year
+ static void RemoveTemplateView(YearView year, View child)
+ {
+ int index = -1;
+#if !WINDOWS
+ if (year._yearCells != null && year._yearCells.Count > 0)
+ {
+ for (int i = 0; i < year._yearCells.Count; i++)
+ {
+ if (year._yearCells[i] != year.Children[i])
+ {
+ index = i;
+ break;
+ }
+ }
+ }
+#else
+ index = year.Children.IndexOf(child);
+#endif
+ if (index != -1)
+ {
+ year.Children.RemoveAt(index);
+ if (year._previousYearCellTemplateView != null)
+ {
+ year.Insert(index, year._previousYearCellTemplateView);
+ }
+
+ year._previousYearCellTemplateView = null;
+ year._selectionCellTemplateView = null;
+ }
+ }
#if __IOS__ || __MACCATALYST__
@@ -181,16 +232,16 @@ async void SetFocus(int delay)
#endif
- ///
- /// Method to draw the start range selection highlight
- ///
- /// The canvas to draw
- /// Position to draw
- /// Position and size for highlight
- /// The circle corner radius
- /// The rectangle corner radius
- /// The end range background to apply
- void DrawStartRangeSelectionHighlight(ICanvas canvas, RectF rect, RectF highlightRect, float circleCornerRadius, float rectCornerRadius, Color selectionBackground)
+ ///
+ /// Method to draw the start range selection highlight
+ ///
+ /// The canvas to draw
+ /// Position to draw
+ /// Position and size for highlight
+ /// The circle corner radius
+ /// The rectangle corner radius
+ /// The end range background to apply
+ void DrawStartRangeSelectionHighlight(ICanvas canvas, RectF rect, RectF highlightRect, float circleCornerRadius, float rectCornerRadius, Color selectionBackground)
{
canvas.FillColor = selectionBackground;
float leftPadding = (rect.Width - highlightRect.Width) * 0.5f;
@@ -361,8 +412,9 @@ void DrawYearViewPanelCells(ICanvas canvas, float xPosition, float yPosition, bo
//// If it is selected date then the selection is drawn based on the selection shape.
if (isSelectedDate)
{
- textStyle = _calendarViewInfo.YearView.SelectionTextStyle;
- DrawSelectionHighlight(canvas, highlightRect, rectCornerRadius, circleCornerRadius, selectionBackground);
+ bool useCustomTemplate = _calendarViewInfo.SelectionCellTemplate == null;
+ textStyle = useCustomTemplate ? _calendarViewInfo.YearView.SelectionTextStyle : (_calendarViewInfo.SelectionMode == CalendarSelectionMode.Single ? new CalendarTextStyle { TextColor = Colors.Transparent } : _calendarViewInfo.YearView.SelectionTextStyle);
+ DrawSelectionHighlight(canvas, highlightRect, rectCornerRadius, circleCornerRadius, selectionBackground);
}
break;
@@ -711,15 +763,113 @@ void DrawTodayHighlight(ICanvas canvas, RectF highlightRect, float rectCornerRad
/// The background Color to fill
void DrawSelectionHighlight(ICanvas canvas, RectF highlightRect, float rectCornerRadius, float circleCornerRadius, Color backgroundColor)
{
- canvas.FillColor = backgroundColor;
- if (_calendarViewInfo.SelectionShape == CalendarSelectionShape.Circle)
- {
- canvas.FillRoundedRectangle(highlightRect, circleCornerRadius);
- }
- else
- {
- canvas.FillRoundedRectangle(highlightRect, rectCornerRadius);
- }
+ if (_calendarViewInfo.SelectionCellTemplate != null && _calendarViewInfo.SelectionMode == CalendarSelectionMode.Single && !_calendarViewInfo.AllowViewNavigation && _calendarViewInfo.View != CalendarView.Month && _selectedDate != null)
+ {
+ // Create the selection cell template view based on the selected date and template settings.
+ // This calls a helper function to generate the view for the year view using the selected date and visible dates.
+ // To get the selected date cell details
+ CalendarCellDetails details = GetYearCellDetails(_selectedDate.Value, _visibleDates[0]);
+ if (details != null)
+ {
+ _selectionCellTemplateView = CalendarViewHelper.CreateSelectionCellTemplate(_selectedDate, _calendarViewInfo.SelectionCellTemplate, _calendarViewInfo.MonthView, details, highlightRect);
+ }
+
+ // Only proceed if the selection cell template view was successfully created (i.e., it is not null).
+ if (_selectionCellTemplateView != null)
+ {
+ // Proceed only if a YearView CellTemplate is defined and a date is currently selected
+ if (_calendarViewInfo.YearView.CellTemplate != null && _selectedDate != null)
+ {
+ // Get the value of the selected date
+ DateTime selected = _selectedDate.Value;
+
+ // Adjust the selected date based on specific year view logic
+ selected = SelectedDateBasedOnYearViews(selected);
+
+ // Ensure yearCells is not null or empty before proceeding
+ if (_yearCells == null || _yearCells.Count == 0)
+ {
+ return; // Exit early if there's nothing to work with
+ }
+
+ // Try to find the index of the cell that matches the adjusted selected date
+ int index = _yearCells.FindIndex(cell =>
+ {
+ var cellDetails = ((View)cell).BindingContext as CalendarCellDetails;
+ return cellDetails?.Date == selected;
+ });
+
+ // If a matching cell was found
+ if (index != -1)
+ {
+ // Retrieve the current cell view at the found index
+ var currentYearCellView = (View)_yearCells[index];
+
+ // If a previous selection template exists and is different from the current index
+ // Also check that the stored index is within valid range
+ if (_previousYearCellTemplateView != null)
+ {
+ int previousYearCellIndex = _yearCells.IndexOf(_previousYearCellTemplateView);
+ if (previousYearCellIndex != index && previousYearCellIndex >= 0 && previousYearCellIndex < _yearCells.Count)
+ {
+ // Remove the previous selection view from the layout
+ Children.RemoveAt(previousYearCellIndex);
+
+ // Reinsert the original view that was replaced by the selection view
+ Insert(previousYearCellIndex, _previousYearCellTemplateView);
+#if !WINDOWS
+ // Reset the tracking variables for the previous selection view
+ _previousYearCellTemplateView = null;
+#endif
+ }
+ }
+
+#if WINDOWS
+ UpdateSelectionCellTemplate(index);
+#else
+ // If no previous selection template is currently tracked
+ if (_previousYearCellTemplateView == null)
+ {
+ UpdateSelectionCellTemplate(index);
+ }
+#endif
+ }
+ }
+ else
+ {
+ // Add the new selection view to the parent container (likely the calendar or its view).
+ Add(_selectionCellTemplateView);
+
+ // If there was a previously existing cell template view, remove it from the container.
+ // This ensures only one selection cell template is visible at a time.
+ if (_previousSelectionCellTemplateView != null)
+ {
+ Remove(_previousSelectionCellTemplateView);
+ }
+
+ // Update the previous cell template view reference to the current one.
+ // This allows the next time to know which view to remove (for reusability).
+ _previousSelectionCellTemplateView = _selectionCellTemplateView;
+ AbsoluteLayout.SetLayoutBounds(_selectionCellTemplateView, highlightRect);
+ }
+ }
+
+#if ANDROID
+ InvalidateViewMeasure();
+#endif
+ }
+ else
+ {
+ canvas.FillColor = backgroundColor;
+ if (_calendarViewInfo.SelectionShape == CalendarSelectionShape.Circle)
+ {
+ canvas.FillRoundedRectangle(highlightRect, circleCornerRadius);
+ }
+ else
+ {
+ canvas.FillRoundedRectangle(highlightRect, rectCornerRadius);
+ }
+ }
}
///
@@ -1629,16 +1779,78 @@ void UpdateSelectionWhileKeyNavigation(KeyEventArgs args, DateTime oldSelectedDa
CalendarViewHelper.ValidateDateOnKeyNavigation(args, oldSelectedDates, newDate, _calendarViewInfo, _visibleDates, _disabledDates);
}
- #endregion
-
- #region Override Methods
-
- ///
- /// Method to draw the Year view cells.
- ///
- /// The draw canvas.
- /// The rectangle.
- protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
+ ///
+ /// This method handle the selected date based on year views
+ ///
+ /// The selected date on year view
+ /// Returns the updated select date based on year view
+ DateTime SelectedDateBasedOnYearViews(DateTime selected)
+ {
+ selected = _calendarViewInfo.View switch
+ {
+ CalendarView.Year => new DateTime(selected.Year, selected.Month, 1),
+ CalendarView.Decade => new DateTime(selected.Year, 1, 1),
+ CalendarView.Century => new DateTime((selected.Year / 10) * 10, 1, 1),
+ _ => selected // or handle default case appropriately
+ };
+
+ return selected;
+ }
+
+ ///
+ /// Insert the selection cell template instead of selected year cell.
+ ///
+ /// Gets the selected month cell index
+ void UpdateSelectionCellTemplate(int index)
+ {
+ // Insert the selection cell template view at the current index
+ if (_yearCells != null && _selectionCellTemplateView != null)
+ {
+ // Store current selection info
+ _previousYearCellTemplateView = (View)_yearCells[index];
+
+ // Replace with the selection view
+ Children.RemoveAt(index);
+ Insert(index, _selectionCellTemplateView);
+ }
+ }
+
+ ///
+ /// This method handle the selection cell template visibility for date restrictions
+ ///
+ void HideSelectionCellTemplateView()
+ {
+ if (_selectionCellTemplateView == null)
+ {
+ return;
+ }
+
+ // to check If the month cell template value not null
+ if (_calendarViewInfo.YearView.CellTemplate != null)
+ {
+ YearView.RemoveTemplateView(this, _selectionCellTemplateView);
+ }
+ else
+ {
+ // Check if the selectionCellTemplateView is not null and is already part of the view's children.
+ if (_selectionCellTemplateView != null && Children.Contains(_selectionCellTemplateView))
+ {
+ // Set the IsVisible property to false, effectively hiding the selectionCellTemplateView.
+ _selectionCellTemplateView.IsVisible = false;
+ }
+ }
+ }
+
+ #endregion
+
+ #region Override Methods
+
+ ///
+ /// Method to draw the Year view cells.
+ ///
+ /// The draw canvas.
+ /// The rectangle.
+ protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
{
//// If the Width and Height value is less than or equal to zero, no need to draw the view.
if (dirtyRect.Width <= 0 || dirtyRect.Height <= 0)
@@ -1930,7 +2142,14 @@ void ITapGestureListener.OnTap(TapEventArgs e)
if (_calendarViewInfo.CanToggleDaySelection && CalendarViewHelper.IsSameDate(_calendarViewInfo.View, _selectedDate, tappedDate, _calendarViewInfo.Identifier))
{
tappedDate = null;
- }
+
+ // If the selection cell template applied over click the same date while the CanToggleDaySelection value true.
+ if (_selectionCellTemplateView != null)
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+ }
_calendarViewInfo.UpdateSelectedDate(tappedDate);
break;
@@ -2016,20 +2235,145 @@ void IKeyboardListener.OnKeyUp(KeyEventArgs args)
{
}
- ///
- /// Method to update visible dates on view change.
- ///
- /// The visible dates collection.
- /// Defines whether the view is current view or not.
- void ICalendarView.UpdateVisibleDatesChange(List visibleDatesCollection, bool isCurrentView)
+ ///
+ /// Method to update visible dates on view change.
+ ///
+ /// The visible dates collection.
+ /// Defines whether the view is current view or not.
+ /// Gets the month view instance for current canvas.
+ void ICalendarView.UpdateVisibleDatesChange(List visibleDatesCollection, bool isCurrentView, CustomSnapLayout customSnapLayout)
{
if (_visibleDates == visibleDatesCollection)
{
return;
}
- List previousVisibleDates = _visibleDates;
+ // Check if a SelectionCellTemplate is defined in the calendar configuration
+ if (_calendarViewInfo.SelectionCellTemplate != null && _calendarViewInfo.SelectionMode == CalendarSelectionMode.Single)
+ {
+ // If the custom layout container is null, exit early nothing to process
+ if (customSnapLayout == null)
+ {
+ return;
+ }
+
+ // Iterate through each child view within the custom layout (expected to be YearView instances)
+ foreach (var yearView in customSnapLayout.Children)
+ {
+ var year = yearView as YearView;
+
+ // Proceed only if the view is a YearView and all essential data is present
+ if (year != null && year._visibleDates != null && year._selectionCellTemplateView != null && year.Children != null)
+ {
+ // Loop through each child view of the YearView
+ for (int i = 0; i < year.Children.Count; i++)
+ {
+ View view = (View)year.Children[i];
+
+ // If a CellTemplate is being used for YearView
+ if (_calendarViewInfo.YearView.CellTemplate != null)
+ {
+ // Ensure valid indexing and matching view before proceeding
+ if (year._yearCells != null && year._yearCells.Count > 0 && i < year._yearCells.Count)
+ {
+ // Skip if the current view is already part of the yearCells collection (i.e., already properly placed)
+ if (year._yearCells[i] == view)
+ {
+ continue;
+ }
+
+ // Skip if the selected date is already visible in this year view
+ if (year._visibleDates.Contains(_selectedDate!.Value))
+ {
+ continue;
+ }
+ }
+ }
+ else
+ {
+ // Skip views that are not the selection cell template view
+ if (view != year._selectionCellTemplateView)
+ {
+ continue;
+ }
+ }
+
+ // Process only when a date is selected and the selection mode is single
+ if (_selectedDate != null)
+ {
+ // Get the selected date value
+ DateTime selected = _selectedDate.Value;
+
+ // Determine whether the selected date falls within the allowed range for the current calendar view
+ bool isSelectedDateAfterMinDateForView = _calendarViewInfo.View switch
+ {
+ CalendarView.Year => selected.Month >= _calendarViewInfo.MinimumDate.Month,
+ CalendarView.Decade => selected.Year >= _calendarViewInfo.MinimumDate.Year,
+ CalendarView.Century => (selected.Year / 100) >= (_calendarViewInfo.MinimumDate.Year / 100),
+ _ => false
+ };
+
+ bool isSelectedDateBeforeMaxDateForView = _calendarViewInfo.View switch
+ {
+ CalendarView.Year => selected.Month <= _calendarViewInfo.MaximumDate.Month,
+ CalendarView.Decade => selected.Year <= _calendarViewInfo.MaximumDate.Year,
+ CalendarView.Century => (selected.Year / 100) <= (_calendarViewInfo.MaximumDate.Year / 100),
+ _ => false
+ };
+
+ // Adjust selected date using helper method for the specific view type
+ selected = SelectedDateBasedOnYearViews(selected);
+
+ // It's applicable only Decade and Century views. Because these have trailing and leading dates.
+ bool check = GetYearCellDetails(selected, year._visibleDates[0]).IsTrailingOrLeadingDate;
+
+ // Check if the selected date is part of the visible date range of the YearView
+ bool isInVisibleDates = year._visibleDates.Contains(selected);
+
+ // Determine whether the view should be visible, based on several calendar settings
+ bool isVisible = _calendarViewInfo.EnablePastDates && isSelectedDateAfterMinDateForView && isSelectedDateBeforeMaxDateForView &&
+ _calendarViewInfo.IsSelectableDayPredicate(selected) && (_calendarViewInfo.ShowTrailingAndLeadingDates || !check);
+
+ // Handle visibility or removal depending on template usage
+ if (_calendarViewInfo.YearView.CellTemplate != null)
+ {
+ // Remove the template if date isn't visible or isn't valid
+ if (!isVisible || !isInVisibleDates)
+ {
+ YearView.RemoveTemplateView(year, view);
+ }
+ }
+ else
+ {
+ // If not using template, just set visibility accordingly
+ view.IsVisible = isVisible && isInVisibleDates;
+ }
+ }
+ else
+ {
+ // If no selection is made
+ if (_calendarViewInfo.YearView.CellTemplate != null)
+ {
+ // Remove the selection view if using template
+ YearView.RemoveTemplateView(year, view);
+ }
+ else
+ {
+ // Otherwise, just hide the view
+ view.IsVisible = false;
+ }
+ }
+
+ // Exit the inner loop after processing the selection view for this YearView
+ break;
+ }
+ }
+ }
+ }
+
+ List previousVisibleDates = _visibleDates;
_visibleDates = visibleDatesCollection;
+
#if MACCATALYST || (!ANDROID && !IOS)
_hoverView.UpdateVisibleDatesChange(_visibleDates);
#endif
@@ -2104,7 +2448,14 @@ void ICalendarView.UpdateVisibleDatesChange(List visibleDatesCollectio
///
void ICalendarView.UpdateSelectionValue()
{
- DateTime? previousSelectedDate = _selectedDate;
+ // If Selected date changed dynamically that time it can perform
+ if (_selectionCellTemplateView != null && _calendarViewInfo.SelectionMode == CalendarSelectionMode.Single)
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+
+ DateTime? previousSelectedDate = _selectedDate;
_selectedDate = _calendarViewInfo.SelectedDate;
if (_calendarViewInfo.SelectionMode != CalendarSelectionMode.Single || CalendarViewHelper.IsSameDate(_calendarViewInfo.View, previousSelectedDate, _selectedDate, _calendarViewInfo.Identifier))
{
@@ -2221,13 +2572,23 @@ void ICalendarView.UpdateDisableAndSpecialDateChange(List? disabledDat
return;
}
+ if (_selectionCellTemplateView != null && _calendarViewInfo.SelectionMode == CalendarSelectionMode.Single)
+ {
+ // If I select any date on current year and selectable predicate date is same as selected date.
+ if (_selectedDate != null && !_calendarViewInfo.IsSelectableDayPredicate((DateTime)_selectedDate))
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+ }
+
#if MACCATALYST || (!ANDROID && !IOS)
- //// Here we are updating the hover position before checking the previous and current disabled dates are equal.
- //// Because the previous and current view disabled dates may be same and we can't update the hover position on swiping to next or previous view.
- //// On view navigation, the mouse hover position will remain same but visible dates will be changed.
- //// So we are removing hovering when updating disabled date for each view.
- //// If we not update the hover position, the hovering view will be remain even after view changed.
- _hoverView.UpdateHoverPosition(null);
+ //// Here we are updating the hover position before checking the previous and current disabled dates are equal.
+ //// Because the previous and current view disabled dates may be same and we can't update the hover position on swiping to next or previous view.
+ //// On view navigation, the mouse hover position will remain same but visible dates will be changed.
+ //// So we are removing hovering when updating disabled date for each view.
+ //// If we not update the hover position, the hovering view will be remain even after view changed.
+ _hoverView.UpdateHoverPosition(null);
#endif
//// Disabled dates is null then the method called only for update special dates.
@@ -2255,7 +2616,25 @@ void ICalendarView.UpdateDisableAndSpecialDateChange(List? disabledDat
///
void ICalendarView.InvalidateView()
{
- InvalidateDrawable();
+ // Check if selectedDate and selectionCellTemplateView is not null before proceeding with the date restriction validation logic.
+ if (_selectionCellTemplateView != null && _selectedDate != null)
+ {
+ bool isInVisibleDates = _visibleDates?.Contains(_selectedDate.Value) ?? false;
+ bool isTemplateView = false;
+ if (_selectedDate >= _calendarViewInfo.MinimumDate && _selectedDate <= _calendarViewInfo.MaximumDate &&
+ (!isInVisibleDates || _calendarViewInfo.EnablePastDates))
+ {
+ isTemplateView = isInVisibleDates;
+ }
+
+ if (!isTemplateView)
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+ }
+
+ InvalidateDrawable();
#if MACCATALYST || (!ANDROID && !IOS)
_hoverView.InvalidateDrawable();
#endif
@@ -2266,7 +2645,15 @@ void ICalendarView.InvalidateView()
///
void ICalendarView.InvalidateViewCells()
{
- InvalidateDrawable();
+ // Check if the SelectionMode is not set to 'Single'.
+ // If the SelectionMode is not 'Single', hide the selection cell template view by calling the SelectionCellTemplateVisibility method.
+ if (_selectionCellTemplateView != null && _calendarViewInfo.SelectionMode != CalendarSelectionMode.Single)
+ {
+ // To handle the selection cell template visibility
+ HideSelectionCellTemplateView();
+ }
+
+ InvalidateDrawable();
}
///
@@ -2291,7 +2678,13 @@ void ICalendarView.UpdateTemplateViews(bool isCurrentView)
}
else
{
- DrawingOrder = DrawingOrder.AboveContent;
+ // It's perform when i change show trailing and leading date dynamically
+ if (_calendarViewInfo.SelectionCellTemplate != null && _previousYearCellTemplateView != null)
+ {
+ _previousYearCellTemplateView = null;
+ }
+
+ DrawingOrder = DrawingOrder.AboveContent;
RemoveYearCellsHandler();
_yearCells = new List();
GenerateYearCells(isCurrentView);
diff --git a/maui/src/Carousel/Platform/Android/PlatformCarousel.Android.cs b/maui/src/Carousel/Platform/Android/PlatformCarousel.Android.cs
index d3b0739a..45ee1271 100644
--- a/maui/src/Carousel/Platform/Android/PlatformCarousel.Android.cs
+++ b/maui/src/Carousel/Platform/Android/PlatformCarousel.Android.cs
@@ -3654,4 +3654,4 @@ public override void GetItemOffsets(Rect outRect, View view, RecyclerView parent
}
#endregion
-}
+}
\ No newline at end of file
diff --git a/maui/src/Charts/Annotation/ChartAnnotation.cs b/maui/src/Charts/Annotation/ChartAnnotation.cs
index 08c00ccf..7e88af36 100644
--- a/maui/src/Charts/Annotation/ChartAnnotation.cs
+++ b/maui/src/Charts/Annotation/ChartAnnotation.cs
@@ -471,11 +471,6 @@ internal static void OnAnnotationPropertyInvalidate(BindableObject bindable, obj
{
if (bindable is ChartAnnotation annotation)
{
- if (annotation is ShapeAnnotation shapeAnnotation)
- {
- shapeAnnotation.InitializeDynamicResource(shapeAnnotation);
- }
-
annotation.Invalidate();
}
}
diff --git a/maui/src/Charts/Annotation/ShapeAnnotation.cs b/maui/src/Charts/Annotation/ShapeAnnotation.cs
index 980d4817..9400c542 100644
--- a/maui/src/Charts/Annotation/ShapeAnnotation.cs
+++ b/maui/src/Charts/Annotation/ShapeAnnotation.cs
@@ -605,19 +605,6 @@ internal virtual Brush GetDefaultStrokeColor()
return new SolidColorBrush(Color.FromArgb("#6750A4"));
}
- internal void InitializeDynamicResource(ShapeAnnotation annotation)
- {
- if (annotation is LineAnnotation)
- {
- SetDynamicResource(StrokeProperty, "SfCartesianChartLineAnnotationStroke");
- }
- else
- {
- SetDynamicResource(FillProperty, "SfCartesianChartShapeAnnotationFill");
- SetDynamicResource(StrokeProperty, "SfCartesianChartShapeAnnotationStroke");
- }
- }
-
#endregion
#region Private Methods
diff --git a/maui/src/Charts/Area/CircularPlotArea.cs b/maui/src/Charts/Area/CircularPlotArea.cs
index 0aae7680..f261cee8 100644
--- a/maui/src/Charts/Area/CircularPlotArea.cs
+++ b/maui/src/Charts/Area/CircularPlotArea.cs
@@ -22,7 +22,7 @@ public CircularPlotArea(CircularChartArea chartArea) : base()
#region Methods
protected override void UpdateLegendItemsSource()
{
- if (Series == null || _legend == null || !_legend.IsVisible)
+ if (Series == null || _legend == null)
{
return;
}
diff --git a/maui/src/Charts/Area/Partial/CartesianChartArea.cs b/maui/src/Charts/Area/Partial/CartesianChartArea.cs
index c9f5c783..ad6d14bf 100644
--- a/maui/src/Charts/Area/Partial/CartesianChartArea.cs
+++ b/maui/src/Charts/Area/Partial/CartesianChartArea.cs
@@ -465,7 +465,6 @@ static void CalculateStackingValues(Dictionary>
{
foreach (var seriesList in seriesGroup.Values)
{
- var xValues = seriesList[0].GetXValues();
var positiveYValues = new Dictionary();
var negativeYValues = new Dictionary();
@@ -482,15 +481,16 @@ static void CalculateStackingValues(Dictionary>
axisCross = 1;
}
}
-
- if (xValues != null)
+
+ foreach (var series in seriesList)
{
- foreach (var series in seriesList)
- {
- var yValues = series.YValues;
- var bottomValues = new List();
- var topValues = new List();
+ var xValues = series.GetXValues();
+ var yValues = series.YValues;
+ var bottomValues = new List();
+ var topValues = new List();
+ if (xValues != null)
+ {
for (int i = 0; i < xValues.Count; i++)
{
var xValue = xValues[i];
diff --git a/maui/src/Charts/Axis/CategoryAxis.cs b/maui/src/Charts/Axis/CategoryAxis.cs
index 3090a03a..c45125f2 100644
--- a/maui/src/Charts/Axis/CategoryAxis.cs
+++ b/maui/src/Charts/Axis/CategoryAxis.cs
@@ -21,7 +21,7 @@ protected override double CalculateActualInterval(DoubleRange range, Size availa
}
///
- protected override DoubleRange ApplyRangePadding(DoubleRange range, double interval)
+ protected sealed override DoubleRange ApplyRangePadding(DoubleRange range, double interval)
{
return LabelPlacement == LabelPlacement.BetweenTicks ? new DoubleRange(-0.5, (int)range.End + 0.5) : range;
}
diff --git a/maui/src/Charts/Axis/ChartAxis.cs b/maui/src/Charts/Axis/ChartAxis.cs
index 4677fb98..33602687 100644
--- a/maui/src/Charts/Axis/ChartAxis.cs
+++ b/maui/src/Charts/Axis/ChartAxis.cs
@@ -684,16 +684,16 @@ void VisibleLabels_CollectionChanged(object? sender, NotifyCollectionChangedEven
if (e.NewItems[0] is ChartAxisLabel item)
{
- var currentLabelCoeffientValue = ValueToCoefficient(item.Position);
+ var currentLabelCoefficientValue = ValueToCoefficient(item.Position);
//Checking the position of the current label already has a label or not.
- if (double.Equals(Math.Round(_previousLabelCoefficientValue, 3), Math.Round(currentLabelCoeffientValue, 3)))
+ if (double.Equals(Math.Round(_previousLabelCoefficientValue, 3), Math.Round(currentLabelCoefficientValue, 3)))
{
VisibleLabels?.Remove(item);
}
else
{
- _previousLabelCoefficientValue = currentLabelCoeffientValue;
+ _previousLabelCoefficientValue = currentLabelCoefficientValue;
}
InvokeLabelCreated(item);
diff --git a/maui/src/Charts/Axis/ChartAxisPartial/ChartAxis.cs b/maui/src/Charts/Axis/ChartAxisPartial/ChartAxis.cs
index f550a371..d5a7f7e1 100644
--- a/maui/src/Charts/Axis/ChartAxisPartial/ChartAxis.cs
+++ b/maui/src/Charts/Axis/ChartAxisPartial/ChartAxis.cs
@@ -479,7 +479,7 @@ public partial class ChartAxis
///
/// Determines the maximum number of labels displayed per 100 pixels.
///
- internal static readonly BindableProperty MaximumLabelsProperty = BindableProperty.Create(
+ public static readonly BindableProperty MaximumLabelsProperty = BindableProperty.Create(
nameof(MaximumLabels),
typeof(int),
typeof(ChartAxis),
@@ -1870,6 +1870,46 @@ public AxisElementPosition LabelsPosition
set { SetValue(LabelsPositionProperty, value); }
}
+ ///
+ /// Gets or sets the value that determines the number of labels to be displayed per 100 pixels.
+ ///
+ /// This property takes the integer value.
+ /// This property used to give constrain over the auto generated labels, which reduces the number elements rendering in view.
+ /// Intended for use with Cartesian axes in SfCartesianChart to control label density; not recommended for Polar charts.
+ ///
+ /// ///
+ /// # [MainPage.xaml](#tab/tabid-1)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ]]>
+ ///
+ /// # [MainPage.xaml.cs](#tab/tabid-2)
+ ///
+ ///
+ /// ***
+ ///
+ public int MaximumLabels
+ {
+ get { return (int)GetValue(MaximumLabelsProperty); }
+ set { SetValue(MaximumLabelsProperty, value); }
+ }
+
///
/// Gets the axis labels visible
///
@@ -1909,18 +1949,6 @@ internal ChartAxisRangeStyleCollection RangeStyles
set { SetValue(RangeStylesProperty, value); }
}
- ///
- /// Gets or sets the value that determines the number of labels to be displayed per 100 pixels.
- ///
- /// This property takes the integer value.
- /// This property used to give constrain over the auto generated labels, which reduces the number elements rendering in view.
- ///TODO: Provide alter way for this support.
- internal int MaximumLabels
- {
- get { return (int)GetValue(MaximumLabelsProperty); }
- set { SetValue(MaximumLabelsProperty, value); }
- }
-
internal static readonly BindableProperty TrackballAxisBackgroundProperty = BindableProperty.Create(nameof(TrackballAxisBackground), typeof(Brush), typeof(ChartAxis), SolidColorBrush.Black, BindingMode.Default, null, null);
internal Brush TrackballAxisBackground
@@ -1945,6 +1973,22 @@ internal Brush MajorGridLineStroke
set { SetValue(MajorGridLineStrokeProperty, value); }
}
+ internal static readonly BindableProperty MajorTickLineStrokeProperty = BindableProperty.Create(nameof(MajorTickLineStroke), typeof(Brush), typeof(ChartAxis), new SolidColorBrush(Color.FromArgb("#CAC4D0")), BindingMode.Default, null, null);
+
+ internal Brush MajorTickLineStroke
+ {
+ get { return (Brush)GetValue(MajorTickLineStrokeProperty); }
+ set { SetValue(MajorTickLineStrokeProperty, value); }
+ }
+
+ internal static readonly BindableProperty AxisLineStrokeProperty = BindableProperty.Create(nameof(AxisLineStroke), typeof(Brush), typeof(ChartAxis), new SolidColorBrush(Color.FromArgb("#CAC4D0")), BindingMode.Default, null, null);
+
+ internal Brush AxisLineStroke
+ {
+ get { return (Brush)GetValue(AxisLineStrokeProperty); }
+ set { SetValue(AxisLineStrokeProperty, value); }
+ }
+
#region is Polar
internal PolarChartArea? PolarArea { get; set; }
@@ -2447,12 +2491,14 @@ void InitializeConstructor()
SetDynamicResource(TrackballAxisBackgroundProperty, "SfCartesianChartTrackballAxisLabelBackground");
SetDynamicResource(TrackballAxisFontSizeProperty, "SfCartesianChartTrackballAxisLabelTextFontSize");
SetDynamicResource(MajorGridLineStrokeProperty, "SfCartesianChartMajorGridLineStroke");
+ SetDynamicResource(MajorTickLineStrokeProperty, "SfCartesianChartMajorTickLineStroke");
+ SetDynamicResource(AxisLineStrokeProperty, "SfCartesianChartAxisLineStroke");
//Todo: Remove this code, After ClipsToBounds works in iOS and Windows.
EdgeLabelsDrawingMode = EdgeLabelsDrawingMode.Shift;
- AxisLineStyle = new ChartLineStyle();
+ AxisLineStyle = new ChartLineStyle() { Stroke = AxisLineStroke };
LabelStyle = new ChartAxisLabelStyle();
MajorGridLineStyle = new ChartLineStyle() { Stroke = MajorGridLineStroke };
- MajorTickStyle = new ChartAxisTickStyle();
+ MajorTickStyle = new ChartAxisTickStyle() { Stroke = MajorTickLineStroke };
TrackballLabelStyle = new ChartLabelStyle() { FontSize = TrackballAxisFontSize, Background = TrackballAxisBackground };
}
@@ -2604,7 +2650,6 @@ static void OnMajorTickStyleChanged(BindableObject bindable, object oldValue, ob
if (newValue is ChartAxisTickStyle tickStyle)
{
SetInheritedBindingContext(tickStyle, axis.BindingContext);
- axis.MajorTickStyle.InitializeDynamicResource("MajorTickStyle");
tickStyle.PropertyChanged += axis.Style_PropertyChanged;
}
@@ -2712,7 +2757,6 @@ static void OnAxisLineStyleChanged(BindableObject bindable, object oldValue, obj
if (newValue is ChartLineStyle lineStyle)
{
SetInheritedBindingContext(lineStyle, axis.BindingContext);
- lineStyle.SetDynamicResource(ChartLineStyle.StrokeProperty, "SfCartesianChartAxisLineStroke");
lineStyle.PropertyChanged += axis.Style_PropertyChanged;
}
diff --git a/maui/src/Charts/Axis/ChartAxisPartial/RangeAxisBase.cs b/maui/src/Charts/Axis/ChartAxisPartial/RangeAxisBase.cs
index 40047140..04cfff6e 100644
--- a/maui/src/Charts/Axis/ChartAxisPartial/RangeAxisBase.cs
+++ b/maui/src/Charts/Axis/ChartAxisPartial/RangeAxisBase.cs
@@ -101,8 +101,10 @@ public RangeAxisBase()
Stroke = MinorGridLineStroke,
StrokeWidth = 0.5f
};
+ SetDynamicResource(MinorTickLineStrokeProperty, "SfCartesianChartMinorTickLineStroke");
MinorTickStyle = new ChartAxisTickStyle
{
+ Stroke = MinorTickLineStroke,
TickSize = 4d
};
}
@@ -329,12 +331,21 @@ public bool ShowMinorGridLines
#endregion
internal static readonly BindableProperty MinorGridLineStrokeProperty = BindableProperty.Create(nameof(MinorGridLineStroke), typeof(Brush), typeof(RangeAxisBase), new SolidColorBrush(Color.FromArgb("#EDEFF1")), BindingMode.Default, null, null);
+
internal Brush MinorGridLineStroke
{
get { return (Brush)GetValue(MinorGridLineStrokeProperty); }
set { SetValue(MinorGridLineStrokeProperty, value); }
}
+ internal static readonly BindableProperty MinorTickLineStrokeProperty = BindableProperty.Create(nameof(MinorTickLineStroke), typeof(Brush), typeof(RangeAxisBase), new SolidColorBrush(Color.FromArgb("#CAC4D0")), BindingMode.Default, null, null);
+
+ internal Brush MinorTickLineStroke
+ {
+ get { return (Brush)GetValue(MinorTickLineStrokeProperty); }
+ set { SetValue(MinorTickLineStrokeProperty, value); }
+ }
+
#region Methods
#region Protected Methods
@@ -356,6 +367,24 @@ protected override void OnBindingContextChanged()
}
}
+ ///
+ /// Sets the parent for the axis elements.
+ ///
+ ///
+ protected override void OnParentSet()
+ {
+ base.OnParentSet();
+
+ if (MinorGridLineStyle != null)
+ {
+ MinorGridLineStyle.Parent = Parent;
+ }
+ if (MinorTickStyle != null)
+ {
+ MinorTickStyle.Parent = Parent;
+ }
+ }
+
#endregion
#region Internal Methods
@@ -440,7 +469,6 @@ static void OnMinorTickStylePropertyChanged(BindableObject bindable, object oldV
if (newValue is ChartAxisTickStyle tickStyle)
{
SetInheritedBindingContext(tickStyle, axis.BindingContext);
- axis.MinorTickStyle.InitializeDynamicResource("MinorTickStyle");
tickStyle.PropertyChanged += axis.LineStyle_PropertyChanged;
tickStyle.Parent = axis.Parent;
}
diff --git a/maui/src/Charts/Axis/DateTimeCategoryAxis.cs b/maui/src/Charts/Axis/DateTimeCategoryAxis.cs
new file mode 100644
index 00000000..2378d854
--- /dev/null
+++ b/maui/src/Charts/Axis/DateTimeCategoryAxis.cs
@@ -0,0 +1,793 @@
+using System.Diagnostics.CodeAnalysis;
+
+namespace Syncfusion.Maui.Toolkit.Charts;
+
+///
+/// The DateTimeCategoryAxis is a specialized axis for plotting chronological data where the axis labels represent dates and times.
+///
+///
+///
+/// The DateTimeCategoryAxis is used only for the X(horizontal) axis in charts.
+///
+/// To render a DateTimeCategory axis, add its instance to the charts collection as shown in the following code sample.
+///
+/// # [MainPage.xaml](#tab/tabid-1)
+///
+///
+///
+///
+///
+///
+///
+/// ]]>
+///
+/// # [MainPage.xaml.cs](#tab/tabid-2)
+///
+///
+/// ***
+///
+/// The DateTimeCategoryAxis supports the following features. Refer to the corresponding APIs for more details and example codes.
+///
+/// Title - To render the title on the axis, refer to the property.
+/// Grid Lines - To show and customize grid lines, refer to and properties.
+/// Axis Line - Customize the axis line using the property.
+/// Labels Customization - Customize axis labels using the property.
+/// Inversed Axis - Invert the axis using the property.
+/// Axis Crossing - For axis crossing, refer to , , and properties.
+/// Interval - Define the interval between axis labels using the and properties.
+///
+public partial class DateTimeCategoryAxis : ChartAxis
+{
+ #region Bindable Properties
+
+ ///
+ /// Identifies the bindable property.
+ ///
+ ///
+ /// Defines the collection of plot bands for the DateTimeCategory axis.
+ ///
+ public static readonly BindableProperty PlotBandsProperty = BindableProperty.Create(nameof(PlotBands), typeof(NumericalPlotBandCollection), typeof(DateTimeCategoryAxis), null, BindingMode.Default, null, OnPlotBandsPropertyChanged);
+
+ ///
+ /// Identifies the bindable property.
+ ///
+ ///
+ /// The property represents the interval value for the DateTimeCategory axis.
+ ///
+ public static readonly BindableProperty IntervalProperty = BindableProperty.Create(nameof(Interval), typeof(double), typeof(DateTimeCategoryAxis), double.NaN, BindingMode.Default, null, OnIntervalPropertyChanged);
+
+ ///
+ /// Identifies the bindable property.
+ ///
+ ///
+ /// Defines the type of interval for the DateTimeCategory axis.
+ ///
+ public static readonly BindableProperty IntervalTypeProperty = BindableProperty.Create(nameof(IntervalType), typeof(DateTimeIntervalType), typeof(DateTimeCategoryAxis), DateTimeIntervalType.Auto, BindingMode.Default, null, OnIntervalTypePropertyChanged);
+
+ #endregion
+
+ #region Public Properties
+
+ ///
+ /// Gets or sets the collection of plot bands.
+ ///
+ /// It accepts and the default value is null.
+ ///
+ /// Plot bands are used to shade specific regions in the chart to improve data visualization.
+ ///
+ ///
+ /// # [MainPage.xaml](#tab/tabid-3)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ]]>
+ ///
+ /// # [MainPage.xaml.cs](#tab/tabid-4)
+ ///
+ ///
+ /// ***
+ ///
+ ///
+ public NumericalPlotBandCollection PlotBands
+ {
+ get { return (NumericalPlotBandCollection)GetValue(PlotBandsProperty); }
+ set { SetValue(PlotBandsProperty, value); }
+ }
+
+ ///
+ /// Gets or sets a value that can be used to change the interval between labels.
+ ///
+ /// It accepts double values and the default value is double.NaN.
+ ///
+ /// If this property is not set, the interval will be calculated automatically.
+ /// By default, the interval will be calculated based on the minimum and maximum values of the provided data.
+ ///
+ ///
+ /// # [MainPage.xaml](#tab/tabid-5)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ]]>
+ ///
+ /// # [MainPage.xaml.cs](#tab/tabid-6)
+ ///
+ ///
+ /// ***
+ ///
+ ///
+ ///
+ public double Interval
+ {
+ get { return (double)GetValue(IntervalProperty); }
+ set { SetValue(IntervalProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the type of interval to be displayed in the axis.
+ ///
+ /// It accepts values and the default value is DateTimeIntervalType.Auto.
+ ///
+ /// The IntervalType property determines the unit of measurement for the axis interval.
+ /// When set to Auto, the interval type will be calculated automatically based on the data range.
+ ///
+ ///
+ /// # [MainPage.xaml](#tab/tabid-7)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ]]>
+ ///
+ /// # [MainPage.xaml.cs](#tab/tabid-8)
+ ///
+ ///
+ /// ***
+ ///
+ ///
+ ///
+ public DateTimeIntervalType IntervalType
+ {
+ get { return (DateTimeIntervalType)GetValue(IntervalTypeProperty); }
+ set { SetValue(IntervalTypeProperty, value); }
+ }
+
+ #endregion
+
+ #region Internal Property
+
+ internal DateTimeIntervalType ActualIntervalType { get; set; }
+
+ #endregion
+
+ #region Methods
+
+ #region Internal Override
+
+ ///
+ protected sealed override DoubleRange ApplyRangePadding(DoubleRange range, double interval)
+ {
+ return range;
+ }
+
+ ///
+ protected override double CalculateNiceInterval(DoubleRange actualRange, Size availableSize)
+ {
+ var dateTimeMin = DateTime.FromOADate(actualRange.Start);
+ var dateTimeMax = DateTime.FromOADate(actualRange.End);
+ var timeSpan = dateTimeMax.Subtract(dateTimeMin);
+ double interval = 0;
+
+ switch (IntervalType)
+ {
+ case DateTimeIntervalType.Years:
+ interval = base.CalculateNiceInterval(new DoubleRange(0, timeSpan.TotalDays / 365), availableSize);
+ break;
+ case DateTimeIntervalType.Months:
+ interval = base.CalculateNiceInterval(new DoubleRange(0, timeSpan.TotalDays / 30), availableSize);
+ break;
+ case DateTimeIntervalType.Days:
+ interval = base.CalculateNiceInterval(new DoubleRange(0, timeSpan.TotalDays), availableSize);
+ break;
+ case DateTimeIntervalType.Hours:
+ interval = base.CalculateNiceInterval(new DoubleRange(0, timeSpan.TotalHours), availableSize);
+ break;
+ case DateTimeIntervalType.Minutes:
+ interval = base.CalculateNiceInterval(new DoubleRange(0, timeSpan.TotalMinutes), availableSize);
+ break;
+ case DateTimeIntervalType.Seconds:
+ interval = base.CalculateNiceInterval(new DoubleRange(0, timeSpan.TotalSeconds), availableSize);
+ break;
+ case DateTimeIntervalType.Milliseconds:
+ interval = base.CalculateNiceInterval(new DoubleRange(0, timeSpan.TotalMilliseconds), availableSize);
+ break;
+ case DateTimeIntervalType.Auto:
+ interval = CalculateIntervalForAutoType(availableSize, timeSpan);
+ break;
+ }
+
+ return Math.Max(1d, interval);
+ }
+
+ ///
+ protected override double CalculateActualInterval(DoubleRange range, Size availableSize)
+ {
+ if (double.IsNaN(AxisInterval) || AxisInterval == 0)
+ {
+ return CalculateNiceInterval(range, availableSize);
+ }
+
+ return AxisInterval;
+ }
+
+ [RequiresUnreferencedCode("The GenerateVisibleLabels is not trim compatible")]
+ internal override void GenerateVisibleLabels()
+ {
+ if (VisibleRange.IsEmpty)
+ {
+ return;
+ }
+
+ _isOverriddenOnCreateLabelsMethod = ChartUtils.IsOverriddenMethod(this, "OnCreateLabels");
+
+ DoubleRange visibleRange = VisibleRange;
+ double actualInterval = ActualInterval;
+ double interval = (AxisInterval != 0 && !double.IsNaN(AxisInterval)) ? AxisInterval : ActualInterval;
+ double position = visibleRange.Start - (visibleRange.Start % actualInterval);
+ var actualSeries = GetActualSeries();
+
+ if (actualSeries == null)
+ {
+ return;
+ }
+
+ if (actualSeries.ActualXValues is List xValues)
+ {
+ switch (ActualIntervalType)
+ {
+ case DateTimeIntervalType.Years:
+ AddYearTypeLabels(xValues, position, interval, _isOverriddenOnCreateLabelsMethod);
+ break;
+ case DateTimeIntervalType.Months:
+ AddMonthTypeLabels(xValues, position, interval, _isOverriddenOnCreateLabelsMethod);
+ break;
+ case DateTimeIntervalType.Days:
+ AddDayTypeLabels(xValues, position, interval, _isOverriddenOnCreateLabelsMethod);
+ break;
+ case DateTimeIntervalType.Hours:
+ AddHourTypeLabels(xValues, position, interval, _isOverriddenOnCreateLabelsMethod);
+ break;
+ case DateTimeIntervalType.Minutes:
+ AddMinutesTypeLabels(xValues, position, interval, _isOverriddenOnCreateLabelsMethod);
+ break;
+ case DateTimeIntervalType.Seconds:
+ AddSecondTypeLabels(xValues, position, interval, _isOverriddenOnCreateLabelsMethod);
+ break;
+ case DateTimeIntervalType.Milliseconds:
+ AddMillisecondTypeLabels(xValues, position, interval, _isOverriddenOnCreateLabelsMethod);
+ break;
+ case DateTimeIntervalType.Auto:
+ AddAutoTypeLabels(xValues, position, interval, _isOverriddenOnCreateLabelsMethod);
+ break;
+ }
+ }
+
+ if (_isOverriddenOnCreateLabelsMethod)
+ {
+ OnCreateLabels();
+ AddVisibleLabels();
+ }
+ }
+
+ #endregion
+
+ #region Internal Methods
+
+ internal string GetLabelContent(double position, string labelFormat)
+ {
+ var actualSeries = GetActualSeries();
+ if (actualSeries != null)
+ {
+ labelFormat = LabelStyle.LabelFormat ?? GetSpecificFormattedLabel(ActualIntervalType);
+ if (actualSeries.ActualXValues is List xValues && position < xValues.Count && position >= 0)
+ {
+ double xDateTime = xValues[(int)position];
+ return GetFormattedAxisLabel(labelFormat, xDateTime);
+ }
+ }
+
+ return position.ToString();
+ }
+
+ #endregion
+
+ #region Callback Method
+
+ private static void OnIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is DateTimeCategoryAxis dateTimeCategoryAxis)
+ {
+ dateTimeCategoryAxis.UpdateAxisInterval((double)newValue);
+ dateTimeCategoryAxis.UpdateLayout();
+ }
+ }
+
+ private static void OnIntervalTypePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is DateTimeCategoryAxis dateTimeCategoryAxis)
+ {
+ dateTimeCategoryAxis.UpdateActualIntervalType((DateTimeIntervalType)newValue);
+ dateTimeCategoryAxis.UpdateLayout();
+ }
+ }
+
+ void UpdateActualIntervalType(DateTimeIntervalType intervalType)
+ {
+ ActualIntervalType = intervalType;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ private double CalculateIntervalForAutoType(SizeF availableSize, TimeSpan timeSpan)
+ {
+ var intervalTypes = new List<(double RangeDivision, DateTimeIntervalType IntervalType)>
+ {
+ (timeSpan.TotalDays / 365, DateTimeIntervalType.Years),
+ (timeSpan.TotalDays / 30, DateTimeIntervalType.Months),
+ (timeSpan.TotalDays, DateTimeIntervalType.Days),
+ (timeSpan.TotalHours, DateTimeIntervalType.Hours),
+ (timeSpan.TotalMinutes, DateTimeIntervalType.Minutes),
+ (timeSpan.TotalSeconds, DateTimeIntervalType.Seconds),
+ (timeSpan.TotalMilliseconds, DateTimeIntervalType.Milliseconds)
+ };
+
+ double interval = 0;
+
+ // Before the loop starts, set ActualIntervalType to the last item in the list.
+ // If none meet the condition interval >= 1, it remains as it is the last item.
+ ActualIntervalType = intervalTypes.Last().IntervalType;
+
+
+ foreach (var (rangeDivision, intervalType) in intervalTypes)
+ {
+ interval = base.CalculateNiceInterval(new DoubleRange(0, rangeDivision), availableSize);
+
+ //If any interval meets the condition interval >= 1, it will override the default.
+ if (interval >= 1)
+ {
+ ActualIntervalType = intervalType;
+ break;
+ }
+ }
+
+ return interval;
+ }
+
+ private void AddAutoTypeLabels(List xValues, double position, double interval, bool isOverriddenOnCreateLabelsMethod)
+ {
+ for (; position <= VisibleRange.End; position += interval)
+ {
+ if (VisibleRange.Inside(position) && position < xValues.Count && position > -1)
+ {
+ int pos = (int)Math.Round(position);
+ var value = xValues[pos];
+ AddVisibleLabels(value, pos, isOverriddenOnCreateLabelsMethod);
+ }
+ }
+ }
+
+ private void AddMillisecondTypeLabels(List xValues, double position, double interval, bool isOverriddenOnCreateLabelsMethod)
+ {
+ var xStartValue = DateTime.FromOADate(xValues[0]);
+ int hour = xStartValue.Hour,
+ mins = xStartValue.Minute,
+ secs = xStartValue.Second,
+ millisecs = xStartValue.Millisecond;
+ DateTime date = xStartValue.Date;
+
+ for (; position <= VisibleRange.End; position++)
+ {
+ int pos = (int)Math.Round(position);
+ if (VisibleRange.Inside(position) && pos > -1 && pos < xValues.Count)
+ {
+ var xValue = DateTime.FromOADate(xValues[pos]);
+ if (xValue.Date > date)
+ {
+ date = xValue.Date;
+ hour = xValue.Hour;
+ }
+
+ if (xValue.Date == date)
+ {
+ if (xValue.Hour > hour)
+ {
+ hour = xValue.Hour;
+ mins = xValue.Minute;
+ }
+
+ if (xValue.Hour == hour)
+ {
+ if (xValue.Minute > mins)
+ {
+ mins = xValue.Minute;
+ secs = xValue.Second;
+ }
+
+ if (xValue.Minute == mins)
+ {
+ if (xValue.Second > secs)
+ {
+ secs = xValue.Second;
+ millisecs = xValue.Millisecond;
+ }
+
+ if (xValue.Second == secs)
+ {
+ if (xValue.Millisecond > millisecs)
+ {
+ millisecs = xValue.Millisecond;
+ }
+
+ if (xValue.Millisecond == millisecs)
+ {
+ var value = xValues[pos];
+ AddVisibleLabels(value, pos, isOverriddenOnCreateLabelsMethod);
+ hour = xValue.AddMilliseconds((int)interval).Hour;
+ mins = xValue.AddMilliseconds((int)interval).Minute;
+ date = xValue.AddMilliseconds((int)interval).Date;
+ secs = xValue.AddMilliseconds((int)interval).Second;
+ millisecs = xValue.AddMilliseconds((int)interval).Millisecond;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void AddSecondTypeLabels(List xValues, double position, double interval, bool isOverriddenOnCreateLabelsMethod)
+ {
+ var xStartValue = DateTime.FromOADate(xValues[0]);
+ int hour = xStartValue.Hour,
+ mins = xStartValue.Minute,
+ secs = xStartValue.Second;
+ DateTime date = xStartValue.Date;
+
+ for (; position <= VisibleRange.End; position++)
+ {
+ int pos = (int)Math.Round(position);
+ if (VisibleRange.Inside(position) && pos > -1 && pos < xValues.Count)
+ {
+ var xValue = DateTime.FromOADate(xValues[pos]);
+ if (xValue.Date > date)
+ {
+ date = xValue.Date;
+ hour = xValue.Hour;
+ }
+
+ if (xValue.Date == date)
+ {
+ if (xValue.Hour > hour)
+ {
+ hour = xValue.Hour;
+ mins = xValue.Minute;
+ }
+
+ if (xValue.Hour == hour)
+ {
+ if (xValue.Minute > mins)
+ {
+ mins = xValue.Minute;
+ secs = xValue.Second;
+ }
+
+ if (xValue.Minute == mins)
+ {
+ if (xValue.Second > secs)
+ {
+ secs = xValue.Second;
+ }
+
+ if (xValue.Second == secs)
+ {
+ var value = xValues[pos];
+ AddVisibleLabels(value, pos, isOverriddenOnCreateLabelsMethod);
+ hour = xValue.AddSeconds((int)interval).Hour;
+ mins = xValue.AddSeconds((int)interval).Minute;
+ date = xValue.AddSeconds((int)interval).Date;
+ secs = xValue.AddSeconds((int)interval).Second;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void AddMinutesTypeLabels(List xValues, double position, double interval, bool isOverriddenOnCreateLabelsMethod)
+ {
+ var xStartValue = DateTime.FromOADate(xValues[0]);
+ int hour = xStartValue.Hour, mins = xStartValue.Minute;
+ DateTime date = xStartValue.Date;
+
+ for (; position <= VisibleRange.End; position++)
+ {
+ int pos = (int)Math.Round(position);
+
+ if (VisibleRange.Inside(position) && pos > -1 && pos < xValues.Count)
+ {
+ var xValue = DateTime.FromOADate(xValues[pos]);
+ if (xValue.Date > date)
+ {
+ date = xValue.Date;
+ hour = xValue.Hour;
+ }
+
+ if (xValue.Date == date)
+ {
+ if (xValue.Hour > hour)
+ {
+ hour = xValue.Hour;
+ mins = xValue.Minute;
+ }
+
+ if (xValue.Hour == hour)
+ {
+ if (xValue.Minute > mins)
+ {
+ mins = xValue.Minute;
+ }
+
+ if (xValue.Minute == mins)
+ {
+ var value = xValues[pos];
+ AddVisibleLabels(value, pos, isOverriddenOnCreateLabelsMethod);
+ hour = xValue.AddMinutes((int)interval).Hour;
+ mins = xValue.AddMinutes((int)interval).Minute;
+ date = xValue.AddMinutes((int)interval).Date;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void AddHourTypeLabels(List xValues, double position, double interval, bool isOverriddenOnCreateLabelsMethod)
+ {
+ var xStartValue = DateTime.FromOADate(xValues[0]);
+ int hour = xStartValue.Hour;
+ DateTime date = xStartValue.Date;
+
+ for (; position <= VisibleRange.End; position++)
+ {
+ int pos = (int)Math.Round(position);
+ if (VisibleRange.Inside(position) && pos > -1 && pos < xValues.Count)
+ {
+ var xValue = DateTime.FromOADate(xValues[pos]);
+ if (xValue.Date > date)
+ {
+ date = xValue.Date;
+ hour = xValue.Hour;
+ }
+
+ if (xValue.Date == date)
+ {
+ if (xValue.Hour > hour)
+ {
+ hour = xValue.Hour;
+ }
+
+ if (xValue.Hour == hour)
+ {
+ var value = xValues[pos];
+ AddVisibleLabels(value, pos, isOverriddenOnCreateLabelsMethod);
+ hour = xValue.AddHours((int)interval).Hour;
+ date = xValue.AddHours((int)interval).Date;
+ }
+ }
+ }
+ }
+ }
+
+ private void AddDayTypeLabels(List xValues, double position, double interval, bool isOverriddenOnCreateLabelsMethod)
+ {
+ var xStartValue = DateTime.FromOADate(xValues[0]);
+ DateTime date = xStartValue.Date;
+
+ for (; position <= VisibleRange.End; position++)
+ {
+ int pos = (int)Math.Round(position);
+ if (VisibleRange.Inside(position) && pos > -1 && pos < xValues.Count)
+ {
+ var xValue = DateTime.FromOADate(xValues[pos]);
+ if (xValue.Date > date)
+ {
+ date = xValue.Date;
+ }
+
+ if (xValue.Date == date)
+ {
+ var value = xValues[pos];
+ AddVisibleLabels(value, pos, isOverriddenOnCreateLabelsMethod);
+ date = xValue.AddDays((int)interval).Date;
+ }
+ }
+ }
+ }
+
+ private void AddMonthTypeLabels(List xValues, double position, double interval, bool isOverriddenOnCreateLabelsMethod)
+ {
+ var xStartValue = DateTime.FromOADate(xValues[0]);
+ int year = xStartValue.Year, month = xStartValue.Month;
+
+ for (; position <= VisibleRange.End; position++)
+ {
+ int pos = (int)Math.Round(position);
+ if (VisibleRange.Inside(position) && pos > -1 && pos < xValues.Count)
+ {
+ var xValue = DateTime.FromOADate(xValues[pos]);
+ if (xValue.Year > year)
+ {
+ year = xValue.Year;
+ month = xValue.Month;
+ }
+
+ if (xValue.Year == year)
+ {
+ if (xValue.Month > month)
+ {
+ month = xValue.Month;
+ }
+
+ if (xValue.Month == month)
+ {
+ var value = xValues[pos];
+ AddVisibleLabels(value, pos, isOverriddenOnCreateLabelsMethod);
+ month = xValue.AddMonths((int)interval).Month;
+ year = xValue.AddMonths((int)interval).Year;
+ }
+ }
+ }
+ }
+ }
+
+ private void AddYearTypeLabels(List xValues, double position, double interval, bool isOverriddenOnCreateLabelsMethod)
+ {
+ var xStartValue = DateTime.FromOADate(xValues[0]);
+ int year = xStartValue.Year;
+
+ for (; position <= VisibleRange.End; position++)
+ {
+ int pos = (int)Math.Round(position);
+ if (VisibleRange.Inside(position) && pos > -1 && pos < xValues.Count)
+ {
+ var xValue = DateTime.FromOADate(xValues[pos]);
+ if (xValue.Year > year)
+ {
+ year = xValue.Year;
+ }
+
+ if (xValue.Year == year)
+ {
+ var value = xValues[pos];
+ AddVisibleLabels(value, pos, _isOverriddenOnCreateLabelsMethod);
+ year = xValue.AddYears((int)interval).Year;
+ }
+ }
+ }
+ }
+
+ private void AddVisibleLabels(double value, int pos, bool isOverriddenOnCreateLabelsMethod)
+ {
+ var labelFormat = LabelStyle.LabelFormat ?? GetSpecificFormattedLabel(ActualIntervalType);
+ var labelContent = GetFormattedAxisLabel(labelFormat, value);
+ var axisLabel = new ChartAxisLabel(pos, labelContent);
+ VisibleLabels.Add(axisLabel);
+
+ if (!_isOverriddenOnCreateLabelsMethod)
+ {
+ TickPositions.Add(pos);
+ }
+ }
+
+ private ChartSeries? GetActualSeries()
+ {
+ var visibleSeries = GetVisibleSeries();
+ if (visibleSeries == null)
+ {
+ return null;
+ }
+
+ int dataCount = 0;
+ ChartSeries? selectedSeries = null;
+
+ if (IsPolarArea)
+ {
+ foreach (PolarSeries series in visibleSeries.Cast())
+ {
+ if (series != null && series.ActualXAxis == this && series.PointsCount > dataCount)
+ {
+ selectedSeries = series;
+ dataCount = series.PointsCount;
+ }
+ }
+ }
+ else
+ {
+
+ foreach (CartesianSeries series in visibleSeries.Cast())
+ {
+ if (series != null && series.ActualXAxis == this && series.PointsCount > dataCount)
+ {
+ selectedSeries = series;
+ dataCount = series.PointsCount;
+ }
+ }
+ }
+
+ return selectedSeries;
+ }
+
+ #endregion
+
+ #endregion
+}
diff --git a/maui/src/Charts/Behaviors/ChartTrackballBehavior.cs b/maui/src/Charts/Behaviors/ChartTrackballBehavior.cs
index 6fd504ea..87a4d8b6 100644
--- a/maui/src/Charts/Behaviors/ChartTrackballBehavior.cs
+++ b/maui/src/Charts/Behaviors/ChartTrackballBehavior.cs
@@ -2053,39 +2053,36 @@ static string GetAxisLabel(ChartAxis axis, double xValue, string labelFormat)
{
string label = string.Empty;
- if (axis is CategoryAxis categoryAxis)
+ switch (axis)
{
- var currSeries = categoryAxis.GetActualSeries();
- if (currSeries != null)
- {
- label = categoryAxis.GetLabelContent(currSeries, (int)Math.Round(xValue), labelFormat);
- }
- }
- else if (axis is NumericalAxis)
- {
- label = ((int)Math.Round(xValue)).ToString(labelFormat);
- }
- else if (axis is LogarithmicAxis)
- {
- label = ChartAxis.GetActualLabelContent(xValue, labelFormat).ToString();
- }
- else if (axis is DateTimeAxis datetimeAxis)
- {
- string format;
- if (labelFormat != null)
- {
- format = labelFormat;
- }
- else
- {
- format = ChartAxis.GetSpecificFormattedLabel(datetimeAxis.ActualIntervalType);
- }
+ case CategoryAxis categoryAxis:
+ var currSeries = categoryAxis.GetActualSeries();
+ if (currSeries != null)
+ {
+ label = categoryAxis.GetLabelContent(currSeries, (int)Math.Round(xValue), labelFormat);
+ }
+ break;
- label = ChartAxis.GetFormattedAxisLabel(format, xValue);
- }
- else
- {
- label = ChartAxis.GetActualLabelContent(xValue, labelFormat);
+ case DateTimeCategoryAxis dateTimeCategoryAxis:
+ label = dateTimeCategoryAxis.GetLabelContent(xValue, labelFormat);
+ break;
+
+ case NumericalAxis:
+ label = ((int)Math.Round(xValue)).ToString(labelFormat);
+ break;
+
+ case LogarithmicAxis:
+ label = ChartAxis.GetActualLabelContent(xValue, labelFormat).ToString();
+ break;
+
+ case DateTimeAxis datetimeAxis:
+ string format = labelFormat ?? ChartAxis.GetSpecificFormattedLabel(datetimeAxis.ActualIntervalType);
+ label = ChartAxis.GetFormattedAxisLabel(format, xValue);
+ break;
+
+ default:
+ label = ChartAxis.GetActualLabelContent(xValue, labelFormat);
+ break;
}
return label;
diff --git a/maui/src/Charts/Behaviors/DataPointSelectionBehavior.cs b/maui/src/Charts/Behaviors/DataPointSelectionBehavior.cs
index d4468dfe..7c15af40 100644
--- a/maui/src/Charts/Behaviors/DataPointSelectionBehavior.cs
+++ b/maui/src/Charts/Behaviors/DataPointSelectionBehavior.cs
@@ -15,22 +15,18 @@ public partial class DataPointSelectionBehavior : ChartSelectionBehavior
internal bool OnTapped(float pointX, float pointY)
{
- if (Source != null)
+ if (Source is ChartSeries series)
{
- RectF bounds = Source.AreaBounds;
- foreach (var segment in Source.Segments)
+ int index;
+
+ if (series.UpdateDataPointSelection(pointX, pointY, out index))
{
- if (segment.HitTest(pointX - bounds.Left, pointY - bounds.Top))
+ if (IsSelectionChangingInvoked(series, index))
{
- var index = segment.Index;
- if (IsSelectionChangingInvoked(Source, index))
- {
- UpdateSelectionChanging(index, true);
- InvokeSelectionChangedEvent(Source, index);
- }
-
- return true;
+ UpdateSelectionChanging(index, true);
+ InvokeSelectionChangedEvent(series, index);
}
+ return true;
}
}
diff --git a/maui/src/Charts/Behaviors/SeriesSelectionBehavior.cs b/maui/src/Charts/Behaviors/SeriesSelectionBehavior.cs
index ef593595..9dd10771 100644
--- a/maui/src/Charts/Behaviors/SeriesSelectionBehavior.cs
+++ b/maui/src/Charts/Behaviors/SeriesSelectionBehavior.cs
@@ -34,20 +34,17 @@ internal bool OnTapped(float pointX, float pointY)
{
foreach (var series in visibleSeries.Reverse())
{
- RectF bounds = series.AreaBounds;
- foreach (var segment in series._segments)
+ var index = visibleSeries.IndexOf(series);
+
+ if (series.UpdateSeriesSelection(pointX, pointY))
{
- if (segment.HitTest(pointX - bounds.Left, pointY - bounds.Top))
+ if (IsSelectionChangingInvoked(Chart, index))
{
- var index = visibleSeries.IndexOf(series);
- if (IsSelectionChangingInvoked(Chart, index))
- {
- UpdateSelectionChanging(index, true);
- InvokeSelectionChangedEvent(Chart, index);
- }
-
- return true;
+ UpdateSelectionChanging(index, true);
+ InvokeSelectionChangedEvent(Chart, index);
}
+
+ return true;
}
}
}
diff --git a/maui/src/Charts/Resources/SfCartesianChartResources.cs b/maui/src/Charts/Resources/SfCartesianChartResources.cs
index f486e59b..e5cb632b 100644
--- a/maui/src/Charts/Resources/SfCartesianChartResources.cs
+++ b/maui/src/Charts/Resources/SfCartesianChartResources.cs
@@ -1,92 +1,44 @@
using Syncfusion.Maui.Toolkit.Localization;
+using System.Globalization;
namespace Syncfusion.Maui.Toolkit.Charts
{
+
///
- /// Localization resource for .
+ /// Represents the localization resource accessor for the control.
+ /// This class is responsible for retrieving culture-specific localized strings based on the application's current UI culture settings.
///
public class SfCartesianChartResources : LocalizationResourceAccessor
{
- internal static string High
- {
- get
- {
- var high = GetString("High");
- return string.IsNullOrEmpty(high) ? "High" : high;
- }
- }
+ internal static CultureInfo CultureInfo => CultureInfo.CurrentUICulture;
- internal static string Low
+ private static string GetResourceString(string key, string defaultValue)
{
- get
+ string? value = string.Empty;
+ if (ResourceManager != null)
{
- var low = GetString("Low");
- return string.IsNullOrEmpty(low) ? "Low" : low;
+ Culture = CultureInfo;
+ value = GetString(key);
}
+ return string.IsNullOrEmpty(value) ? defaultValue : value;
}
- internal static string Open
- {
- get
- {
- var open = GetString("Open");
- return string.IsNullOrEmpty(open) ? "Open" : open;
- }
- }
+ internal static string High => GetResourceString("High", "High");
- internal static string Close
- {
- get
- {
- var close = GetString("Close");
- return string.IsNullOrEmpty(close) ? "Close" : close;
- }
- }
+ internal static string Low => GetResourceString("Low", "Low");
- internal static string Maximum
- {
- get
- {
- var maximum = GetString("Maximum");
- return string.IsNullOrEmpty(maximum) ? "Maximum" : maximum;
- }
- }
+ internal static string Open => GetResourceString("Open", "Open");
- internal static string Minimum
- {
- get
- {
- var minimum = GetString("Minimum");
- return string.IsNullOrEmpty(minimum) ? "Minimum" : minimum;
- }
- }
+ internal static string Close => GetResourceString("Close", "Close");
- internal static string Median
- {
- get
- {
- var median = GetString("Median");
- return string.IsNullOrEmpty(median) ? "Median" : median;
- }
- }
+ internal static string Maximum => GetResourceString("Maximum", "Maximum");
- internal static string Q3
- {
- get
- {
- var upperQuartile = GetString("Q3");
- return string.IsNullOrEmpty(upperQuartile) ? "Q3" : upperQuartile;
- }
- }
+ internal static string Minimum => GetResourceString("Minimum", "Minimum");
- internal static string Q1
- {
- get
- {
- var lowerQuartile = GetString("Q1");
- return string.IsNullOrEmpty(lowerQuartile) ? "Q1" : lowerQuartile;
- }
- }
+ internal static string Median => GetResourceString("Median", "Median");
+
+ internal static string Q3 => GetResourceString("Q3", "Q3");
+ internal static string Q1 => GetResourceString("Q1", "Q1");
}
-}
+}
\ No newline at end of file
diff --git a/maui/src/Charts/Resources/SfCircularChartResources.cs b/maui/src/Charts/Resources/SfCircularChartResources.cs
index 38e96a17..0418a01c 100644
--- a/maui/src/Charts/Resources/SfCircularChartResources.cs
+++ b/maui/src/Charts/Resources/SfCircularChartResources.cs
@@ -1,19 +1,29 @@
-using Syncfusion.Maui.Toolkit.Localization;
+using System.Globalization;
+using Syncfusion.Maui.Toolkit.Localization;
namespace Syncfusion.Maui.Toolkit.Charts
{
+
///
- /// Localization resource for .
+ /// Represents the localization resource accessor for the control.
+ /// This class is responsible for retrieving culture-specific localized strings based on the application's current UI culture settings.
///
public class SfCircularChartResources : LocalizationResourceAccessor
{
+ internal static CultureInfo CultureInfo => CultureInfo.CurrentUICulture;
+
internal static string Others
{
get
{
- var others = GetString("Others");
- return string.IsNullOrEmpty(others) ? "Others" : others;
+ string? value = string.Empty;
+ if (ResourceManager != null)
+ {
+ Culture = CultureInfo;
+ value = GetString("Others");
+ }
+ return string.IsNullOrEmpty(value) ? "Others" : value;
}
}
}
-}
+}
\ No newline at end of file
diff --git a/maui/src/Charts/Segment/CandleSegment.cs b/maui/src/Charts/Segment/CandleSegment.cs
index c7461726..66b986a5 100644
--- a/maui/src/Charts/Segment/CandleSegment.cs
+++ b/maui/src/Charts/Segment/CandleSegment.cs
@@ -33,7 +33,11 @@ internal override void SetData(double[] values, bool isFill, bool isBull)
XValue = values[7];
IsFill = isFill;
IsBull = isBull;
-
+ if (Open < Close)
+ {
+ (Open, Close) = (Close, Open);
+ }
+
series.XRange += DoubleRange.Union(XValue);
series.YRange += new DoubleRange(High, Low);
}
@@ -171,14 +175,13 @@ void Layout(CandleSeries series)
AnimationValuesCalculation(series);
}
- if (Left > Right)
+ if (series.ChartArea != null && series.ChartArea.SecondaryAxis != null)
{
- (Right, Left) = (Left, Right);
- }
- if (Top > Bottom)
- {
- (Bottom, Top) = (Top, Bottom);
+ if (Left > Right || series.ChartArea.SecondaryAxis.IsInversed)
+ {
+ (Right, Left) = (Left, Right);
+ }
}
}
else
diff --git a/maui/src/Charts/Segment/DoughnutSegment.cs b/maui/src/Charts/Segment/DoughnutSegment.cs
index 4b4f2bea..4a7ba175 100644
--- a/maui/src/Charts/Segment/DoughnutSegment.cs
+++ b/maui/src/Charts/Segment/DoughnutSegment.cs
@@ -7,13 +7,15 @@
public partial class DoughnutSegment : PieSegment
{
#region Fields
-
+
RectF _actualBounds, _currentBounds;
-
double _startAngle, _endAngle;
-
double _segmentRadius;
-
+ PointF _innerCurveStartPoint, _innerCurveStartMidpoint, _startCurveCeterPoint, _outerCurveStartMidPoint, _outerCurveStartPoint,
+ _innerCurveEndPoint, _innerCurveEndMidpoint, _outerCurveEndMidPoint, _outerCurveEndPoint, _endCurveCenterPoint;
+ float _explodeOffsetX, _explodeOffsetY;
+ bool _isExploded;
+
#endregion
#region Internal Properties
@@ -29,14 +31,14 @@ public partial class DoughnutSegment : PieSegment
///
protected internal override void OnLayout()
{
- if (Series is not DoughnutSeries || double.IsNaN(YValue))
+ if (Series is not DoughnutSeries series || double.IsNaN(YValue))
{
return;
}
//For calculating doughnut center angle.
MidAngle = (StartAngle + (EndAngle / 2)) * 0.0174532925f;
- UpdatePosition();
+ UpdatePosition(series);
}
///
@@ -66,36 +68,9 @@ protected internal override void Draw(ICanvas canvas)
}
}
- UpdatePosition();
+ UpdatePosition(series);
var endArcAngle = series._isClockWise ? drawStartAngle + drawEndAngle : drawEndAngle;
-
- PathF path = new PathF();
-
- //Drawing segment.
- path.AddArc(_actualBounds.Left, _actualBounds.Top, _actualBounds.Right, _actualBounds.Bottom, -drawStartAngle, -(endArcAngle), series._isClockWise);
- path.AddArc(_currentBounds.Left, _currentBounds.Top, _currentBounds.Right, _currentBounds.Bottom, -(endArcAngle), -drawStartAngle, !series._isClockWise);
- path.Close();
- canvas.SetFillPaint(Fill, path.Bounds);
- canvas.Alpha = Opacity;
- canvas.FillPath(path);
-
- //Drawing stroke.
- if (HasStroke)
- {
-#if WINDOWS
- //MAUI-582: Exception faced "Value not fall within expected range, due to path closed before stroke drawing"
- var radius = (float)series.InnerRadius * (Math.Min(_actualBounds.Width, _actualBounds.Height) / 2);
- var x = (float)(_currentBounds.X + (_currentBounds.Width / 2) + (radius * Math.Cos(drawStartAngle * (Math.PI / 180))));
- var y = (float)(_currentBounds.Y + (_currentBounds.Width / 2) + (radius * Math.Sin(drawStartAngle * (Math.PI / 180))));
- path.MoveTo(x, y);
-#endif
- path.AddArc(_actualBounds.Left, _actualBounds.Top, _actualBounds.Right, _actualBounds.Bottom, -drawStartAngle, -(endArcAngle), series._isClockWise);
- path.AddArc(_currentBounds.Left, _currentBounds.Top, _currentBounds.Right, _currentBounds.Bottom, -(endArcAngle), -drawStartAngle, !series._isClockWise);
- path.Close();
- canvas.StrokeColor = Stroke.ToColor();
- canvas.StrokeSize = (float)StrokeWidth;
- canvas.DrawPath(path);
- }
+ DrawSegment(canvas, series, drawStartAngle, endArcAngle);
}
#endregion
@@ -131,30 +106,46 @@ internal override int GetDataPointIndex(float x, float y)
#region private Methods
- void UpdatePosition()
+ void UpdatePosition(DoughnutSeries series)
{
- if (Series is not DoughnutSeries series)
- {
- return;
- }
-
var center = series.Center;
RectF seriesClipRect = series.AreaBounds;
double minScale = Math.Min(seriesClipRect.Width, seriesClipRect.Height) * series.Radius;
- _actualBounds = new RectF((float)((center.X * 2) - minScale) / 2, (float)((center.Y * 2) - minScale) / 2, (float)minScale, (float)minScale);
- _currentBounds = new RectF(_actualBounds.Left, _actualBounds.Top, _actualBounds.Width, _actualBounds.Height);
+
+ float halfMinScale = (float)minScale / 2;
+ float centerOffsetX = (float)(center.X - halfMinScale);
+ float centerOffsetY = (float)(center.Y - halfMinScale);
+
+ _actualBounds = new RectF(centerOffsetX, centerOffsetY, halfMinScale * 2, halfMinScale * 2);
+ _currentBounds = new RectF(_actualBounds.Location, _actualBounds.Size);
if (series.ExplodeIndex == Index || series.ExplodeAll)
{
+ if (!_isExploded)
+ {
+ _explodeOffsetX = 0;
+ _explodeOffsetY = 0;
+ }
+
float angle = series._isClockWise ? (float)((2f * (series.StartAngle + ((StartAngle - series.StartAngle) * series.AnimationValue)) * 0.0174532925f) + ((EndAngle * series.AnimationValue) * 0.0174532925f)) / 2f
- : (float)(((series.StartAngle + ((StartAngle - series.StartAngle) * series.AnimationValue)) * 0.0174532925f) +
- (EndAngle * series.AnimationValue * 0.0174532925f)) / 2;
- _actualBounds = _actualBounds.Offset((float)(series.ExplodeRadius * Math.Cos(angle)), (float)(series.ExplodeRadius * Math.Sin(angle)));
+ : (float)(((series.StartAngle + ((StartAngle - series.StartAngle) * series.AnimationValue)) * 0.0174532925f) + (EndAngle * series.AnimationValue * 0.0174532925f)) / 2;
+ _explodeOffsetX = (float)(series.ExplodeRadius * Math.Cos(angle));
+ _explodeOffsetY = (float)(series.ExplodeRadius * Math.Sin(angle));
+ _actualBounds = _actualBounds.Offset(_explodeOffsetX, _explodeOffsetY);
_currentBounds = _actualBounds;
+
+ _isExploded = true;
+ }
+ else if (_isExploded)
+ {
+ _explodeOffsetX = 0;
+ _explodeOffsetY = 0;
+ _isExploded = false;
}
InnerRadius = (float)series.InnerRadius * (Math.Min(_actualBounds.Width, _actualBounds.Height) / 2);
_currentBounds = new RectF(_currentBounds.X + (_currentBounds.Width / 2) - InnerRadius, _currentBounds.Y + (_currentBounds.Height / 2) - InnerRadius, 2 * InnerRadius, 2 * InnerRadius);
+
series.ActualRadius = (float)minScale / 2;
_segmentRadius = series.ActualRadius;
}
@@ -223,6 +214,218 @@ bool IsPointInDoughnutSegment(double radius, double x, double y)
return false;
}
+ void DrawSegment(ICanvas canvas, DoughnutSeries series, float drawStartAngle, float drawEndAngle)
+ {
+ PathF path = new PathF();
+ float outerRadius = (float)_segmentRadius;
+ float innerRadius = InnerRadius;
+ float midRadius = (float)(outerRadius + innerRadius) / 2;
+ float radius = (float)(outerRadius - innerRadius) / 2;
+ float deviationAngle = ChartUtils.CalculateAngleDeviation(midRadius, radius, 360);
+
+ if (series.CapStyle == CapStyle.BothFlat || series.VisibleSegmentCount == 1)
+ {
+ DrawFlatSegment(canvas, series, drawStartAngle, drawEndAngle, path);
+ }
+ else
+ {
+ float segmentStartAngle = drawStartAngle;
+ float segmentEndAngle = drawEndAngle;
+
+ if (series.CapStyle == CapStyle.StartCurve || series.CapStyle == CapStyle.BothCurve)
+ {
+ if ((drawStartAngle + deviationAngle) >= drawEndAngle && series.CapStyle == CapStyle.StartCurve)
+ {
+ deviationAngle = drawEndAngle - drawStartAngle;
+ }
+
+ if (series.CapStyle == CapStyle.BothCurve)
+ {
+ float segmentSweepAngle = drawEndAngle - drawStartAngle;
+ float maxDeviation = 2 * deviationAngle;
+
+ if (segmentSweepAngle < maxDeviation)
+ {
+ deviationAngle = segmentSweepAngle / 2;
+ }
+ }
+
+ segmentStartAngle += deviationAngle;
+ PointF[] startCurvePoints = CalculateCurvePoints(series, segmentStartAngle, drawStartAngle, innerRadius, outerRadius, midRadius);
+ _innerCurveStartPoint = startCurvePoints[0];
+ _innerCurveStartMidpoint = startCurvePoints[1];
+ _startCurveCeterPoint = startCurvePoints[2];
+ _outerCurveStartMidPoint = startCurvePoints[3];
+ _outerCurveStartPoint = startCurvePoints[4];
+
+ }
+
+ if (series.CapStyle == CapStyle.EndCurve || series.CapStyle == CapStyle.BothCurve)
+ {
+ if ((drawEndAngle - deviationAngle) <= drawStartAngle && series.CapStyle == CapStyle.EndCurve)
+ {
+ deviationAngle = drawEndAngle - drawStartAngle;
+ }
+
+ segmentEndAngle -= deviationAngle;
+ PointF[] endCurvePoints = CalculateCurvePoints(series, segmentEndAngle, drawEndAngle, innerRadius, outerRadius, midRadius);
+ _innerCurveEndPoint = endCurvePoints[0];
+ _innerCurveEndMidpoint = endCurvePoints[1];
+ _endCurveCenterPoint = endCurvePoints[2];
+ _outerCurveEndMidPoint = endCurvePoints[3];
+ _outerCurveEndPoint = endCurvePoints[4];
+ }
+
+ // To draw the start curve for the segment
+ if (series.CapStyle == CapStyle.StartCurve || series.CapStyle == CapStyle.BothCurve)
+ {
+ path.MoveTo(_innerCurveStartPoint);
+ path.CurveTo(_innerCurveStartPoint, _innerCurveStartMidpoint, _startCurveCeterPoint);
+ path.CurveTo(_startCurveCeterPoint, _outerCurveStartMidPoint, _outerCurveStartPoint);
+ }
+
+ if (NormalizeAngle(segmentStartAngle) != NormalizeAngle(segmentEndAngle))
+ {
+ // To draw the outer arc for the segment
+ path.AddArc(_actualBounds.Left, _actualBounds.Top, _actualBounds.Right, _actualBounds.Bottom, -segmentStartAngle, -(segmentEndAngle), series._isClockWise);
+
+ // To draw the inner arc for the segment
+ path.AddArc(_currentBounds.Left, _currentBounds.Top, _currentBounds.Right, _currentBounds.Bottom, -(segmentEndAngle), -segmentStartAngle, !series._isClockWise);
+ }
+
+ // To draw the end curve for the segment
+ if (series.CapStyle == CapStyle.EndCurve || series.CapStyle == CapStyle.BothCurve)
+ {
+ path.MoveTo(_innerCurveEndPoint);
+ path.CurveTo(_innerCurveEndPoint, _innerCurveEndMidpoint, _endCurveCenterPoint);
+ path.CurveTo(_endCurveCenterPoint, _outerCurveEndMidPoint, _outerCurveEndPoint);
+ }
+
+ canvas.SetFillPaint(Fill, path.Bounds);
+ canvas.Alpha = Opacity;
+ canvas.FillPath(path);
+ SegmentStrokePath(canvas, series, segmentStartAngle, segmentEndAngle, drawStartAngle);
+ }
+ }
+
+ void DrawFlatSegment(ICanvas canvas, DoughnutSeries series, float drawStartAngle, float drawEndAngle, PathF path)
+ {
+ var radius = (float)series.InnerRadius * (Math.Min(_actualBounds.Width, _actualBounds.Height) / 2);
+ var x = (float)(_currentBounds.X + (_currentBounds.Width / 2) + (radius * Math.Cos(drawStartAngle * (Math.PI / 180))));
+ var y = (float)(_currentBounds.Y + (_currentBounds.Width / 2) + (radius * Math.Sin(drawStartAngle * (Math.PI / 180))));
+
+ path.MoveTo(x, y);
+ path.AddArc(_actualBounds.Left, _actualBounds.Top, _actualBounds.Right, _actualBounds.Bottom, -drawStartAngle, -(drawEndAngle), series._isClockWise);
+ path.AddArc(_currentBounds.Left, _currentBounds.Top, _currentBounds.Right, _currentBounds.Bottom, -(drawEndAngle), -drawStartAngle, !series._isClockWise);
+ path.Close();
+ canvas.SetFillPaint(Fill, path.Bounds);
+ canvas.Alpha = Opacity;
+ canvas.FillPath(path);
+
+ if (HasStroke)
+ {
+ canvas.StrokeColor = Stroke.ToColor();
+ canvas.StrokeSize = (float)StrokeWidth;
+ canvas.DrawPath(path);
+ }
+ }
+
+ PointF[] CalculateCurvePoints(DoughnutSeries series, float segmentAngle, float arcAngle, float innerRadius, float outerRadius, float midRadius)
+ {
+ // Convert degrees to radians
+ const double DegreeToRadian = Math.PI / 180;
+ double angleRadius = segmentAngle * DegreeToRadian;
+ double arcRadius = arcAngle * DegreeToRadian;
+
+ var center = series.Center;
+
+ // Calculate points using a helper function for readability
+ PointF innerPoint = ComputePoint(center, innerRadius, angleRadius);
+ PointF outerPoint = ComputePoint(center, outerRadius, angleRadius);
+ PointF centerPoint = ComputePoint(center, midRadius, arcRadius);
+ PointF innerMidPoint = ComputePoint(center, innerRadius, arcRadius);
+ PointF outerMidPoint = ComputePoint(center, outerRadius, arcRadius);
+
+ return [innerPoint, innerMidPoint, centerPoint, outerMidPoint, outerPoint];
+ }
+
+ private PointF ComputePoint(PointF center, float radius, double angle)
+ {
+ return new PointF(
+ (float)(center.X + (radius * Math.Cos(angle)) + _explodeOffsetX),
+ (float)(center.Y + (radius * Math.Sin(angle)) + _explodeOffsetY)
+ );
+ }
+
+ void SegmentStrokePath(ICanvas canvas, DoughnutSeries series, float segmentStartAngle, float segmentEndAngle, float segmentAngle)
+ {
+ if (HasStroke)
+ {
+ PathF strokePath = new PathF();
+
+ // To draw the start curve for the segment stroke
+ if (series.CapStyle == CapStyle.BothCurve || series.CapStyle == CapStyle.StartCurve)
+ {
+ strokePath.MoveTo(_innerCurveStartPoint);
+ strokePath.CurveTo(_innerCurveStartPoint, _innerCurveStartMidpoint, _startCurveCeterPoint);
+ strokePath.CurveTo(_startCurveCeterPoint, _outerCurveStartMidPoint, _outerCurveStartPoint);
+ }
+
+ if (NormalizeAngle(segmentStartAngle) != NormalizeAngle(segmentEndAngle))
+ {
+ // To draw the outer arc for the segment stroke
+ strokePath.AddArc(_actualBounds.Left, _actualBounds.Top, _actualBounds.Right, _actualBounds.Bottom, -segmentStartAngle, -(segmentEndAngle), series._isClockWise);
+ }
+
+ // To draw the end curve for the segment stroke
+ if (series.CapStyle == CapStyle.EndCurve || series.CapStyle == CapStyle.BothCurve)
+ {
+ strokePath.MoveTo(_innerCurveEndPoint);
+ strokePath.CurveTo(_innerCurveEndPoint, _innerCurveEndMidpoint, _endCurveCenterPoint);
+ strokePath.CurveTo(_endCurveCenterPoint, _outerCurveEndMidPoint, _outerCurveEndPoint);
+ strokePath.MoveTo(_innerCurveEndPoint);
+ }
+
+ if (NormalizeAngle(segmentStartAngle) != NormalizeAngle(segmentEndAngle))
+ {
+ // To draw the inner arc for the segment stroke
+ strokePath.AddArc(_currentBounds.Left, _currentBounds.Top, _currentBounds.Right, _currentBounds.Bottom, -(segmentEndAngle), -segmentStartAngle, !series._isClockWise);
+ }
+
+ if (NormalizeAngle(segmentStartAngle) == NormalizeAngle(segmentEndAngle) && series.CapStyle == CapStyle.StartCurve)
+ {
+ strokePath.Close();
+ }
+
+ canvas.StrokeColor = Stroke.ToColor();
+ canvas.StrokeLineCap = LineCap.Round;
+ canvas.StrokeSize = (float)StrokeWidth;
+ canvas.DrawPath(strokePath);
+
+ if (series.CapStyle == CapStyle.EndCurve)
+ {
+ double angleRad = segmentAngle * (Math.PI / 180);
+ var innerPoint = new PointF((float)(series.Center.X + (InnerRadius * Math.Cos(angleRad)) + _explodeOffsetX), (float)(series.Center.Y + (InnerRadius * Math.Sin(angleRad)) + _explodeOffsetY));
+ var outerPoint = new PointF((float)(series.Center.X + (_segmentRadius * Math.Cos(angleRad)) + _explodeOffsetX), (float)(series.Center.Y + (_segmentRadius * Math.Sin(angleRad)) + _explodeOffsetY));
+
+ canvas.StrokeLineCap = LineCap.Round;
+
+ //To draw line for the segment stroke
+ canvas.DrawLine(innerPoint, outerPoint);
+ }
+ }
+ }
+
+ // Normalizes angle precision to avoid rendering artifacts specific to platform.
+ static float NormalizeAngle(float angle)
+ {
+#if IOS || MACCATALYST
+ return MathF.Round(angle, 4);
+#else
+ return MathF.Round(angle, 5);
+#endif
+ }
+
#endregion
#endregion
diff --git a/maui/src/Charts/Series/BoxAndWhiskerSeries.cs b/maui/src/Charts/Series/BoxAndWhiskerSeries.cs
index 3db4af97..ad2fbfbf 100644
--- a/maui/src/Charts/Series/BoxAndWhiskerSeries.cs
+++ b/maui/src/Charts/Series/BoxAndWhiskerSeries.cs
@@ -1099,31 +1099,17 @@ internal override void GenerateSegments(SeriesView seriesView)
actualMaximum = Math.Max(outliers.Max(), actualMaximum);
}
- if (IsIndexed && IsGrouped && xValues != null)
+ if (xValues != null)
{
+ var x = xValues[i];
+
if (i < _segments.Count && _segments[i] is BoxAndWhiskerSegment segment)
{
- segment.SetData([i + SbsInfo.Start, i + SbsInfo.End, actualMinimum, minimum, lowerQuartile, median, upperQuartile, maximum, actualMaximum, xValues[i] + SbsInfo.Median, average]);
+ segment.SetData([x + SbsInfo.Start, x + SbsInfo.End, actualMinimum, minimum, lowerQuartile, median, upperQuartile, maximum, actualMaximum, xValues[i] + SbsInfo.Median, average]);
}
else
{
- CreateSegment(seriesView, [i + SbsInfo.Start, i + SbsInfo.End, actualMinimum, minimum, lowerQuartile, median, upperQuartile, maximum, actualMaximum, xValues[i] + SbsInfo.Median, average], i, outliers);
- }
- }
- else
- {
- if (xValues != null)
- {
- var x = xValues[i];
-
- if (i < _segments.Count && _segments[i] is BoxAndWhiskerSegment segment)
- {
- segment.SetData([x + SbsInfo.Start, x + SbsInfo.End, actualMinimum, minimum, lowerQuartile, median, upperQuartile, maximum, actualMaximum, xValues[i] + SbsInfo.Median, average]);
- }
- else
- {
- CreateSegment(seriesView, [x + SbsInfo.Start, x + SbsInfo.End, actualMinimum, minimum, lowerQuartile, median, upperQuartile, maximum, actualMaximum, xValues[i] + SbsInfo.Median, average], i, outliers);
- }
+ CreateSegment(seriesView, [x + SbsInfo.Start, x + SbsInfo.End, actualMinimum, minimum, lowerQuartile, median, upperQuartile, maximum, actualMaximum, xValues[i] + SbsInfo.Median, average], i, outliers);
}
}
diff --git a/maui/src/Charts/Series/CartesianSeries.cs b/maui/src/Charts/Series/CartesianSeries.cs
index 42d676bd..5e1e23dc 100644
--- a/maui/src/Charts/Series/CartesianSeries.cs
+++ b/maui/src/Charts/Series/CartesianSeries.cs
@@ -28,20 +28,7 @@ public abstract class CartesianSeries : ChartSeries
internal bool IsIndexed
{
- get { return ActualXAxis is CategoryAxis; }
- }
-
- internal bool IsGrouped
- {
- get
- {
- if (ActualXValues is CategoryAxis category)
- {
- return category.ArrangeByIndex;
- }
-
- return false;
- }
+ get { return ActualXAxis is CategoryAxis || ActualXAxis is DateTimeCategoryAxis; }
}
internal DoubleRange SbsInfo { get; set; } = DoubleRange.Empty;
diff --git a/maui/src/Charts/Series/ChartSeries.cs b/maui/src/Charts/Series/ChartSeries.cs
index 496b7ac0..ffe8d428 100644
--- a/maui/src/Charts/Series/ChartSeries.cs
+++ b/maui/src/Charts/Series/ChartSeries.cs
@@ -1665,6 +1665,37 @@ internal virtual void UpdateLegendItemToggle()
{
}
+ internal virtual bool UpdateDataPointSelection(float pointX, float pointY, out int index)
+ {
+ RectF bounds = AreaBounds;
+ index = -1;
+
+ foreach (var segment in _segments)
+ {
+ if (segment.HitTest(pointX - bounds.Left, pointY - bounds.Top))
+ {
+ index = segment.Index;
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ internal virtual bool UpdateSeriesSelection(float pointX, float pointY)
+ {
+ RectF bounds = AreaBounds;
+ foreach (var segment in _segments)
+ {
+ if (segment.HitTest(pointX - bounds.Left, pointY - bounds.Top))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
internal virtual void UpdateLegendIconColor(ChartSelectionBehavior sender, int index)
{
}
diff --git a/maui/src/Charts/Series/ColumnSeries.cs b/maui/src/Charts/Series/ColumnSeries.cs
index 0fd40813..f34e89b1 100644
--- a/maui/src/Charts/Series/ColumnSeries.cs
+++ b/maui/src/Charts/Series/ColumnSeries.cs
@@ -380,37 +380,19 @@ internal override void GenerateSegments(SeriesView seriesView)
double.IsNaN(axis.ActualCrossingValue) ? 0 : axis.ActualCrossingValue;
}
- if (IsGrouped && (IsIndexed || xValues == null))
+ for (var i = 0; i < PointsCount; i++)
{
- for (var i = 0; i < PointsCount; i++)
+ if (xValues != null)
{
+ var x = xValues[i];
+
if (i < _segments.Count && _segments[i] is ColumnSegment segment)
{
- segment.SetData([i + SbsInfo.Start, i + SbsInfo.End, YValues[i], bottomValue, i, YValues[i]]);
+ segment.SetData([x + SbsInfo.Start, x + SbsInfo.End, YValues[i], bottomValue, x, YValues[i]]);
}
else
{
- CreateSegment(seriesView, [i + SbsInfo.Start, i + SbsInfo.End, YValues[i], bottomValue, i, YValues[i]], i);
- }
- }
- }
- else
- {
- for (var i = 0; i < PointsCount; i++)
- {
- if (xValues != null)
-
- {
- var x = xValues[i];
-
- if (i < _segments.Count && _segments[i] is ColumnSegment segment)
- {
- segment.SetData([x + SbsInfo.Start, x + SbsInfo.End, YValues[i], bottomValue, x, YValues[i]]);
- }
- else
- {
- CreateSegment(seriesView, [x + SbsInfo.Start, x + SbsInfo.End, YValues[i], bottomValue, x, YValues[i]], i);
- }
+ CreateSegment(seriesView, [x + SbsInfo.Start, x + SbsInfo.End, YValues[i], bottomValue, x, YValues[i]], i);
}
}
}
diff --git a/maui/src/Charts/Series/DoughnutSeries.cs b/maui/src/Charts/Series/DoughnutSeries.cs
index a1906d8d..c669b4e4 100644
--- a/maui/src/Charts/Series/DoughnutSeries.cs
+++ b/maui/src/Charts/Series/DoughnutSeries.cs
@@ -71,6 +71,7 @@ public partial class DoughnutSeries : PieSeries
double _angleDifference;
float _yValue;
double _centerHoleSize = 1;
+ float _gapRadius;
#endregion
@@ -108,6 +109,38 @@ public partial class DoughnutSeries : PieSeries
null,
OnCenterViewChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ ///
+ /// The property defines the space between segments
+ ///
+ public static readonly BindableProperty GapRatioProperty = BindableProperty.Create(
+ nameof(GapRatio),
+ typeof(double),
+ typeof(DoughnutSeries),
+ 0d,
+ BindingMode.Default,
+ null,
+ OnSpacingPropertyChanged,
+ null,
+ coerceValue: CoerceSpacing);
+
+ ///
+ /// Identifies the bindable property.
+ ///
+ ///
+ /// The property represents the shape of the start and end points of a doughnut segment.
+ ///
+ public static readonly BindableProperty CapStyleProperty = BindableProperty.Create(
+ nameof(CapStyle),
+ typeof(CapStyle),
+ typeof(DoughnutSeries),
+ CapStyle.BothFlat,
+ BindingMode.Default,
+ null,
+ OnCapStylePropertyChanged);
+
#endregion
#region Public Properties
@@ -222,6 +255,92 @@ public View CenterView
set { SetValue(CenterViewProperty, value); }
}
+ ///
+ /// Gets or sets a value to adjust spacing between segments
+ ///
+ /// It accepts double values, and the default value is 0. Here, the value is between 0 and 1.
+ ///
+ /// # [Xaml](#tab/tabid-8)
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ]]>
+ /// # [C#](#tab/tabid-9)
+ ///
+ /// ***
+ ///
+ public double GapRatio
+ {
+ get { return (double)GetValue(GapRatioProperty); }
+ set { SetValue(GapRatioProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the value, that represents the shape of the start and end points of a doughnut segment.
+ ///
+ /// It accepts values, and its default value is
+ ///
+ /// # [Xaml](#tab/tabid-10)
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ /// ]]>
+ /// # [C#](#tab/tabid-11)
+ ///
+ /// ***
+ ///
+ public CapStyle CapStyle
+ {
+ get { return (CapStyle)GetValue(CapStyleProperty); }
+ set { SetValue(CapStyleProperty, value); }
+ }
+
///
/// Gets the size of the doughnut center hole.
///
@@ -291,6 +410,12 @@ internal override void GenerateSegments(SeriesView seriesView)
_angleDifference = GetAngleDifference();
_total = CalculateTotalYValues();
+ var gapPercentage = VisibleSegmentCount == 1 ? 0 : GapRatio;
+ _gapRadius = (float)(gapPercentage * _total / (VisibleSegmentCount * 2));
+ _total += gapPercentage * _total;
+
+ float gapAngle = (float)((_gapRadius * _angleDifference) / _total);
+
var oldSegments = OldSegments != null && OldSegments.Count > 0 && PointsCount == OldSegments.Count ? OldSegments : null;
var legendItems = GetLegendItems();
@@ -299,6 +424,11 @@ internal override void GenerateSegments(SeriesView seriesView)
_yValue = (legendItems == null || legendItems.Count == 0) ? (float)Math.Abs(double.IsNaN(ActualYValues[i]) ? double.NaN : ActualYValues[i]) : (float)(legendItems[i].IsToggled ? double.NaN : ActualYValues[i]);
_doughnutEndAngle = (float)(Math.Abs(float.IsNaN(_yValue) ? 0 : _yValue) * (_angleDifference / _total));
+ if (!float.IsNaN(_yValue) && _yValue != 0)
+ {
+ _doughnutStartAngle += gapAngle;
+ }
+
if (i < _segments.Count && _segments[i] is DoughnutSegment segment1)
{
segment1.SetData(_doughnutStartAngle, _doughnutEndAngle, _yValue);
@@ -333,6 +463,12 @@ internal override void GenerateSegments(SeriesView seriesView)
if (_segments[i].IsVisible)
{
_doughnutStartAngle += _doughnutEndAngle;
+
+ if(!float.IsNaN(_yValue) && _yValue != 0)
+ {
+ _doughnutStartAngle += gapAngle;
+
+ }
}
}
}
@@ -407,12 +543,35 @@ static void OnCenterViewChanged(BindableObject bindable, object oldValue, object
}
}
+ static void OnSpacingPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is DoughnutSeries series)
+ {
+ series.SegmentsCreated = false;
+ series.ScheduleUpdateChart();
+ }
+ }
+
static object CoerceDoughnutCoefficient(BindableObject bindable, object value)
{
double coefficient = Convert.ToDouble(value);
return coefficient > 1 ? 1 : coefficient < 0 ? 0 : value;
}
+ static object CoerceSpacing(BindableObject bindable, object value)
+ {
+ return Math.Clamp(Convert.ToDouble(value), 0.0, 1.0);
+ }
+
+ static void OnCapStylePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is DoughnutSeries series)
+ {
+ series.SegmentsCreated = false;
+ series.ScheduleUpdateChart();
+ }
+ }
+
void AddCenterView()
{
if (ChartArea?.PlotArea is ChartPlotArea plotArea && CenterView != null)
diff --git a/maui/src/Charts/Series/LineSeries.cs b/maui/src/Charts/Series/LineSeries.cs
index c6a7bf62..91515203 100644
--- a/maui/src/Charts/Series/LineSeries.cs
+++ b/maui/src/Charts/Series/LineSeries.cs
@@ -477,28 +477,7 @@ internal override bool SeriesContainsPoint(PointF point)
return false;
}
- LineSegment? endSegment = null;
- var seriesClipRect = AreaBounds;
- point.X -= ((float)seriesClipRect.Left);
- point.Y -= ((float)seriesClipRect.Top);
-
-
- LineSegment? startSegment;
- if (TooltipDataPointIndex == 0)
- {
- startSegment = _segments[TooltipDataPointIndex] as LineSegment;
- }
- else if (TooltipDataPointIndex == PointsCount - 1)
- {
- startSegment = _segments[TooltipDataPointIndex - 1] as LineSegment;
- }
- else
- {
- startSegment = _segments[TooltipDataPointIndex - 1] as LineSegment;
- endSegment = _segments[TooltipDataPointIndex] as LineSegment;
- }
-
- return SegmentContains(startSegment, endSegment, point, this);
+ return IsSegmentContainsWithPoints(point, TooltipDataPointIndex);
}
return false;
@@ -519,6 +498,51 @@ internal virtual bool SegmentContains(ChartSegment? startSegment, ChartSegment?
return false;
}
+ internal virtual bool IsSegmentContainsWithPoints(PointF point, int index)
+ {
+ LineSegment? endSegment = null;
+ var seriesClipRect = AreaBounds;
+ point.X -= ((float)seriesClipRect.Left);
+ point.Y -= ((float)seriesClipRect.Top);
+
+
+ LineSegment? startSegment;
+ if (index == 0)
+ {
+ startSegment = _segments[index] as LineSegment;
+ }
+ else if (index == PointsCount - 1)
+ {
+ startSegment = _segments[index - 1] as LineSegment;
+ }
+ else
+ {
+ startSegment = _segments[index - 1] as LineSegment;
+ endSegment = _segments[index] as LineSegment;
+ }
+
+ return SegmentContains(startSegment, endSegment, point, this);
+ }
+
+ internal override bool UpdateDataPointSelection(float pointX, float pointY, out int index)
+ {
+ index = -1;
+ foreach (var segment in _segments)
+ {
+ if (IsSegmentContainsWithPoints(new PointF(pointX, pointY), segment.Index))
+ {
+ index = segment.Index;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ internal override bool UpdateSeriesSelection(float pointX, float pointY)
+ {
+ return SeriesContainsPoint(new PointF(pointX, pointY));
+ }
+
internal override TooltipInfo? GetTooltipInfo(ChartTooltipBehavior tooltipBehavior, float tooltipX, float tooltipY)
{
var tooltipInfo = base.GetTooltipInfo(tooltipBehavior, tooltipX, tooltipY);
diff --git a/maui/src/Charts/Series/PieSeries.cs b/maui/src/Charts/Series/PieSeries.cs
index 0b52fb34..fc6bff06 100644
--- a/maui/src/Charts/Series/PieSeries.cs
+++ b/maui/src/Charts/Series/PieSeries.cs
@@ -79,6 +79,8 @@ public partial class PieSeries : CircularSeries, IDrawCustomLegendIcon
internal List GroupToDataPoints { get; set; }
+ internal int VisibleSegmentCount { get; set; }
+
#endregion
#region Bindable Properties
@@ -769,6 +771,7 @@ internal double GetGroupModeValue(double yValue, double total)
internal override double CalculateTotalYValues()
{
double total = 0;
+ VisibleSegmentCount = 0;
var legendItems = GetLegendItems();
for (int i = 0; i < ActualYValues.Count; i++)
@@ -778,10 +781,15 @@ internal override double CalculateTotalYValues()
if (legendItems == null || legendItems.Count == 0)
{
total += Math.Abs(ActualYValues[i]);
+ VisibleSegmentCount++;
}
else
{
- total += legendItems[i].IsToggled ? 0 : Math.Abs(ActualYValues[i]);
+ if(!legendItems[i].IsToggled)
+ {
+ total += Math.Abs(ActualYValues[i]);
+ VisibleSegmentCount++;
+ }
}
}
}
diff --git a/maui/src/Charts/Series/PolarSeries.cs b/maui/src/Charts/Series/PolarSeries.cs
index ef0e9650..7cbd23a9 100644
--- a/maui/src/Charts/Series/PolarSeries.cs
+++ b/maui/src/Charts/Series/PolarSeries.cs
@@ -19,7 +19,7 @@ public abstract class PolarSeries : ChartSeries, IDrawCustomLegendIcon, IMarkerD
bool IsIndexed
{
- get { return ActualXAxis is CategoryAxis; }
+ get { return ActualXAxis is CategoryAxis || ActualXAxis is DateTimeCategoryAxis; }
}
#endregion
diff --git a/maui/src/Charts/Series/RangeColumnSeries.cs b/maui/src/Charts/Series/RangeColumnSeries.cs
index 11961b99..a635203c 100644
--- a/maui/src/Charts/Series/RangeColumnSeries.cs
+++ b/maui/src/Charts/Series/RangeColumnSeries.cs
@@ -320,42 +320,26 @@ protected override ChartSegment CreateSegment()
#region Internal Methods
- internal override void GenerateSegments(SeriesView seriesView)
- {
- var xValues = GetXValues();
- if (IsGrouped && (IsIndexed || xValues == null))
- {
- for (var i = 0; i < PointsCount; i++)
- {
- if (i < _segments.Count && _segments[i] is RangeColumnSegment)
- {
- ((RangeColumnSegment)_segments[i]).SetData([i + SbsInfo.Start, i + SbsInfo.End, HighValues[i], LowValues[i], i]);
- }
- else
- {
- CreateSegment(seriesView, [i + SbsInfo.Start, i + SbsInfo.End, HighValues[i], LowValues[i], i], i);
- }
- }
- }
- else
- {
- if (xValues != null)
- {
- for (var i = 0; i < PointsCount; i++)
- {
- var x = xValues[i];
- if (i < _segments.Count && _segments[i] is RangeColumnSegment)
- {
- ((RangeColumnSegment)_segments[i]).SetData([x + SbsInfo.Start, x + SbsInfo.End, HighValues[i], LowValues[i], x]);
- }
- else
- {
- CreateSegment(seriesView, [x + SbsInfo.Start, x + SbsInfo.End, HighValues[i], LowValues[i], x], i);
- }
- }
- }
- }
- }
+ internal override void GenerateSegments(SeriesView seriesView)
+ {
+ var xValues = GetXValues();
+
+ if (xValues != null)
+ {
+ for (var i = 0; i < PointsCount; i++)
+ {
+ var x = xValues[i];
+ if (i < _segments.Count && _segments[i] is RangeColumnSegment)
+ {
+ ((RangeColumnSegment)_segments[i]).SetData([x + SbsInfo.Start, x + SbsInfo.End, HighValues[i], LowValues[i], x]);
+ }
+ else
+ {
+ CreateSegment(seriesView, [x + SbsInfo.Start, x + SbsInfo.End, HighValues[i], LowValues[i], x], i);
+ }
+ }
+ }
+ }
internal override double GetActualWidth()
{
diff --git a/maui/src/Charts/Series/StackingColumnSeries.cs b/maui/src/Charts/Series/StackingColumnSeries.cs
index f3011a19..79c3c8c6 100644
--- a/maui/src/Charts/Series/StackingColumnSeries.cs
+++ b/maui/src/Charts/Series/StackingColumnSeries.cs
@@ -455,37 +455,20 @@ internal override void GenerateSegments(SeriesView seriesView)
if (yEndValues != null && yStartValues != null)
{
- if (IsGrouped && (IsIndexed || xValues == null))
+ for (var i = 0; i < PointsCount; i++)
{
- for (int i = 0; i < PointsCount; i++)
+ if (xValues != null)
+
{
+ var x = xValues[i];
+
if (i < _segments.Count && _segments[i] is ColumnSegment segment)
{
- segment.SetData([i + SbsInfo.Start, i + SbsInfo.End, yEndValues[i], yStartValues[i], i, YValues[i]]);
+ segment.SetData([x + SbsInfo.Start, x + SbsInfo.End, yEndValues[i], yStartValues[i], x, YValues[i]]);
}
else
{
- CreateSegment(i, [i + SbsInfo.Start, i + SbsInfo.End, yEndValues[i], yStartValues[i], i, YValues[i]]);
- }
- }
- }
- else
- {
- for (var i = 0; i < PointsCount; i++)
- {
- if (xValues != null)
-
- {
- var x = xValues[i];
-
- if (i < _segments.Count && _segments[i] is ColumnSegment segment)
- {
- segment.SetData([x + SbsInfo.Start, x + SbsInfo.End, yEndValues[i], yStartValues[i], x, YValues[i]]);
- }
- else
- {
- CreateSegment(i, [x + SbsInfo.Start, x + SbsInfo.End, yEndValues[i], yStartValues[i], x, YValues[i]]);
- }
+ CreateSegment(i, [x + SbsInfo.Start, x + SbsInfo.End, yEndValues[i], yStartValues[i], x, YValues[i]]);
}
}
}
diff --git a/maui/src/Charts/Series/WaterfallSeries.cs b/maui/src/Charts/Series/WaterfallSeries.cs
index a0670389..e9e523f3 100644
--- a/maui/src/Charts/Series/WaterfallSeries.cs
+++ b/maui/src/Charts/Series/WaterfallSeries.cs
@@ -653,43 +653,21 @@ internal override void GenerateSegments(SeriesView seriesView)
double.IsNaN(xAxis.ActualCrossingValue) ? 0 : xAxis.ActualCrossingValue;
}
- if (IsGrouped && (IsIndexed || xValues == null))
+ if (xValues != null)
{
for (var i = 0; i < PointsCount; i++)
{
- if (xValues != null)
- {
- OnCalculateSegmentValues(out x1, out x2, out y1, out y2, i, _bottomValue, xValues[i]);
+ var x = xValues[i];
- if (i < _segments.Count && _segments[i] is WaterfallSegment segment)
- {
- segment.SetData([x1, x2, y1, y2, i, YValues[i]]);
- }
- else
- {
- CreateSegment(seriesView, [x1, x2, y1, y2, i, YValues[i]], i);
- }
+ OnCalculateSegmentValues(out x1, out x2, out y1, out y2, i, _bottomValue, xValues[i]);
+
+ if (i < _segments.Count && _segments[i] is WaterfallSegment segment)
+ {
+ segment.SetData([x1, x2, y1, y2, x, YValues[i]]);
}
- }
- }
- else
- {
- if (xValues != null)
- {
- for (var i = 0; i < PointsCount; i++)
+ else
{
- var x = xValues[i];
-
- OnCalculateSegmentValues(out x1, out x2, out y1, out y2, i, _bottomValue, xValues[i]);
-
- if (i < _segments.Count && _segments[i] is WaterfallSegment segment)
- {
- segment.SetData([x1, x2, y1, y2, x, YValues[i]]);
- }
- else
- {
- CreateSegment(seriesView, [x1, x2, y1, y2, x, YValues[i]], i);
- }
+ CreateSegment(seriesView, [x1, x2, y1, y2, x, YValues[i]], i);
}
}
}
diff --git a/maui/src/Charts/Styles/ChartAxisTickStyle.cs b/maui/src/Charts/Styles/ChartAxisTickStyle.cs
index e277ad08..fff4b7ca 100644
--- a/maui/src/Charts/Styles/ChartAxisTickStyle.cs
+++ b/maui/src/Charts/Styles/ChartAxisTickStyle.cs
@@ -165,23 +165,6 @@ void IThemeElement.OnCommonThemeChanged(string oldTheme, string newTheme)
#endregion
- #region Internal Methods
-
- internal void InitializeDynamicResource(string tickStyle)
- {
- if (tickStyle == "MajorTickStyle")
- {
- SetDynamicResource(StrokeProperty, "SfCartesianChartMajorTickLineStroke");
- }
-
- if (tickStyle == "MinorTickStyle")
- {
- SetDynamicResource(StrokeProperty, "SfCartesianChartMinorTickLineStroke");
- }
- }
-
- #endregion
-
#region Callback
static void OnTickSizeChanged(BindableObject bindable, object oldValue, object newValue)
{
diff --git a/maui/src/Charts/Themes/SfCartesianChartStyles.xaml b/maui/src/Charts/Themes/SfCartesianChartStyles.xaml
index 8eebecb5..2e652fab 100644
--- a/maui/src/Charts/Themes/SfCartesianChartStyles.xaml
+++ b/maui/src/Charts/Themes/SfCartesianChartStyles.xaml
@@ -37,4 +37,26 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/maui/src/Charts/Utils/ChartUtils.cs b/maui/src/Charts/Utils/ChartUtils.cs
index d02d1fcd..a0e4bad4 100644
--- a/maui/src/Charts/Utils/ChartUtils.cs
+++ b/maui/src/Charts/Utils/ChartUtils.cs
@@ -100,7 +100,7 @@ internal static double ConvertToDouble(object? val)
///
internal static IFontManager? GetFontManager(this IElementHandler? handler)
{
- if (IPlatformApplication.Current != null)
+ if (IPlatformApplication.Current != null && IPlatformApplication.Current.Services != null)
{
return IPlatformApplication.Current.Services.GetRequiredService();
}
diff --git a/maui/src/Charts/Utils/FastReflection.cs b/maui/src/Charts/Utils/FastReflection.cs
index cc49f716..22e0adae 100644
--- a/maui/src/Charts/Utils/FastReflection.cs
+++ b/maui/src/Charts/Utils/FastReflection.cs
@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq.Expressions;
using System.Reflection;
+using System.Runtime.CompilerServices;
namespace Syncfusion.Maui.Toolkit.Charts
{
@@ -160,7 +161,10 @@ void InitializeGet(PropertyInfo propertyInfo)
var instance = Expression.Parameter(typeof(object), "instance");
var methodInfo = propertyInfo.GetGetMethod();
-
+#if MACCATALYST
+ if (RuntimeFeature.IsDynamicCodeSupported)
+ {
+#endif
// non-instance for static method, or ((TInstance)instance)
var instanceCast = methodInfo != null && methodInfo.IsStatic ? null :
Expression.Convert(instance, propertyInfo.DeclaringType);
@@ -175,8 +179,51 @@ void InitializeGet(PropertyInfo propertyInfo)
var lambda = Expression.Lambda>(castPropertyValue, instance);
_getter = lambda.Compile();
+#if MACCATALYST
+ }
+ else
+ {
+ this.InitializeGet(propertyInfo, methodInfo);
+ }
+#endif
}
+#if MACCATALYST
+ ///
+ /// Method to initialize get value.
+ ///
+ /// The property info value.
+ /// The method info.
+ [UnconditionalSuppressMessage("Trimming", "IL3050:Using member 'typeof(Func<,>)' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling.", Justification = "")]
+ private void InitializeGet(PropertyInfo propertyInfo, MethodInfo? getMethod)
+ {
+ if (getMethod != null)
+ {
+ // AOT — use MethodInfo.CreateDelegate for better performance and AOT compatibility
+ if (getMethod.IsStatic)
+ {
+ // Static property: () => (object)Property
+ var staticDelegate = (Func)Delegate.CreateDelegate(typeof(Func), getMethod);
+ this._getter = _ => staticDelegate();
+ }
+ else
+ {
+ // Instance property: instance => (object)((TInstance)instance).Property
+ var declaringType = propertyInfo.DeclaringType!;
+ var propertyType = propertyInfo.PropertyType;
+
+ var delegateType = typeof(Func<,>).MakeGenericType(declaringType, propertyType);
+ var typedDelegate = getMethod.CreateDelegate(delegateType);
+
+ this._getter = (object instance) =>
+ {
+ return typedDelegate.DynamicInvoke(instance) !;
+ };
+ }
+ }
+ }
+#endif
+
void InitializeSet(PropertyInfo propertyInfo)
{
var methodInfo = propertyInfo.GetSetMethod();
@@ -250,6 +297,10 @@ public object Invoke(object instance, params object[] parameters)
static Func CreateInvokeDelegate(MethodInfo methodInfo)
{
+#if MACCATALYST
+ if (RuntimeFeature.IsDynamicCodeSupported)
+ {
+#endif
// Target: ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)
// parameters to execute
var instanceParameter = Expression.Parameter(typeof(object), "instance");
@@ -301,7 +352,43 @@ static Func CreateInvokeDelegate(MethodInfo methodInfo
return lambda.Compile();
}
+#if MACCATALYST
+ }
+ else
+ {
+ return CreateInvokeDelegateForAOTCompile(methodInfo);
+ }
+#endif
+ }
+
+#if MACCATALYST
+ ///
+ /// Method to create invoke delegate.
+ ///
+ /// The method info.
+ /// The invoke delegate.
+ [UnconditionalSuppressMessage("Trimming", "IL3050:Using member 'GetFuncType(params Type[])' which has 'RequiresDynamicCodeAttribute' can break functionality when AOT compiling.", Justification = "")]
+ private static Func CreateInvokeDelegateForAOTCompile(MethodInfo methodInfo)
+ {
+ // AOT-safe version using MethodInfo.CreateDelegate and DynamicInvoke
+ if (methodInfo.IsStatic)
+ {
+ // Create delegate for static method
+ var staticDelegate = methodInfo.CreateDelegate(
+ Expression.GetFuncType(methodInfo.GetParameters()
+ .Select(p => typeof(object))
+ .Append(typeof(object))
+ .ToArray()));
+
+ return (instance, parameters) => staticDelegate.DynamicInvoke(parameters)!;
+ }
+ else
+ {
+ // Create delegate for instance method
+ return (instance, parameters) => methodInfo.Invoke(instance, parameters)!;
+ }
}
+#endif
#region IMethodInvoker Members
diff --git a/maui/src/Core/BaseView/PlatformView/LayoutViewExt.ios.cs b/maui/src/Core/BaseView/PlatformView/LayoutViewExt.ios.cs
index 9921c97e..ae0236fd 100644
--- a/maui/src/Core/BaseView/PlatformView/LayoutViewExt.ios.cs
+++ b/maui/src/Core/BaseView/PlatformView/LayoutViewExt.ios.cs
@@ -86,7 +86,7 @@ public LayoutViewExt(IntPtr aPtr)
set => _mauiView = value == null ? null : new(value);
}
- IDrawable? Drawable
+ internal IDrawable? Drawable
{
get => _drawable != null && _drawable.TryGetTarget(out var v) ? v : null;
set
@@ -193,7 +193,6 @@ internal void InitializeNativeGraphicsView()
public override void LayoutSubviews()
{
base.LayoutSubviews();
-
if (View is not ILayout layout)
{
return;
@@ -201,12 +200,26 @@ public override void LayoutSubviews()
var bounds = AdjustForSafeArea(Bounds).ToRectangle();
var widthConstraint = bounds.Width;
- var heightConstraint = bounds.Height;
-
- if (!IsMeasureValid(widthConstraint, heightConstraint) && Superview is not Microsoft.Maui.Platform.MauiView && View is SfView sfView && !sfView.IsLayoutControl)
+ var heightConstraint = bounds.Height;
+ if (View is SfView sfview && !sfview.IsLayoutControl)
{
- layout.CrossPlatformMeasure(bounds.Width, bounds.Height);
- CacheMeasureConstraints(widthConstraint, heightConstraint);
+ layout.CrossPlatformMeasure(widthConstraint, heightConstraint);
+ }
+ else
+ {
+ // If the SuperView is a MauiView (backing a cross-platform ContentView or Layout), then measurement
+ // has already happened via SizeThatFits and doesn't need to be repeated in LayoutSubviews. But we
+ // _do_ need LayoutSubviews to make a measurement pass if the parent is something else (for example,
+ // the window); there's no guarantee that SizeThatFits has been called in that case.
+#if NET8_0_OR_GREATER
+ if (!IsMeasureValid(widthConstraint, heightConstraint) && Superview is not Microsoft.Maui.Platform.MauiView)
+ {
+ layout.CrossPlatformMeasure(widthConstraint, heightConstraint);
+ CacheMeasureConstraints(widthConstraint, heightConstraint);
+ }
+#else
+ layout.CrossPlatformMeasure(widthConstraint, heightConstraint);
+#endif
}
layout.CrossPlatformArrange(bounds);
diff --git a/maui/src/Core/Extensions/CanvasExtensions.Android.cs b/maui/src/Core/Extensions/CanvasExtensions.Android.cs
index 4e359fd3..2dae5525 100644
--- a/maui/src/Core/Extensions/CanvasExtensions.Android.cs
+++ b/maui/src/Core/Extensions/CanvasExtensions.Android.cs
@@ -2,7 +2,6 @@
using Android.Graphics;
using Android.Text;
using Microsoft.Maui.Graphics.Platform;
-using System;
using Font = Microsoft.Maui.Font;
using Paint = Android.Graphics.Paint;
using Rect = Microsoft.Maui.Graphics.Rect;
@@ -107,9 +106,7 @@ public static void DrawText(this ICanvas canvas, string value, Rect rect, Horizo
alignment = Android.Text.Layout.Alignment.AlignOpposite;
}
- // Ensure width is positive to prevent Android StaticLayout crashes
- int layoutWidth = Math.Max(1, (int)(rect.Width * nativeCanvas.DisplayScale));
- StaticTextLayout layout = new StaticTextLayout(value, paint, layoutWidth, alignment, 1.0f, 0.0f, false);
+ StaticTextLayout layout = new StaticTextLayout(value, paint, (int)(rect.Width * nativeCanvas.DisplayScale), alignment, 1.0f, 0.0f, false);
double rectDensityHeight = rect.Height * nativeCanvas.DisplayScale;
//// Check the layout does not accommodate the text inside the specified rect then
//// restrict the text rendering with line count.
diff --git a/maui/src/Core/Helper/IconButton/SfIconButton.cs b/maui/src/Core/Helper/IconButton/SfIconButton.cs
index 7d1e4627..64c62b72 100644
--- a/maui/src/Core/Helper/IconButton/SfIconButton.cs
+++ b/maui/src/Core/Helper/IconButton/SfIconButton.cs
@@ -39,14 +39,14 @@ internal class SfIconButton : Grid, ITouchListener
readonly SfIconView? _iconView;
///
- /// Used to identify the button need to hover while released the press.
+ /// Holds the view which is used to clip.
///
- readonly bool _isHoveringOnReleased;
+ Grid clipView;
///
- /// Holds the view which is used to clip.
+ /// Used to identify the button need to hover while released the press.
///
- Grid clipView;
+ readonly bool _isHoveringOnReleased;
#if __MACCATALYST__ || (!__ANDROID__ && !__IOS__)
///
@@ -217,7 +217,7 @@ void UpdateClip(double width, double height)
EllipseGeometry? previousClip = null;
if (clipView.Clip != null && clipView.Clip is EllipseGeometry)
{
- previousClip = (EllipseGeometry)clipView.Clip;
+ previousClip = (EllipseGeometry)EffectsView.Clip;
}
//// If the previous and current clip values are same, then no need to update the effects view clip.
@@ -288,11 +288,11 @@ protected override Size MeasureOverride(double widthConstraint, double heightCon
///
/// The width allocated to the element.
/// The height allocated to the element.
- protected override void OnSizeAllocated(double width, double height)
- {
- base.OnSizeAllocated(width, height);
+ protected override void OnSizeAllocated(double width, double height)
+ {
+ base.OnSizeAllocated(width, height);
#if __MACCATALYST__ || (!__ANDROID__ && !__IOS__)
- this.ApplyCornerClip();
+ this.ApplyCornerClip();
#endif
}
diff --git a/maui/src/Expander/ExpanderHeader.cs b/maui/src/Expander/ExpanderHeader.cs
index 90d1182b..debb770f 100644
--- a/maui/src/Expander/ExpanderHeader.cs
+++ b/maui/src/Expander/ExpanderHeader.cs
@@ -604,7 +604,6 @@ public void OnTouch(Internals.PointerEventArgs e)
if (e.Action == PointerActions.Released)
{
Expander._effectsView.Reset();
-
// Restore icon color based on current state.
if (IsMouseHover)
{
@@ -627,7 +626,6 @@ public void OnTouch(Internals.PointerEventArgs e)
{
Expander._effectsView.Reset();
IsMouseHover = false;
-
// Restore icon color to normal state since mouse hover is reset
if (!Expander.IsSelected)
{
diff --git a/maui/src/NavigationDrawer/SfNavigationDrawer.iOS.cs b/maui/src/NavigationDrawer/SfNavigationDrawer.iOS.cs
index 5041f07d..44522e5c 100644
--- a/maui/src/NavigationDrawer/SfNavigationDrawer.iOS.cs
+++ b/maui/src/NavigationDrawer/SfNavigationDrawer.iOS.cs
@@ -1,5 +1,6 @@
using Syncfusion.Maui.Toolkit.Internals;
using Syncfusion.Maui.Toolkit.Platform;
+using CoreGraphics;
using UIKit;
using PointerEventArgs = Syncfusion.Maui.Toolkit.Internals.PointerEventArgs;
@@ -29,8 +30,22 @@ public partial class SfNavigationDrawer
///
LayoutViewExt? _nativeView;
+#if !MACCATALYST
+
+ ///
+ /// Represents a scrollable UIView.
+ ///
+ internal UIView? ScrollableView;
+
+ ///
+ /// Indicates whether scrolling is currently active or not.
+ ///
+ internal bool isScroll = false;
+#endif
+
#endregion
+
#region Private Methods
///
@@ -120,6 +135,14 @@ void ITouchListener.OnTouch(PointerEventArgs e)
return;
}
+#if !MACCATALYST
+ if (isScroll)
+ {
+ isScroll = false;
+ return;
+ }
+#endif
+
switch (e.Action)
{
case PointerActions.Pressed:
@@ -153,6 +176,40 @@ void ConfigureTouch()
InitializeGesture();
}
+#if !MACCATALYST
+
+ ///
+ /// Determines if the specified UIView or any of its parent views is a UIScrollView or UICollectionView.
+ ///
+ /// The UIView to check.
+ /// True if a scrollable view is found, otherwise false.
+ internal bool IsScrollableView(UIView? view)
+ {
+ if (view == null)
+ return false;
+
+ if (view is UIScrollView || view is UICollectionView)
+ {
+ ScrollableView = view;
+ return true;
+ }
+
+ UIView? parent = view.Superview;
+ while (parent != null)
+ {
+ if (parent is UIScrollView || parent is UICollectionView)
+ {
+ ScrollableView = parent;
+ return true;
+ }
+
+ parent = parent.Superview;
+ }
+
+ return false;
+ }
+
+#endif
#endregion
#region Override Methods
@@ -192,9 +249,89 @@ internal bool GestureShouldBegin(UIGestureRecognizer uIGestureRecognizer)
}
}
+#if !MACCATALYST
+
+ // Use the improved gesture detection logic
+ if (view is not null && view.IsOpen)
+ {
+ if(view.DrawerSettings.Position == Position.Bottom || view.DrawerSettings.Position == Position.Top)
+ {
+ if (uIGestureRecognizer is UIPanGestureRecognizer panGesture)
+ {
+ var translation = panGesture.TranslationInView(panGesture.View);
+ var location = panGesture.LocationInView(panGesture.View);
+ UIView? hitView = panGesture.View?.HitTest(location, null);
+ bool isScrollableContent = view?.IsScrollableView(hitView) ?? false;
+ bool isHorizontalGesture = Math.Abs(translation.X) > Math.Abs(translation.Y);
+ if (isScrollableContent && !isHorizontalGesture && view is not null)
+ {
+ view.isScroll = true;
+ if (view.ScrollableView is UIScrollView scrollView)
+ {
+ bool atTop = scrollView.ContentOffset.Y <= 0;
+ bool atBottom = scrollView.ContentOffset.Y >= scrollView.ContentSize.Height - scrollView.Bounds.Height;
+ if(ShouldProcessDrawerScroll(atTop, atBottom, view.DrawerSettings.Position,translation))
+ {
+ view.isScroll =false;
+ return true;
+ }
+
+ return false;
+ }
+
+ if(view.ScrollableView is UITableView collectionView)
+ {
+ bool atTop = collectionView.ContentOffset.Y <= 0;
+ bool atBottom = collectionView.ContentOffset.Y >= collectionView.ContentSize.Height - collectionView.Bounds.Height;
+ if (ShouldProcessDrawerScroll(atTop, atBottom, view.DrawerSettings.Position, translation))
+ {
+ view.isScroll = false;
+ return true;
+ }
+
+ return false;
+ }
+
+ return false;
+ }
+ }
+ }
+ }
+#endif
return true;
}
+#if !MACCATALYST
+ ///
+ /// Determines whether a scroll gesture should be handled by the drawer.
+ ///
+ /// Whether the scrollable content is at the top position
+ /// Whether the scrollable content is at the bottom position
+ /// The position of the drawer (Top or Bottom)
+ /// The translation point of the gesture
+ /// True if the drawer should handle the gesture; otherwise, false
+ bool ShouldProcessDrawerScroll(bool atTop, bool atBottom,Position position,CGPoint translation)
+ {
+ _view.TryGetTarget(out var view);
+ if (view is not null)
+ {
+ if (position == Position.Bottom)
+ {
+ if (atTop && translation.Y > 0)
+ {
+ return true;
+ }
+ }
+ else if (atBottom && translation.Y < 0)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+#endif
+
}
#endif
diff --git a/maui/src/OtpInput/OTPEntry.cs b/maui/src/OtpInput/OTPEntry.cs
index b95929ce..b5118b47 100644
--- a/maui/src/OtpInput/OTPEntry.cs
+++ b/maui/src/OtpInput/OTPEntry.cs
@@ -273,6 +273,7 @@ protected override void OnHandlerChanged()
#if WINDOWS
case Microsoft.UI.Xaml.Controls.TextBox textbox:
OptimizeWindowsTextBox(textbox);
+ textbox.CornerRadius = new Microsoft.UI.Xaml.CornerRadius(4, 4, 0, 0);
break;
#elif ANDROID
case AndroidX.AppCompat.Widget.AppCompatEditText textbox:
diff --git a/maui/src/OtpInput/SfOtpInput.cs b/maui/src/OtpInput/SfOtpInput.cs
index 2174826a..42337e02 100644
--- a/maui/src/OtpInput/SfOtpInput.cs
+++ b/maui/src/OtpInput/SfOtpInput.cs
@@ -1,11 +1,11 @@
-using System.Globalization;
+using Syncfusion.Maui.Toolkit.Internals;
using Syncfusion.Maui.Toolkit.Graphics.Internals;
+using Rect = Microsoft.Maui.Graphics.Rect; // Alias for Maui's Rect
+using TextAlignment = Microsoft.Maui.TextAlignment; // Alias for Maui's TextAlignment
+using ResourceDictionary = Microsoft.Maui.Controls.ResourceDictionary; // Alias for Maui's ResourceDictionary
using Syncfusion.Maui.Toolkit.Helper;
-using Syncfusion.Maui.Toolkit.Internals;
using Syncfusion.Maui.Toolkit.Themes;
-using Rect = Microsoft.Maui.Graphics.Rect;
-using ResourceDictionary = Microsoft.Maui.Controls.ResourceDictionary;
-using TextAlignment = Microsoft.Maui.TextAlignment;
+using System.Globalization;
#if WINDOWS
using Microsoft.UI.Xaml;
using Microsoft.UI.Xaml.Input;
@@ -16,58 +16,58 @@
namespace Syncfusion.Maui.Toolkit.OtpInput
{
- ///
- /// The control is used to enter and verify one-time passwords (OTP) easily and securely.
- ///
- public class SfOtpInput : SfView, IKeyboardListener, IParentThemeElement
+ ///
+ /// The control is used to enter and verify one-time passwords (OTP) easily and securely.
+ ///
+ public class SfOtpInput : SfView, IKeyboardListener, IParentThemeElement
{
- #region Fields
+ #region Fields
- ///
- /// Array of OTP entry controls, each representing an OTP input field.
- ///
- OTPEntry[]? _otpEntries;
+ ///
+ /// Array of OTP entry controls, each representing an OTP input field.
+ ///
+ OTPEntry[]? _otpEntries;
- ///
- /// Array of bounds defining the position and size of each OTP input field.
- ///
- RectF[] _entryBounds = new RectF[4];
+ ///
+ /// Array of bounds defining the position and size of each OTP input field.
+ ///
+ RectF[] _entryBounds = new RectF[4];
- ///
- /// Index of the currently focused OTP input field.
- ///
- int _focusedIndex = 0;
+ ///
+ /// Index of the currently focused OTP input field.
+ ///
+ int _focusedIndex = 0;
- ///
- /// Rectangle used to outline the bounds of each OTP input field for drawing purposes.
- ///
- RectF _outlineRectF = new();
+ ///
+ /// Rectangle used to outline the bounds of each OTP input field for drawing purposes.
+ ///
+ RectF _outlineRectF = new();
- ///
- /// Starting point of a drawable element line within the OTP control.
- ///
- PointF _startPoint = new();
+ ///
+ /// Starting point of a drawable element line within the OTP control.
+ ///
+ PointF _startPoint = new();
- ///
- /// Ending point of a drawable element line within the OTP control.
- ///
- PointF _endPoint = new();
+ ///
+ /// Ending point of a drawable element line within the OTP control.
+ ///
+ PointF _endPoint = new();
- ///
- /// Width of each OTP input field.
- ///
- float _entryWidth;
+ ///
+ /// Width of each OTP input field.
+ ///
+ float _entryWidth;
- ///
- /// Height of each OTP input field.
- ///
- float _entryHeight;
+ ///
+ /// Height of each OTP input field.
+ ///
+ float _entryHeight;
- ///
- /// Corner radius for the rounded edges of OTP input fields.
- ///
- const float _cornerRadius = 4;
+ ///
+ /// Corner radius for the rounded edges of OTP input fields.
+ ///
+ const float _cornerRadius = 4;
///
/// This helps in adjusting the alignment or layout of the elements.
@@ -81,42 +81,42 @@ public class SfOtpInput : SfView, IKeyboardListener, IParentThemeElement
float _platformSpecificPadding = 6;
#endif
- ///
- /// Space between each OTP input field.
- ///
- float _spacing;
+ ///
+ /// Space between each OTP input field.
+ ///
+ float _spacing;
#if WINDOWS
- ///
- /// Determines whether the input should be displayed in uppercase letters.
- ///
- bool _isCapsOn = false;
+ ///
+ /// Determines whether the input should be displayed in uppercase letters.
+ ///
+ bool _isCapsOn = false;
#endif
- ///
- /// Array of separator labels placed between the OTP input fields.
- ///
- SfLabel[] _separators = new SfLabel[3];
+ ///
+ /// Array of separator labels placed between the OTP input fields.
+ ///
+ SfLabel[] _separators = new SfLabel[3];
- ///
- /// Width of the separator between OTP input fields.
- ///
- float _separatorWidth;
+ ///
+ /// Width of the separator between OTP input fields.
+ ///
+ float _separatorWidth;
- ///
- /// Height of the separator between OTP input fields.
- ///
- float _separatorHeight;
+ ///
+ /// Height of the separator between OTP input fields.
+ ///
+ float _separatorHeight;
- ///
- /// Font size of the separator text between OTP input fields.
- ///
- const double _separatorTextSize = 14;
+ ///
+ /// Font size of the separator text between OTP input fields.
+ ///
+ const double _separatorTextSize = 14;
- ///
- /// Represents the previous value of the OTP input.
- ///
- string? _oldValue;
+ ///
+ /// Represents the previous value of the OTP input.
+ ///
+ string? _oldValue;
#if IOS || MACCATALYST
///
@@ -125,9 +125,10 @@ public class SfOtpInput : SfView, IKeyboardListener, IParentThemeElement
string? _oldText;
///
- /// Stores the last UITextField to unhook event handlers.
+ /// A dictionary mapping OTPEntry objects to UITextField.
///
- UIKit.UITextField? _lastPlatformView;
+ Dictionary _platformViews = new Dictionary();
+
#endif
///
@@ -140,6 +141,11 @@ public class SfOtpInput : SfView, IKeyboardListener, IParentThemeElement
/// Indicates whether the Control key is currently pressed.
///
bool _isCtrlPressed = false;
+
+ ///
+ /// Indicates the StrokeThickness of the entry.
+ ///
+ float _strokeThickness = 1 ;
#endif
#if ANDROID || WINDOWS
@@ -150,85 +156,73 @@ public class SfOtpInput : SfView, IKeyboardListener, IParentThemeElement
///
bool _isPasteHandled = false;
#endif
-
- ///
- /// Identifies the BoxWidth bindable property.
- ///
- public static readonly BindableProperty BoxWidthProperty =
- BindableProperty.Create(nameof(BoxWidth), typeof(double), typeof(SfOtpInput), 40.0, BindingMode.TwoWay, propertyChanged: OnBoxSizePropertyChanged);
-
- ///
- /// Identifies the BoxHeight bindable property.
- ///
- public static readonly BindableProperty BoxHeightProperty =
- BindableProperty.Create(nameof(BoxHeight), typeof(double), typeof(SfOtpInput), 40.0, BindingMode.TwoWay, propertyChanged: OnBoxSizePropertyChanged);
#endregion
#region BindableProperties
///
- /// Identifies the bindable property.
+ /// Identifies the bindable property.
///
public static readonly BindableProperty ValueProperty =
- BindableProperty.Create(nameof(Value), typeof(string), typeof(SfOtpInput), null, BindingMode.TwoWay, propertyChanged: OnValuePropertyChanged);
+ BindableProperty.Create(nameof(Value), typeof(string), typeof(SfOtpInput), null, BindingMode.TwoWay, propertyChanged: OnValuePropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- public static readonly BindableProperty LengthProperty =
- BindableProperty.Create(nameof(Length), typeof(double), typeof(SfOtpInput), 4.0, BindingMode.TwoWay, propertyChanged: OnLengthPropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ public static readonly BindableProperty LengthProperty =
+ BindableProperty.Create(nameof(Length), typeof(double), typeof(SfOtpInput), 4.0, BindingMode.TwoWay, propertyChanged: OnLengthPropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- public static readonly BindableProperty StylingModeProperty =
- BindableProperty.Create(nameof(StylingMode), typeof(OtpInputStyle), typeof(SfOtpInput), OtpInputStyle.Outlined, BindingMode.TwoWay, propertyChanged: OnStylingModePropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ public static readonly BindableProperty StylingModeProperty =
+ BindableProperty.Create(nameof(StylingMode), typeof(OtpInputStyle), typeof(SfOtpInput), OtpInputStyle.Outlined, BindingMode.TwoWay, propertyChanged: OnStylingModePropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- public static readonly BindableProperty PlaceholderProperty =
- BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(SfOtpInput), null, BindingMode.TwoWay, propertyChanged: OnPlaceholderPropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ public static readonly BindableProperty PlaceholderProperty =
+ BindableProperty.Create(nameof(Placeholder), typeof(string), typeof(SfOtpInput), null, BindingMode.TwoWay, propertyChanged: OnPlaceholderPropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- public static readonly BindableProperty TypeProperty =
- BindableProperty.Create(nameof(Type), typeof(OtpInputType), typeof(SfOtpInput), OtpInputType.Number, BindingMode.TwoWay, propertyChanged: OnTypePropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ public static readonly BindableProperty TypeProperty =
+ BindableProperty.Create(nameof(Type), typeof(OtpInputType), typeof(SfOtpInput), OtpInputType.Number, BindingMode.TwoWay, propertyChanged: OnTypePropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- public static readonly BindableProperty SeparatorProperty =
- BindableProperty.Create(nameof(Separator), typeof(string), typeof(SfOtpInput), string.Empty, BindingMode.TwoWay, propertyChanged: OnSeparatorpropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ public static readonly BindableProperty SeparatorProperty =
+ BindableProperty.Create(nameof(Separator), typeof(string), typeof(SfOtpInput), string.Empty, BindingMode.TwoWay, propertyChanged: OnSeparatorpropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- public static new readonly BindableProperty IsEnabledProperty =
- BindableProperty.Create(nameof(IsEnabled), typeof(bool), typeof(SfOtpInput), true, BindingMode.TwoWay, propertyChanged: OnIsEnabledpropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ public static new readonly BindableProperty IsEnabledProperty =
+ BindableProperty.Create(nameof(IsEnabled), typeof(bool), typeof(SfOtpInput), true, BindingMode.TwoWay, propertyChanged: OnIsEnabledpropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- public static readonly BindableProperty AutoFocusProperty =
- BindableProperty.Create(nameof(AutoFocus), typeof(bool), typeof(SfOtpInput), false, BindingMode.TwoWay, propertyChanged: OnAutoFocusPropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ public static readonly BindableProperty AutoFocusProperty =
+ BindableProperty.Create(nameof(AutoFocus), typeof(bool), typeof(SfOtpInput), false, BindingMode.TwoWay, propertyChanged: OnAutoFocusPropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- public static readonly BindableProperty MaskCharacterProperty =
- BindableProperty.Create(nameof(MaskCharacter), typeof(char), typeof(SfOtpInput), '●', BindingMode.TwoWay, propertyChanged: OnMaskCharacterPropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ public static readonly BindableProperty MaskCharacterProperty =
+ BindableProperty.Create(nameof(MaskCharacter), typeof(char), typeof(SfOtpInput), '●', BindingMode.TwoWay, propertyChanged: OnMaskCharacterPropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- public static readonly BindableProperty InputStateProperty =
- BindableProperty.Create(nameof(InputState), typeof(OtpInputState), typeof(SfOtpInput), OtpInputState.Default, BindingMode.TwoWay, propertyChanged: OnInputStatePropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ public static readonly BindableProperty InputStateProperty =
+ BindableProperty.Create(nameof(InputState), typeof(OtpInputState), typeof(SfOtpInput), OtpInputState.Default, BindingMode.TwoWay, propertyChanged: OnInputStatePropertyChanged);
///
/// Identifies the bindable property.
///
- public static readonly BindableProperty TextColorProperty = BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(SfOtpInput), Color.FromArgb("#1C1B1F"), BindingMode.TwoWay, propertyChanged: OnPropertyChanged);
+ public static readonly BindableProperty TextColorProperty = BindableProperty.Create(nameof(TextColor), typeof(Color), typeof(SfOtpInput), Color.FromArgb("#1C1B1F"), BindingMode.TwoWay,propertyChanged:OnPropertyChanged);
///
/// Identifies the bindable property.
@@ -245,6 +239,18 @@ public class SfOtpInput : SfView, IKeyboardListener, IParentThemeElement
///
public static readonly BindableProperty InputBackgroundProperty = BindableProperty.Create(nameof(InputBackground), typeof(Color), typeof(SfOtpInput), Color.FromArgb("#E7E0EC"), BindingMode.TwoWay, propertyChanged: OnPropertyChanged);
+ ///
+ /// Identifies the BoxWidth bindable property.
+ ///
+ public static readonly BindableProperty BoxWidthProperty =
+ BindableProperty.Create(nameof(BoxWidth), typeof(double), typeof(SfOtpInput), 40.0, BindingMode.TwoWay, propertyChanged: OnBoxSizePropertyChanged);
+
+ ///
+ /// Identifies the BoxHeight bindable property.
+ ///
+ public static readonly BindableProperty BoxHeightProperty =
+ BindableProperty.Create(nameof(BoxHeight), typeof(double), typeof(SfOtpInput), 40.0, BindingMode.TwoWay, propertyChanged: OnBoxSizePropertyChanged);
+
#endregion
#region Internal BindableProperties
@@ -289,35 +295,35 @@ public class SfOtpInput : SfView, IKeyboardListener, IParentThemeElement
///
internal static readonly BindableProperty WarningStrokeProperty = BindableProperty.Create(nameof(WarningStroke), typeof(Color), typeof(SfOtpInput), Color.FromArgb("#914C00"), BindingMode.TwoWay, propertyChanged: OnPropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- internal static readonly BindableProperty ErrorStrokeProperty = BindableProperty.Create(nameof(ErrorStroke), typeof(Color), typeof(SfOtpInput), Color.FromArgb("#B3261E"), BindingMode.TwoWay, propertyChanged: OnPropertyChanged);
+ ///
+ /// Identifies the bindable property.
+ ///
+ internal static readonly BindableProperty ErrorStrokeProperty = BindableProperty.Create(nameof(ErrorStroke), typeof(Color), typeof(SfOtpInput), Color.FromArgb("#B3261E"), BindingMode.TwoWay, propertyChanged: OnPropertyChanged);
- ///
- /// Identifies the bindable property.
- ///
- internal static readonly BindableProperty DisabledTextColorProperty = BindableProperty.Create(nameof(DisabledTextColor), typeof(Color), typeof(SfOtpInput), Color.FromArgb("#611c1b1f"), BindingMode.TwoWay);
+ ///
+ /// Identifies the bindable property.
+ ///
+ internal static readonly BindableProperty DisabledTextColorProperty = BindableProperty.Create(nameof(DisabledTextColor), typeof(Color), typeof(SfOtpInput), Color.FromArgb("#611c1b1f"), BindingMode.TwoWay);
- #endregion
+ #endregion
- #region Constructor
+ #region Constructor
- ///
- /// Initializes a new instance of the class.
- ///
- public SfOtpInput()
- {
- ThemeElement.InitializeThemeResources(this, "SfOtpInputTheme");
- DrawingOrder = DrawingOrder.BelowContent;
- _entryWidth = (float)BoxWidth;
- _entryHeight = (float)BoxHeight;
- InitializeFields();
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public SfOtpInput()
+ {
+ ThemeElement.InitializeThemeResources(this, "SfOtpInputTheme");
+ DrawingOrder = DrawingOrder.BelowContent;
+ _entryWidth = (float)BoxWidth;
+ _entryHeight = (float)BoxHeight;
+ InitializeFields();
#if IOS
- this.IgnoreSafeArea = true;
+ this.IgnoreSafeArea = true;
#endif
- this.AddKeyboardListener(this);
- HookEvents();
+ this.AddKeyboardListener(this);
+ HookEvents();
this.SetDynamicResource(FilledHoveredBackgroundProperty, "SfOtpInputHoveredBackground");
this.SetDynamicResource(FilledDisableBackgroundProperty, "SfOtpInputBackgroundDisabled");
this.SetDynamicResource(FocusedStrokeProperty, "SfOtpInputBorderPressed");
@@ -333,27 +339,27 @@ public SfOtpInput()
#region Properties
- ///
- /// Gets or sets a value for the OTP input in SfOtpInput control.
- ///
- ///
+ ///
+ /// Gets or sets a value for the OTP input in SfOtpInput control.
+ ///
+ ///
/// It accepts string values.
- ///
+ ///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public string Value
@@ -362,27 +368,27 @@ public string Value
set { SetValue(ValueProperty, value); }
}
- ///
- /// Gets or sets a value that can be used to specify the length of the input fields in SfOtpInput control.
- ///
- ///
+ ///
+ /// Gets or sets a value that can be used to specify the length of the input fields in SfOtpInput control.
+ ///
+ ///
/// It accepts double values and the default value is 4.
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public double Length
@@ -391,27 +397,27 @@ public double Length
set { SetValue(LengthProperty, value); }
}
- ///
+ ///
/// Gets or sets a value that can be used to customize the OTP input fields in SfOtpInput.
- ///
- ///
+ ///
+ ///
/// A value. The default is .
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public OtpInputStyle StylingMode
@@ -420,27 +426,27 @@ public OtpInputStyle StylingMode
set { SetValue(StylingModeProperty, value); }
}
- ///
+ ///
/// Gets or sets a value that can be used to shown as a hint/placeholder until the user focuses on or enters a value in SfOtpInput.
- ///
- ///
+ ///
+ ///
/// It accepts string values.
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public string Placeholder
@@ -449,27 +455,27 @@ public string Placeholder
set { SetValue(PlaceholderProperty, value); }
}
- ///
+ ///
/// Gets or sets a value that can be used to specify the input values for the OTP input fields in SfOtpInput.
- ///
- ///
+ ///
+ ///
/// It accepts the values and the default is .
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public OtpInputType Type
@@ -480,25 +486,25 @@ public OtpInputType Type
///
/// Gets or sets a value that can be add a unique characters between the OTP input fields in SfOtpInput.
- ///
- ///
+ ///
+ ///
/// It accepts string values and the default value is an empty string.
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public string Separator
@@ -507,27 +513,27 @@ public string Separator
set { SetValue(SeparatorProperty, value); }
}
- ///
- /// Gets or sets a value indicating whether to enable or disable the SfOtpInput control.
- ///
- ///
+ ///
+ /// Gets or sets a value indicating whether to enable or disable the SfOtpInput control.
+ ///
+ ///
/// It accepts Boolean values and the default value is true.
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public new bool IsEnabled
@@ -536,27 +542,27 @@ public string Separator
set { SetValue(IsEnabledProperty, value); }
}
- ///
- /// Gets or sets a value indicating whether the SfOtpInput field should automatically receive focus when the component is rendered.
- ///
- ///
+ ///
+ /// Gets or sets a value indicating whether the SfOtpInput field should automatically receive focus when the component is rendered.
+ ///
+ ///
/// It accepts the Boolean values and the default value is false.
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public bool AutoFocus
@@ -565,20 +571,20 @@ public bool AutoFocus
set { SetValue(AutoFocusProperty, value); }
}
- ///
+ ///
/// Gets or sets a character value that can be used to mask the OTP input values in password mode.
- ///
- ///
+ ///
+ ///
/// It accepts the character values, and the default value is a dot ('●').
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public char MaskCharacter
{
get { return (char)GetValue(MaskCharacterProperty); }
- set { SetValue(MaskCharacterProperty, value); }
+ set { SetValue(MaskCharacterProperty, value); }
}
- ///
+ ///
/// Gets or sets a value that can be used to customize the visual state of the SfOtpInput control.
- ///
- ///
+ ///
+ ///
/// It accepts the OtpInputState values, and the default value is Default.
///
///
- /// This property is useful for scenarios where sensitive information needs to be hidden from view, such as when entering a password or PIN.
+ /// This property is useful for scenarios where sensitive information needs to be hidden from view, such as when entering a password or PIN.
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public OtpInputState InputState
@@ -635,19 +641,19 @@ public OtpInputState InputState
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public Color TextColor
@@ -664,19 +670,19 @@ public Color TextColor
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public Color Stroke
@@ -693,12 +699,12 @@ public Color Stroke
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public Color PlaceholderColor
@@ -726,12 +732,12 @@ public Color PlaceholderColor
///
///
/// Below is an example of how to configure the property using XAML and C#:
- ///
+ ///
/// # [XAML](#tab/tabid-1)
///
/// ]]>
- ///
+ ///
/// # [C#](#tab/tabid-2)
///
- ///
+ ///
/// ***
///
public Color InputBackground
@@ -749,44 +755,84 @@ public Color InputBackground
}
///
- /// Gets or sets the width of each OTP input box.
+ /// Gets or sets a value that defines the width of each input field in the OTP input.
///
+ ///
+ /// A double value representing the width of each input field. The default value is 40.
+ ///
+ ///
+ /// Below is an example of how to configure the property using XAML and C#:
+ ///
+ /// # [XAML](#tab/tabid-1)
+ ///
+ /// ]]>
+ ///
+ /// # [C#](#tab/tabid-2)
+ ///
+ ///
+ /// ***
+ ///
public double BoxWidth
- {
- get => (double)GetValue(BoxWidthProperty);
- set => SetValue(BoxWidthProperty, value);
- }
+ {
+ get => (double)GetValue(BoxWidthProperty);
+ set => SetValue(BoxWidthProperty, value);
+ }
///
- /// Gets or sets the height of each OTP input box.
+ /// Gets or sets a value that defines the height of each input field in the OTP input.
///
+ ///
+ /// A double value representing the height of each input field. The default value is 40.
+ ///
+ ///
+ /// Below is an example of how to configure the property using XAML and C#:
+ ///
+ /// # [XAML](#tab/tabid-1)
+ ///
+ /// ]]>
+ ///
+ /// # [C#](#tab/tabid-2)
+ ///
+ ///
+ /// ***
+ ///
public double BoxHeight
- {
- get => (double)GetValue(BoxHeightProperty);
- set => SetValue(BoxHeightProperty, value);
- }
+ {
+ get => (double)GetValue(BoxHeightProperty);
+ set => SetValue(BoxHeightProperty, value);
+ }
- #endregion
+ #endregion
- #region Event
+ #region Event
- ///
- /// Invoke the event when the value of is changed.
- ///
- public event EventHandler? ValueChanged;
+ ///
+ /// Invoke the event when the value of is changed.
+ ///
+ public event EventHandler? ValueChanged;
- #endregion
+ #endregion
- #region Internal Properties
+ #region Internal Properties
- ///
- /// Gets or sets the color used for separators between OTP input fields.
- ///
- internal Color SeparatorColor
- {
- get { return (Color)GetValue(SeparatorColorProperty); }
- set { SetValue(SeparatorColorProperty, value); }
- }
+ ///
+ /// Gets or sets the color used for separators between OTP input fields.
+ ///
+ internal Color SeparatorColor
+ {
+ get { return (Color)GetValue(SeparatorColorProperty); }
+ set { SetValue(SeparatorColorProperty, value); }
+ }
///
/// Gets or sets the hovered stroke color of the Input.
@@ -880,10 +926,10 @@ internal Color DisabledTextColor
/// The old value of the Value property.
/// The new value of the Value property.
static void OnValuePropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput)
- {
- if (oldValue is not null && newValue is not null && newValue.ToString()?.Length > (int)otpInput.Length)
+ {
+ if (bindable is SfOtpInput otpInput)
+ {
+ if(oldValue is not null && newValue is not null && newValue.ToString()?.Length > (int)otpInput.Length)
{
otpInput.TrimValueToLength((int)otpInput.Length);
return;
@@ -899,7 +945,7 @@ static void OnValuePropertyChanged(BindableObject bindable, object oldValue, obj
}
if (otpInput.ValueChanged != null)
- {
+ {
if (otpInput.Type == OtpInputType.Number)
{
RaiseValueChangedEvent(otpInput, oldValueStr, newValueStr);
@@ -909,21 +955,21 @@ static void OnValuePropertyChanged(BindableObject bindable, object oldValue, obj
RaiseValueChangedEvent(otpInput, oldValue?.ToString() ?? string.Empty, newValue?.ToString() ?? string.Empty);
}
}
+
+ otpInput.UpdateValue(bindable, newValue?? string.Empty);
+ }
+ }
- otpInput.UpdateValue(bindable, newValue ?? string.Empty);
- }
- }
-
- ///
- /// Property changed method for Length property.
- ///
- /// The bindable object should be SfOtpInput.
- /// The old value of the Length property.
- /// The new value of the Length property.
- static void OnLengthPropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput && newValue is double newLength && oldValue is double oldLength)
- {
+ ///
+ /// Property changed method for Length property.
+ ///
+ /// The bindable object should be SfOtpInput.
+ /// The old value of the Length property.
+ /// The new value of the Length property.
+ static void OnLengthPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput && newValue is double newLength && oldValue is double oldLength)
+ {
if (newLength <= 0)
{
@@ -931,73 +977,83 @@ static void OnLengthPropertyChanged(BindableObject bindable, object oldValue, ob
otpInput.Length = oldLength;
return;
}
- else if (oldLength <= 0)
+ else if(oldLength <= 0)
{
oldLength = newLength;
}
otpInput.UpdateEntriesLength((int)oldLength, (int)newLength);
- otpInput.UpdateValue(bindable, otpInput.Value);
- otpInput.HookEvents();
- otpInput.UpdatePlaceholderText();
- OnIsEnabledpropertyChanged(bindable, otpInput.IsEnabled, otpInput.IsEnabled);
- otpInput.InvalidateDrawable();
- }
- }
-
- ///
- /// Property changed method for StylingMode property.
- ///
- /// The bindable object should be SfOtpInput.
- /// The old value of the StylingMode property.
- /// The new value of the StylingMode property.
- static void OnStylingModePropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput)
- {
- otpInput.InvalidateDrawable();
- }
- }
+ otpInput.UpdateValue(bindable, otpInput.Value);
+ otpInput.HookEvents();
+ otpInput.UpdatePlaceholderText();
+ OnIsEnabledpropertyChanged(bindable, otpInput.IsEnabled, otpInput.IsEnabled);
+ otpInput.InvalidateDrawable();
+ }
+ }
- ///
- /// Property changed method for Placeholder property.
- ///
- /// The bindable object should be SfOtpInput.
- /// The old value of the Placeholder property.
- /// The new value of the Placeholder property.
- static void OnPlaceholderPropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput)
- {
- otpInput.UpdatePlaceholderText();
- otpInput.InvalidateDrawable();
- }
- }
+ ///
+ /// Property changed method for StylingMode property.
+ ///
+ /// The bindable object should be SfOtpInput.
+ /// The old value of the StylingMode property.
+ /// The new value of the StylingMode property.
+ static void OnStylingModePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput)
+ {
+ if(newValue is OtpInputStyle.Filled)
+ {
+ if (otpInput._otpEntries is not null)
+ {
+ foreach (var entry in otpInput._otpEntries)
+ {
+ otpInput.ApplyEntrySize(entry);
+ }
+ }
+ }
+ otpInput.InvalidateDrawable();
+ }
+ }
- ///
- /// Property changed method for Separator property.
- ///
- /// The bindable object should be SfOtpInput.
- /// The old value of the Separator property.
- /// The new value of the Separator property.
- static void OnSeparatorpropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput)
- {
- otpInput.InvalidateMeasure();
- }
- }
+ ///
+ /// Property changed method for Placeholder property.
+ ///
+ /// The bindable object should be SfOtpInput.
+ /// The old value of the Placeholder property.
+ /// The new value of the Placeholder property.
+ static void OnPlaceholderPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput)
+ {
+ otpInput.UpdatePlaceholderText();
+ otpInput.InvalidateDrawable();
+ }
+ }
- ///
- /// Property changed method for Type property.
- ///
- /// The bindable object should be SfOtpInput.
- /// The old value of the Type property.
- /// The new value of the Type property.
- static void OnTypePropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput)
- {
+ ///
+ /// Property changed method for Separator property.
+ ///
+ /// The bindable object should be SfOtpInput.
+ /// The old value of the Separator property.
+ /// The new value of the Separator property.
+ static void OnSeparatorpropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput)
+ {
+ otpInput.InvalidateMeasure();
+ }
+ }
+
+ ///
+ /// Property changed method for Type property.
+ ///
+ /// The bindable object should be SfOtpInput.
+ /// The old value of the Type property.
+ /// The new value of the Type property.
+ static void OnTypePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput)
+ {
if (otpInput.Type is OtpInputType.Number && otpInput.Value is not null && otpInput.Value.Any(char.IsLetter))
{
otpInput._oldValue = otpInput.Value;
@@ -1008,116 +1064,116 @@ static void OnTypePropertyChanged(BindableObject bindable, object oldValue, obje
}
otpInput.UpdateTypeProperty();
otpInput.UpdateKeyboardType();
- otpInput.UpdateMaskCharacter();
+ otpInput.UpdateMaskCharacter();
if (otpInput.Value is not null)
{
otpInput.UpdateValue(bindable, otpInput.Value);
}
- otpInput.InvalidateDrawable();
- }
- }
+ otpInput.InvalidateDrawable();
+ }
+ }
- ///
- /// Property changed method for IsEnabled property.
- ///
- /// The bindable object should be SfOtpInput.
- /// The old value of the IsEnabled property.
- /// The new value of the IsEnabled property.
- static void OnIsEnabledpropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput)
- {
- if (otpInput._otpEntries is not null)
- {
- foreach (var field in otpInput._otpEntries)
- {
- field.IsEnabled = (bool)newValue;
- }
+ ///
+ /// Property changed method for IsEnabled property.
+ ///
+ /// The bindable object should be SfOtpInput.
+ /// The old value of the IsEnabled property.
+ /// The new value of the IsEnabled property.
+ static void OnIsEnabledpropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput)
+ {
+ if (otpInput._otpEntries is not null)
+ {
+ foreach (var field in otpInput._otpEntries)
+ {
+ field.IsEnabled = (bool)newValue;
+ }
- otpInput.InvalidateDrawable();
- }
- }
- }
+ otpInput.InvalidateDrawable();
+ }
+ }
+ }
- ///
- /// Property changed method for AutoFocus property.
- ///
- /// The bindable object should be SfOtpInput.
- /// The old value of the AutoFocus property.
- /// The new value of the AutoFocus property.
- static void OnAutoFocusPropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput && otpInput._otpEntries is not null)
- {
- if ((bool)newValue)
- {
+ ///
+ /// Property changed method for AutoFocus property.
+ ///
+ /// The bindable object should be SfOtpInput.
+ /// The old value of the AutoFocus property.
+ /// The new value of the AutoFocus property.
+ static void OnAutoFocusPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput && otpInput._otpEntries is not null)
+ {
+ if ((bool)newValue)
+ {
#if !(MACCATALYST || IOS)
- otpInput.Loaded += (s, e) =>
- {
- otpInput._otpEntries[0].Focus();
- };
+ otpInput.Loaded += (s, e) =>
+ {
+ otpInput._otpEntries[0].Focus();
+ };
#else
- Task.Run(() =>
- {
- MainThread.BeginInvokeOnMainThread(() =>
- {
- otpInput._otpEntries[0].Focus();
- });
- });
+ Task.Run(() =>
+ {
+ MainThread.BeginInvokeOnMainThread(() =>
+ {
+ otpInput._otpEntries[0].Focus();
+ });
+ });
#endif
- }
- else
- {
- otpInput._otpEntries[0].Unfocus();
- }
+ }
+ else
+ {
+ otpInput._otpEntries[0].Unfocus();
+ }
- otpInput.InvalidateDrawable();
- }
- }
+ otpInput.InvalidateDrawable();
+ }
+ }
- ///
- /// Invoked when the MaskCharacter property is changed.
- ///
- /// The bindable object should be SfOtpInput.
- /// The old value of the MaskCharacter property.
- /// The new value of the MaskCharacter property.
- static void OnMaskCharacterPropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput)
- {
- if (newValue is char maskChar && maskChar is ' ')
+ ///
+ /// Invoked when the MaskCharacter property is changed.
+ ///
+ /// The bindable object should be SfOtpInput.
+ /// The old value of the MaskCharacter property.
+ /// The new value of the MaskCharacter property.
+ static void OnMaskCharacterPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput)
+ {
+ if(newValue is char maskChar && maskChar is ' ')
{
otpInput.MaskCharacter = '●';
}
- otpInput.UpdateMaskCharacter();
- }
- }
+ otpInput.UpdateMaskCharacter();
+ }
+ }
- ///
- /// Invoked when the InputState property is changed.Invalidates and redraws the OTP input fields to reflect the updated state.
- ///
- /// The bindable object should be SfOtpInput.
- /// The old value of the InputState property.
- /// The new value of the InputState property.
- static void OnInputStatePropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput)
- {
- otpInput.InvalidateDrawable();
- }
- }
+ ///
+ /// Invoked when the InputState property is changed.Invalidates and redraws the OTP input fields to reflect the updated state.
+ ///
+ /// The bindable object should be SfOtpInput.
+ /// The old value of the InputState property.
+ /// The new value of the InputState property.
+ static void OnInputStatePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput)
+ {
+ otpInput.InvalidateDrawable();
+ }
+ }
- ///
- /// Raises the ValueChanged event for the SfOtpInput control.
- ///
- /// The SfOtpInput control.
- /// The previous value.
- /// The new value.
- static void RaiseValueChangedEvent(SfOtpInput otpInput, string? oldValue, string? newValue)
- {
- var valueChangedEventArgs = new OtpInputValueChangedEventArgs(newValue, oldValue);
- otpInput.ValueChanged?.Invoke(otpInput, valueChangedEventArgs);
- }
+ ///
+ /// Raises the ValueChanged event for the SfOtpInput control.
+ ///
+ /// The SfOtpInput control.
+ /// The previous value.
+ /// The new value.
+ static void RaiseValueChangedEvent(SfOtpInput otpInput, string? oldValue, string? newValue)
+ {
+ var valueChangedEventArgs = new OtpInputValueChangedEventArgs(newValue, oldValue);
+ otpInput.ValueChanged?.Invoke(otpInput, valueChangedEventArgs);
+ }
///
/// Invoked when the background property of the control is changed.
@@ -1148,15 +1204,23 @@ static void OnStrokePropertyChanged(BindableObject bindable, object oldValue, ob
}
}
- ///
- /// Updates all OTP entry dimensions when BoxWidth or BoxHeight changes.
- ///
- static void OnBoxSizePropertyChanged(BindableObject bindable, object oldValue, object newValue)
- {
- if (bindable is SfOtpInput otpInput)
- {
- otpInput._entryWidth = (float)otpInput.BoxWidth;
- otpInput._entryHeight = (float)otpInput.BoxHeight;
+ ///
+ /// Updates all OTP entry dimensions when BoxWidth or BoxHeight changes.
+ ///
+ static void OnBoxSizePropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ if (bindable is SfOtpInput otpInput)
+ {
+ if (otpInput.BoxWidth > 0)
+ {
+ otpInput._entryWidth = (float)otpInput.BoxWidth;
+ }
+
+ if (otpInput.BoxHeight > 0)
+ {
+ otpInput._entryHeight = (float)otpInput.BoxHeight;
+ }
+
if (otpInput._otpEntries is not null)
{
foreach (var entry in otpInput._otpEntries)
@@ -1166,113 +1230,113 @@ static void OnBoxSizePropertyChanged(BindableObject bindable, object oldValue, o
}
otpInput.InvalidateMeasure();
otpInput.InvalidateDrawable();
- }
- }
+ }
+ }
- #endregion
+ #endregion
- #region Public Methods
+ #region Public Methods
- ///
- /// This method used to draw the rectangles.
- ///
- /// The canvas on which the rectangles will be drawn.
- /// The bounds defining the size and position of the rectangles.
- public void DrawUI(ICanvas canvas, RectF rectF)
- {
- if (_otpEntries is not null)
- {
- for (int i = 0; i < Length; i++)
- {
- UpdateDrawingParameters(i);
- canvas.StrokeSize = GetStrokeThickness(i);
- _otpEntries[i].UpdateParameters(StylingMode, _cornerRadius, _startPoint, _endPoint, this, IsEnabled, InputState, Stroke, InputBackground, TextColor, DisabledTextColor);
- _otpEntries[i].Draw(canvas, _outlineRectF);
+ ///
+ /// This method used to draw the rectangles.
+ ///
+ /// The canvas on which the rectangles will be drawn.
+ /// The bounds defining the size and position of the rectangles.
+ public void DrawUI(ICanvas canvas, RectF rectF)
+ {
+ if (_otpEntries is not null)
+ {
+ for (int i = 0; i < Length; i++)
+ {
+ UpdateDrawingParameters(i);
+ canvas.StrokeSize = GetStrokeThickness(i);
+ _otpEntries[i].UpdateParameters(StylingMode, _cornerRadius, _startPoint, _endPoint, this, IsEnabled, InputState,Stroke,InputBackground ,TextColor, DisabledTextColor);
+ _otpEntries[i].Draw(canvas, _outlineRectF);
- }
- }
- }
+ }
+ }
+ }
#endregion
#region Private Methods
- ///
- /// This method handles navigation and focus changes based on keyboard input.
- ///
- void HandleKeyPress(string key)
- {
- if (_otpEntries is null)
- {
- return;
- }
-
- int newIndex;
+ ///
+ /// This method handles navigation and focus changes based on keyboard input.
+ ///
+ void HandleKeyPress(string key)
+ {
+ if (_otpEntries is null)
+ {
+ return;
+ }
- switch (key)
- {
- case "Left":
- newIndex = _focusedIndex - 1;
+ int newIndex;
- if (newIndex >= 0)
- {
- FocusEntry(newIndex, true);
- }
+ switch (key)
+ {
+ case "Left":
+ newIndex = _focusedIndex - 1;
- break;
+ if (newIndex >= 0)
+ {
+ FocusEntry(newIndex, true);
+ }
- case "Right":
- newIndex = _focusedIndex + 1;
+ break;
- if (newIndex < _otpEntries.Length)
- {
- FocusEntry(newIndex, true);
- }
+ case "Right":
+ newIndex = _focusedIndex + 1;
- break;
+ if (newIndex < _otpEntries.Length)
+ {
+ FocusEntry(newIndex, true);
+ }
+
+ break;
- case "Back":
- _oldValue = string.Empty;
- if ((!string.IsNullOrEmpty(_otpEntries[_focusedIndex].Text)) & _otpEntries[_focusedIndex].Text is not "\0")
- {
- _otpEntries[_focusedIndex].Text = string.Empty;
- }
- else if (_focusedIndex > 0)
- {
- newIndex = _focusedIndex - 1;
- _otpEntries[newIndex].Text = string.Empty;
- FocusEntry(newIndex, false);
- }
+ case "Back":
+ _oldValue = string.Empty;
+ if ((!string.IsNullOrEmpty(_otpEntries[_focusedIndex].Text)) & _otpEntries[_focusedIndex].Text is not "\0")
+ {
+ _otpEntries[_focusedIndex].Text = string.Empty;
+ }
+ else if (_focusedIndex > 0)
+ {
+ newIndex = _focusedIndex - 1;
+ _otpEntries[newIndex].Text = string.Empty;
+ FocusEntry(newIndex, false);
+ }
- break;
+ break;
- case "Tab":
- newIndex = _focusedIndex + (_isShiftOn ? -1 : 1);
- if (newIndex >= 0 && newIndex < _otpEntries.Length)
- {
- FocusEntry(newIndex, true);
- }
+ case "Tab":
+ newIndex = _focusedIndex + (_isShiftOn ? -1 : 1);
+ if (newIndex >= 0 && newIndex < _otpEntries.Length)
+ {
+ FocusEntry(newIndex, true);
+ }
else
{
MoveFocusToNextElement(_isShiftOn);
}
- break;
+ break;
- default:
+ default:
_oldValue = string.Empty;
- char input = key.Last();
- bool isValidInput = ((Type == OtpInputType.Number && char.IsDigit(input)) || (Type != OtpInputType.Number));
- if (isValidInput)
- {
- UpdateEntryValue(input.ToString());
- }
+ char input = key.Last();
+ bool isValidInput = ((Type == OtpInputType.Number && char.IsDigit(input)) || (Type != OtpInputType.Number) );
+ if (isValidInput)
+ {
+ UpdateEntryValue(input.ToString());
+ }
- break;
- }
- }
+ break;
+ }
+ }
- ///
+ ///
/// Moves the focus to the next or previous focusable element within the parent visual tree.
///
///
@@ -1297,7 +1361,7 @@ void MoveFocusToNextElement(bool moveBackward)
var previousElement = focusableElements[currentIndex - 1];
if (previousElement is SfOtpInput otpInput && otpInput._otpEntries is not null)
{
- otpInput._otpEntries[^1]?.Focus();
+ otpInput._otpEntries[^1]?.Focus();
}
else
{
@@ -1312,7 +1376,7 @@ void MoveFocusToNextElement(bool moveBackward)
var nextElement = focusableElements[currentIndex + 1];
if (nextElement is SfOtpInput otpInput && otpInput._otpEntries is not null)
{
- otpInput._otpEntries[0]?.Focus();
+ otpInput._otpEntries[0]?.Focus();
}
else
{
@@ -1325,24 +1389,24 @@ void MoveFocusToNextElement(bool moveBackward)
///
- /// Initializes the OTP input fields, separators, and their layout.This method dynamically creates the required number of input fields and separators based on the specified length and positions them within the layout.
- ///
- void InitializeFields()
- {
- _otpEntries = new OTPEntry[(int)Length];
- _entryBounds = new RectF[(int)Length];
- _separators = new SfLabel[(int)Length - 1];
+ /// Initializes the OTP input fields, separators, and their layout.This method dynamically creates the required number of input fields and separators based on the specified length and positions them within the layout.
+ ///
+ void InitializeFields()
+ {
+ _otpEntries = new OTPEntry[(int)Length];
+ _entryBounds = new RectF[(int)Length];
+ _separators = new SfLabel[(int)Length - 1];
- var layout = new AbsoluteLayout();
+ var layout = new AbsoluteLayout();
#if IOS
- layout.IgnoreSafeArea = true;
+ layout.IgnoreSafeArea = true;
#endif
layout.BindingContext = this;
#if WINDOWS || ANDROID
layout.SetBinding(AbsoluteLayout.FlowDirectionProperty, BindingHelper.CreateBinding(nameof(FlowDirection), getter: static (SfOtpInput otpInput) => otpInput.FlowDirection));
#elif MACCATALYST || IOS
- layout.SetBinding(AbsoluteLayout.FlowDirectionProperty, BindingHelper.CreateBinding(nameof(FlowDirection), getter: static (SfOtpInput otpInput) =>
+ layout.SetBinding(AbsoluteLayout.FlowDirectionProperty,BindingHelper.CreateBinding(nameof(FlowDirection),getter: static (SfOtpInput otpInput) =>
{
VisualElement? parent = otpInput.Parent as VisualElement;
@@ -1354,89 +1418,96 @@ void InitializeFields()
return otpInput.FlowDirection == FlowDirection.MatchParent
? (parent?.FlowDirection ?? FlowDirection.LeftToRight)
: otpInput.FlowDirection;
- }
+ }
)
);
#endif
for (int i = 0; i < Length; i++)
- {
- OTPEntry otpEntry = InitializeEntry();
- _otpEntries[i] = otpEntry;
- layout.Children.Add(otpEntry);
-
- // Add separator only if it's not the last entry.
- if (i < Length - 1)
- {
- SfLabel separatorlabel = InitializeSeparator();
- _separators[i] = separatorlabel;
- layout.Children.Add(separatorlabel);
- }
- }
+ {
+ OTPEntry otpEntry = InitializeEntry();
+ _otpEntries[i] = otpEntry;
+ layout.Children.Add(otpEntry);
- Children.Clear();
- Children.Add(layout);
- }
+ // Add separator only if it's not the last entry.
+ if (i < Length - 1)
+ {
+ SfLabel separatorlabel = InitializeSeparator();
+ _separators[i] = separatorlabel;
+ layout.Children.Add(separatorlabel);
+ }
+ }
- ///
- /// Initializes a separator label to be placed between OTP input fields.
- ///
- /// A new instance of the configured as a separator.
- SfLabel InitializeSeparator()
- {
- return new SfLabel
- {
- Text = Separator,
- FontSize = _separatorTextSize,
- HorizontalTextAlignment = TextAlignment.Center,
- VerticalTextAlignment = TextAlignment.Center,
- LineBreakMode = LineBreakMode.NoWrap,
- TextColor = SeparatorColor
- };
- }
+ Children.Clear();
+ Children.Add(layout);
+ }
- ///
- /// Initializes an OTP input entry field at the specified index.
- ///
- /// A new instance of the configured for OTP input.
- OTPEntry InitializeEntry()
- {
- OTPEntry otpEntry = new OTPEntry
- {
- FontSize = 16,
- HorizontalTextAlignment = TextAlignment.Center,
+ ///
+ /// Initializes a separator label to be placed between OTP input fields.
+ ///
+ /// A new instance of the configured as a separator.
+ SfLabel InitializeSeparator()
+ {
+ return new SfLabel
+ {
+ Text = Separator,
+ FontSize = _separatorTextSize,
+ HorizontalTextAlignment = TextAlignment.Center,
VerticalTextAlignment = TextAlignment.Center,
- Keyboard = Type is OtpInputType.Number ? Keyboard.Numeric : Keyboard.Text,
- };
-
- ApplyEntrySize(otpEntry);
- otpEntry.BindingContext = this;
- otpEntry.SetBinding(OTPEntry.PlaceholderColorProperty, BindingHelper.CreateBinding(nameof(PlaceholderColor), getter: static (SfOtpInput otpInput) => otpInput.PlaceholderColor));
- otpEntry.SetBinding(OTPEntry.TextColorProperty, BindingHelper.CreateBinding(nameof(TextColor), getter: static (SfOtpInput otpInput) => otpInput.TextColor));
+ LineBreakMode = LineBreakMode.NoWrap,
+ TextColor = SeparatorColor
+ };
+ }
- return otpEntry;
- }
+ ///
+ /// Initializes an OTP input entry field at the specified index.
+ ///
+ /// A new instance of the configured for OTP input.
+ OTPEntry InitializeEntry()
+ {
+ OTPEntry otpEntry = new OTPEntry
+ {
+ FontSize = 16,
+ HorizontalTextAlignment = TextAlignment.Center,
+ VerticalTextAlignment = TextAlignment.Center,
+ Keyboard = Type is OtpInputType.Number ? Keyboard.Numeric : Keyboard.Text,
+ };
+
+ ApplyEntrySize(otpEntry);
+ otpEntry.BindingContext = this;
+ otpEntry.SetBinding(OTPEntry.PlaceholderColorProperty, BindingHelper.CreateBinding(nameof(PlaceholderColor), getter: static (SfOtpInput otpInput) => otpInput.PlaceholderColor));
+ otpEntry.SetBinding(OTPEntry.TextColorProperty, BindingHelper.CreateBinding(nameof(TextColor), getter: static (SfOtpInput otpInput) => otpInput.TextColor));
+
+ return otpEntry;
+ }
- ///
- /// Applies the current entry size to the given OTPEntry.
- ///
- void ApplyEntrySize(OTPEntry entry)
+ ///
+ /// Applies the current entry size to the given OTPEntry.
+ ///
+ void ApplyEntrySize(OTPEntry entry)
{
entry.MinimumWidthRequest = _entryWidth;
- entry.MinimumHeightRequest = _entryHeight;
entry.WidthRequest = _entryWidth;
- entry.HeightRequest = _entryHeight;
+
+#if WINDOWS
+ float height = (StylingMode == OtpInputStyle.Filled) ? _entryHeight - _strokeThickness : _entryHeight;
+#else
+ float height = _entryHeight;
+#endif
+
+ entry.MinimumHeightRequest = height;
+ entry.HeightRequest = height;
}
- ///
- /// Handles the event when an OTP input field receives focus.
- /// Updates the focused index and redraws the control.
- ///
- /// The source of the focus event.
- /// The event data for the focus event.
- void FocusAsync(object? sender, FocusEventArgs e)
- {
- _focusedIndex = Array.IndexOf(_otpEntries!, sender);
+ ///
+ /// Handles the event when an OTP input field receives focus.
+ /// Updates the focused index and redraws the control.
+ ///
+ /// The source of the focus event.
+ /// The event data for the focus event.
+ void FocusAsync(object? sender, FocusEventArgs e)
+ {
+ _focusedIndex = Array.IndexOf(_otpEntries!, sender);
if (sender is Entry entry)
{
entry.CursorPosition = 0;
@@ -1449,40 +1520,40 @@ void FocusAsync(object? sender, FocusEventArgs e)
if (_otpEntries[_focusedIndex].Text is not "" && text[0] is not '\0')
{
_otpEntries[_focusedIndex].Unfocus();
-
+
}
}
#endif
}
InvalidateDrawable();
- }
+ }
- ///
- /// Handles the event when an OTP input field loses focus.
- /// Redraws the control to update the visual state.
- ///
- /// The source of the unfocus event.
- /// The event data for the unfocus event.
- void FocusOutAsync(object? sender, FocusEventArgs e)
- {
- InvalidateDrawable();
- }
+ ///
+ /// Handles the event when an OTP input field loses focus.
+ /// Redraws the control to update the visual state.
+ ///
+ /// The source of the unfocus event.
+ /// The event data for the unfocus event.
+ void FocusOutAsync(object? sender, FocusEventArgs e)
+ {
+ InvalidateDrawable();
+ }
- ///
- /// Focuses the OTP entry at the specified index and sets the cursor position.
- ///
- /// The index of the OTP entry to focus.
- /// If true, the cursor is set to the start; otherwise, it is set to position 1.
- void FocusEntry(int index, bool setCursorToStart)
- {
- if (_otpEntries is not null)
- {
- _focusedIndex = index;
- _otpEntries[_focusedIndex].Focus();
- _otpEntries[_focusedIndex].CursorPosition = setCursorToStart ? 0 : 1;
- }
- }
+ ///
+ /// Focuses the OTP entry at the specified index and sets the cursor position.
+ ///
+ /// The index of the OTP entry to focus.
+ /// If true, the cursor is set to the start; otherwise, it is set to position 1.
+ void FocusEntry(int index, bool setCursorToStart)
+ {
+ if (_otpEntries is not null)
+ {
+ _focusedIndex = index;
+ _otpEntries[_focusedIndex].Focus();
+ _otpEntries[_focusedIndex].CursorPosition = setCursorToStart ? 0 : 1;
+ }
+ }
///
/// Handles the text change event for the OTP entry fields.
@@ -1505,12 +1576,12 @@ void OnEntryTextChanged(object? sender, Microsoft.Maui.Controls.TextChangedEvent
if (e.NewTextValue.Length <= 1)
{
#endif
- string currentValue = Value?.PadRight((int)Length, '\0') ?? new string('\0', (int)Length);
- char[] valueArray = currentValue.ToCharArray();
+ string currentValue = Value?.PadRight((int)Length, '\0') ?? new string('\0', (int)Length);
+ char[] valueArray = currentValue.ToCharArray();
- bool hasText = !string.IsNullOrEmpty(e.NewTextValue) && e.NewTextValue is not "\0";
- bool isValidText = (string.IsNullOrEmpty(e.NewTextValue) || e.NewTextValue is "\0" || char.IsLetterOrDigit(char.Parse(e.NewTextValue))) && (!string.IsNullOrEmpty(MaskCharacter.ToString()));
- valueArray[index] = hasText ? e.NewTextValue[0] : '\0';
+ bool hasText = !string.IsNullOrEmpty(e.NewTextValue) && e.NewTextValue is not "\0";
+ bool isValidText = (string.IsNullOrEmpty(e.NewTextValue) || e.NewTextValue is "\0" || char.IsLetterOrDigit(char.Parse(e.NewTextValue))) && (!string.IsNullOrEmpty(MaskCharacter.ToString()));
+ valueArray[index] = hasText ? e.NewTextValue[0] : '\0';
#if (MACCATALYST || IOS)
{
@@ -1533,19 +1604,19 @@ void OnEntryTextChanged(object? sender, Microsoft.Maui.Controls.TextChangedEvent
});
}
#else
- {
- if (Type is OtpInputType.Password && e.NewTextValue is not "")
{
- _otpEntries[index].Text = MaskCharacter.ToString();
- }
+ if (Type is OtpInputType.Password && e.NewTextValue is not "")
+ {
+ _otpEntries[index].Text = MaskCharacter.ToString();
+ }
- if (isValidText)
- {
- Value = new string(valueArray);
- }
+ if (isValidText)
+ {
+ Value = new string(valueArray);
+ }
- HandleFocus(index, hasText);
- }
+ HandleFocus(index, hasText);
+ }
#endif
#if ANDROID || WINDOWS
@@ -1597,11 +1668,11 @@ void OnEntryTextChanged(object? sender, Microsoft.Maui.Controls.TextChangedEvent
/// The current index of the OTP input field that triggered the focus change.
/// A boolean indicating whether the current field contains text.
void HandleFocus(int index, bool hasText)
- {
- if (_otpEntries is not null)
- {
- if (hasText)
- {
+ {
+ if (_otpEntries is not null)
+ {
+ if (hasText)
+ {
#if ANDROID || WINDOWS
if(_isPasteHandled && _isPaste)
{
@@ -1622,14 +1693,14 @@ void HandleFocus(int index, bool hasText)
}
#endif
if (index < Length - 1)
- {
- _otpEntries[index + 1].Focus();
- }
- else if (index == Length - 1)
- {
+ {
+ _otpEntries[index + 1].Focus();
+ }
+ else if (index == Length - 1)
+ {
_otpEntries[index].Unfocus();
- }
- }
+ }
+ }
#if IOS || MACCATALYST
else
{
@@ -1642,15 +1713,15 @@ void HandleFocus(int index, bool hasText)
}
}
- ///
- /// Determines the stroke size of the OTP entry based on focus state.
- ///
- /// The index of the OTP entry.
- /// Returns the stroke size for the OTP entry.
- float GetStrokeThickness(int index)
- {
- return _otpEntries is not null && _otpEntries[index].IsFocused ? 2 : 1;
- }
+ ///
+ /// Determines the stroke size of the OTP entry based on focus state.
+ ///
+ /// The index of the OTP entry.
+ /// Returns the stroke size for the OTP entry.
+ float GetStrokeThickness(int index)
+ {
+ return _otpEntries is not null && _otpEntries[index].IsFocused ? 2 : 1;
+ }
///
/// Updates the drawing parameters such as line points and rectangle boundaries for an OTP entry.
@@ -1672,16 +1743,15 @@ void UpdateDrawingParameters(int index)
yPadding = _platformSpecificPadding;
#endif
- // Adjust for RTL
- xPos = isRTL
- ? (float)(this.Width - _entryWidth - baseXPos)
- : baseXPos;
-
- _startPoint = new PointF(xPos, _entryHeight + _extraSpacing + yPadding);
- _endPoint = new PointF(xPos + _entryWidth, _entryHeight + _extraSpacing + yPadding);
+ // Adjust for RTL
+ xPos = isRTL
+ ? (float)(this.Width - _entryWidth - baseXPos)
+ : baseXPos;
- // Update outline rectangle
+ _startPoint = new PointF(xPos, _entryHeight + _extraSpacing + yPadding);
+ _endPoint = new PointF(xPos + _entryWidth, _entryHeight + _extraSpacing + yPadding);
_outlineRectF = new RectF(xPos, _extraSpacing + yPadding, _entryWidth, _entryHeight);
+
}
///
@@ -1690,65 +1760,65 @@ void UpdateDrawingParameters(int index)
/// The bindable object associated with this method.
/// The new value to update the OTP fields with.
void UpdateValue(BindableObject bindable, object value)
- {
- var otpInput = bindable as SfOtpInput;
- string? newValue = value as string;
+ {
+ var otpInput = bindable as SfOtpInput;
+ string? newValue = value as string;
- if (otpInput?._otpEntries is not null && newValue is not null)
- {
- newValue = !string.IsNullOrEmpty(_oldValue) ? _oldValue : newValue;
+ if (otpInput?._otpEntries is not null && newValue is not null)
+ {
+ newValue = !string.IsNullOrEmpty(_oldValue) ? _oldValue : newValue;
- for (int i = 0; i < otpInput._otpEntries.Length; i++)
- {
- if (i < newValue.Length)
- {
- char enteredCharacter = newValue[i];
- if (Type != OtpInputType.Password)
- {
- if (!char.IsLetterOrDigit(enteredCharacter) || (Type is OtpInputType.Number && !char.IsDigit(enteredCharacter)))
- {
- otpInput._otpEntries[i].Text = string.Empty;
- }
- else
- {
- otpInput._otpEntries[i].Text = enteredCharacter.ToString();
- }
- }
- else if (char.IsLetterOrDigit(enteredCharacter))
+ for (int i = 0; i < otpInput._otpEntries.Length; i++)
+ {
+ if (i < newValue.Length)
+ {
+ char enteredCharacter = newValue[i];
+ if (Type != OtpInputType.Password)
+ {
+ if (!char.IsLetterOrDigit(enteredCharacter) || (Type is OtpInputType.Number && !char.IsDigit(enteredCharacter)))
+ {
+ otpInput._otpEntries[i].Text = string.Empty;
+ }
+ else
+ {
+ otpInput._otpEntries[i].Text = enteredCharacter.ToString();
+ }
+ }
+ else if(char.IsLetterOrDigit(enteredCharacter))
{
otpInput._otpEntries[i].Text = MaskCharacter.ToString();
}
- }
- else
- {
- otpInput._otpEntries[i].Text = string.Empty;
- }
- }
- }
+ }
+ else
+ {
+ otpInput._otpEntries[i].Text = string.Empty;
+ }
+ }
+ }
}
- ///
- /// Updates the length of OTP input fields when the length property changes.
- ///
- /// The previous number of OTP input fields.
- /// The new desired number of OTP input fields.
- void UpdateEntriesLength(int oldLength, int newLength)
- {
- if (_otpEntries is null)
- {
- return;
- }
+ ///
+ /// Updates the length of OTP input fields when the length property changes.
+ ///
+ /// The previous number of OTP input fields.
+ /// The new desired number of OTP input fields.
+ void UpdateEntriesLength(int oldLength, int newLength)
+ {
+ if (_otpEntries is null)
+ {
+ return;
+ }
var layout = Children[0] as AbsoluteLayout;
- if (newLength > oldLength)
- {
- AddEntry(oldLength, newLength, layout);
- }
- else if (newLength < oldLength)
- {
- RemoveEntry(oldLength, newLength, layout);
- }
- }
+ if (newLength > oldLength)
+ {
+ AddEntry(oldLength, newLength, layout);
+ }
+ else if (newLength < oldLength)
+ {
+ RemoveEntry(oldLength, newLength, layout);
+ }
+ }
///
/// Trims the value to the specified length if it exceeds the given length.
@@ -1756,37 +1826,37 @@ void UpdateEntriesLength(int oldLength, int newLength)
/// Length of the OTPInput
void TrimValueToLength(int length)
{
- if (!string.IsNullOrEmpty(Value) && Value.Length > length)
+ if(!string.IsNullOrEmpty(Value) && Value.Length > length)
{
Value = Value.Substring(0, length);
}
}
- ///
- /// Removes OTP entry fields and separators when the length is reduced.
- ///
- /// The previous number of OTP input fields.
- /// The desired new number of OTP input fields.
- /// The AbsoluteLayout container holding the OTP input fields.
- void RemoveEntry(int oldLength, int newLength, AbsoluteLayout? layout)
- {
- if (_otpEntries is not null)
- {
- for (int i = oldLength - 1; i >= newLength; i--)
- {
- layout?.Children.Remove(_otpEntries[i]);
- DetachEventsForEntry(i);
- if (i >= newLength && i is not 0)
- {
- layout?.Children.Remove(_separators[i - 1]);
- }
- }
+ ///
+ /// Removes OTP entry fields and separators when the length is reduced.
+ ///
+ /// The previous number of OTP input fields.
+ /// The desired new number of OTP input fields.
+ /// The AbsoluteLayout container holding the OTP input fields.
+ void RemoveEntry(int oldLength, int newLength, AbsoluteLayout? layout)
+ {
+ if (_otpEntries is not null)
+ {
+ for (int i = oldLength - 1; i >= newLength; i--)
+ {
+ layout?.Children.Remove(_otpEntries[i]);
+ DetachEventsForEntry(i);
+ if (i >= newLength && i is not 0)
+ {
+ layout?.Children.Remove(_separators[i - 1]);
+ }
+ }
- _otpEntries = _otpEntries.Take(newLength).ToArray();
- _entryBounds = _entryBounds.Take(newLength).ToArray();
- _separators = _separators.Take(newLength - 1).ToArray();
- }
- }
+ _otpEntries =_otpEntries.Take(newLength).ToArray();
+ _entryBounds = _entryBounds.Take(newLength).ToArray();
+ _separators = _separators.Take(newLength - 1).ToArray();
+ }
+ }
///
/// Adds new OTP entry fields and separators when the length is increased.
@@ -1812,7 +1882,7 @@ void AddEntry(int oldLength, int newLength, AbsoluteLayout? layout)
OTPEntry otpEntry = InitializeEntry();
AttachEvents(otpEntry);
- _otpEntries = _otpEntries.Concat(new[] { otpEntry }).ToArray();
+ _otpEntries = _otpEntries.Concat(new[] { otpEntry }).ToArray();
SetInputFieldPosition(i, otpEntry);
layout?.Children.Add(otpEntry);
}
@@ -1840,7 +1910,7 @@ void SetInputFieldPosition(int i, OTPEntry otpEntry)
AbsoluteLayout.SetLayoutBounds(otpEntry, new Rect(rect.X, rect.Y, rect.Width, rect.Height));
float entryX = ((_entryWidth + _spacing) * i) + _extraSpacing;
- float entryY = _extraSpacing;
+ float entryY = _extraSpacing ;
_entryBounds[i] = new RectF(entryX, entryY, _entryWidth, _entryHeight);
AbsoluteLayout.SetLayoutBounds(otpEntry, new Rect(_entryBounds[i].X, _entryBounds[i].Y, _entryBounds[i].Width, _entryBounds[i].Height));
}
@@ -1867,55 +1937,55 @@ void SetSeparatorPosition(int i, SfLabel label)
/// A formatted string based on the placeholder value. If the placeholder length is less than the total OTP input field length, it appends empty characters.
///
string GetPlaceHolder()
- {
- if (string.IsNullOrEmpty(Placeholder))
- {
- return string.Empty;
- }
-
- if (Placeholder.Length is 1)
- {
- return new string(Placeholder[0], _otpEntries?.Length ?? 0);
- }
+ {
+ if (string.IsNullOrEmpty(Placeholder))
+ {
+ return string.Empty;
+ }
- if (_otpEntries is not null)
- {
- if (Placeholder.Length < _otpEntries.Length)
- {
- return Placeholder + new string('\0', _otpEntries.Length - Placeholder.Length);
- }
- }
+ if (Placeholder.Length is 1)
+ {
+ return new string(Placeholder[0], _otpEntries?.Length ?? 0);
+ }
- return Placeholder;
- }
+ if (_otpEntries is not null)
+ {
+ if (Placeholder.Length < _otpEntries.Length)
+ {
+ return Placeholder + new string('\0', _otpEntries.Length - Placeholder.Length);
+ }
+ }
- ///
- /// Updates the placeholder text for all OTP input fields based on the Placeholder property.
- ///
- void UpdatePlaceholderText()
- {
- if (_otpEntries is null || Placeholder is null)
- {
- return;
- }
+ return Placeholder;
+ }
- string actualPlaceholder = GetPlaceHolder();
- for (int i = 0; i < _otpEntries.Length; i++)
- {
- _otpEntries[i].Placeholder = actualPlaceholder.Length > i ? actualPlaceholder[i].ToString() : string.Empty;
- }
- }
+ ///
+ /// Updates the placeholder text for all OTP input fields based on the Placeholder property.
+ ///
+ void UpdatePlaceholderText()
+ {
+ if (_otpEntries is null || Placeholder is null)
+ {
+ return;
+ }
- ///
- /// Updates the text value of the currently focused OTP entry field based on the key pressed.
- ///
- /// The key input value to be assigned to the OTP entry.
- void UpdateEntryValue(string key)
- {
- if (_otpEntries is not null)
- {
+ string actualPlaceholder = GetPlaceHolder();
+ for (int i = 0; i < _otpEntries.Length; i++)
+ {
+ _otpEntries[i].Placeholder = actualPlaceholder.Length > i ? actualPlaceholder[i].ToString() : string.Empty;
+ }
+ }
+
+ ///
+ /// Updates the text value of the currently focused OTP entry field based on the key pressed.
+ ///
+ /// The key input value to be assigned to the OTP entry.
+ void UpdateEntryValue(string key)
+ {
+ if (_otpEntries is not null)
+ {
char input = key[key.Length - 1];
-#if WINDOWS
+#if WINDOWS
if (_isCapsOn || _isShiftOn)
{
_otpEntries[_focusedIndex].Text = input.ToString().ToUpper(CultureInfo.CurrentCulture);
@@ -1928,75 +1998,75 @@ void UpdateEntryValue(string key)
_otpEntries[_focusedIndex].Text = input.ToString();
#endif
}
- }
+ }
- ///
- /// Updates the properties of OTP input fields when the Type property is changed.
- /// This includes enabling/disabling password masking and clearing invalid text.
- ///
- void UpdateTypeProperty()
- {
- if (_focusedIndex < 0 || _otpEntries is null || Length == 0)
- {
- return;
- }
+ ///
+ /// Updates the properties of OTP input fields when the Type property is changed.
+ /// This includes enabling/disabling password masking and clearing invalid text.
+ ///
+ void UpdateTypeProperty()
+ {
+ if (_focusedIndex < 0 || _otpEntries is null || Length == 0)
+ {
+ return;
+ }
- if (_otpEntries[_focusedIndex].Text is not "")
- {
- for (int i = 0; i < _otpEntries.Length; i++)
- {
- if (Type == OtpInputType.Password && _otpEntries[i].Text is not "")
- {
- _otpEntries[i].Text = MaskCharacter.ToString();
- }
- else if (Type == OtpInputType.Number && _otpEntries[i].Text.Any(char.IsLetter))
- {
- _otpEntries[i].Text = string.Empty;
- }
- }
- }
- }
+ if (_otpEntries[_focusedIndex].Text is not "")
+ {
+ for (int i = 0; i < _otpEntries.Length; i++)
+ {
+ if (Type == OtpInputType.Password && _otpEntries[i].Text is not "")
+ {
+ _otpEntries[i].Text = MaskCharacter.ToString();
+ }
+ else if (Type == OtpInputType.Number && _otpEntries[i].Text.Any(char.IsLetter))
+ {
+ _otpEntries[i].Text = string.Empty;
+ }
+ }
+ }
+ }
- ///
- /// Helps to wire the event handlers to the OTP entry fields.
- ///
- void HookEvents()
- {
- UnHookEvents();
- if (_otpEntries is not null)
- {
- foreach (var otpEntry in _otpEntries)
+ ///
+ /// Helps to wire the event handlers to the OTP entry fields.
+ ///
+ void HookEvents()
+ {
+ UnHookEvents();
+ if (_otpEntries is not null)
+ {
+ foreach (var otpEntry in _otpEntries)
{
AttachEvents(otpEntry);
}
}
- }
+ }
///
/// Helps to Unwire the event handlers to the OTP entry fields.
///
void UnHookEvents()
- {
- if (_otpEntries is not null)
- {
- foreach (var otpEntry in _otpEntries)
- {
- DetachEvents(otpEntry);
- }
- }
- }
+ {
+ if (_otpEntries is not null)
+ {
+ foreach (var otpEntry in _otpEntries)
+ {
+ DetachEvents(otpEntry);
+ }
+ }
+ }
- ///
- /// Detaches event handlers from a specific OTP entry field when its length is changed.
- ///
- /// The index of the OTP input field to remove event handlers from.
- void DetachEventsForEntry(int i)
- {
- if (_otpEntries is not null)
- {
- DetachEvents(_otpEntries[i]);
- }
- }
+ ///
+ /// Detaches event handlers from a specific OTP entry field when its length is changed.
+ ///
+ /// The index of the OTP input field to remove event handlers from.
+ void DetachEventsForEntry(int i)
+ {
+ if (_otpEntries is not null)
+ {
+ DetachEvents(_otpEntries[i]);
+ }
+ }
///
/// Attaches event handlers to a specified OTP entry field.
@@ -2140,7 +2210,7 @@ void OnKeyPress(object? sender, Android.Views.View.KeyEventArgs e)
{
return;
}
-
+
switch (e.KeyCode)
{
case Android.Views.Keycode.Del:
@@ -2172,7 +2242,7 @@ void OnKeyPress(object? sender, Android.Views.View.KeyEventArgs e)
}
break;
}
-
+
}
#endif
@@ -2180,34 +2250,34 @@ void OnKeyPress(object? sender, Android.Views.View.KeyEventArgs e)
/// Updates the size of the separator text based on its content and font size.
///
void UpdateSeparatorSize()
- {
- var size = TextMeasurer.CreateTextMeasurer().MeasureText(Separator, (float)_separatorTextSize);
- _separatorHeight = (float)size.Height;
- _separatorWidth = (float)size.Width + Separator.Length;
- }
+ {
+ var size = TextMeasurer.CreateTextMeasurer().MeasureText(Separator, (float)_separatorTextSize);
+ _separatorHeight = (float)size.Height;
+ _separatorWidth = (float)size.Width + Separator.Length;
+ }
#if MACCATALYST || IOS
- ///
- /// Validates the input text for the OTP entry field, handling backspace and alphanumeric input.
- ///
- /// The UITextField where the input is occurring.
- /// The range of the text to be replaced.
- /// The new string being entered.
- ///
- /// Returns true if the input should proceed; otherwise, false to prevent input.
- ///
- bool ValidateText(UITextField textField, NSRange range, string inputText)
- {
- if (_otpEntries is null)
- {
- return true;
- }
+ ///
+ /// Validates the input text for the OTP entry field, handling backspace and alphanumeric input.
+ ///
+ /// The UITextField where the input is occurring.
+ /// The range of the text to be replaced.
+ /// The new string being entered.
+ ///
+ /// Returns true if the input should proceed; otherwise, false to prevent input.
+ ///
+ bool ValidateText(UITextField textField, NSRange range, string inputText)
+ {
+ if (_otpEntries is null)
+ {
+ return true;
+ }
- if (string.IsNullOrEmpty(inputText))
- {
- HandleKeyPress("Back");
- return true;
- }
+ if (string.IsNullOrEmpty(inputText))
+ {
+ HandleKeyPress("Back");
+ return true;
+ }
if (inputText.Length > 1)
{
@@ -2216,35 +2286,35 @@ bool ValidateText(UITextField textField, NSRange range, string inputText)
}
char enteredText = inputText[0];
- if (char.IsLetterOrDigit(enteredText))
- {
- HandleKeyPress(enteredText.ToString());
- return false;
- }
+ if (char.IsLetterOrDigit(enteredText))
+ {
+ HandleKeyPress(enteredText.ToString());
+ return false;
+ }
- return false;
- }
+ return false;
+ }
#endif
- ///
- /// Handles the key-down event for the OTP input field and invokes the OnPreviewKeyDown method.
- ///
- /// The key event arguments associated with the key-down event.
- void IKeyboardListener.OnKeyDown(KeyEventArgs args)
- {
+ ///
+ /// Handles the key-down event for the OTP input field and invokes the OnPreviewKeyDown method.
+ ///
+ /// The key event arguments associated with the key-down event.
+ void IKeyboardListener.OnKeyDown(KeyEventArgs args)
+ {
#if MACCATALYST || IOS
- OnPreviewKeyDown(args);
+ OnPreviewKeyDown(args);
#endif
- }
+ }
- ///
- /// Handles the key-up event for the OTP input field.
- ///
- /// The key event arguments associated with the key-up event.
- void IKeyboardListener.OnKeyUp(KeyEventArgs args)
- {
+ ///
+ /// Handles the key-up event for the OTP input field.
+ ///
+ /// The key event arguments associated with the key-up event.
+ void IKeyboardListener.OnKeyUp(KeyEventArgs args)
+ {
#if MACCATALYST || IOS
- if (!args.IsShiftKeyPressed)
+ if(!args.IsShiftKeyPressed)
{
_isShiftOn = false;
}
@@ -2252,79 +2322,79 @@ void IKeyboardListener.OnKeyUp(KeyEventArgs args)
}
#if MACCATALYST || IOS
- ///
- /// Processes key-down events for OTP input fields.
- /// Handles navigation keys, backspace, delete, caps lock, and alphanumeric input.
- ///
- /// The key event arguments containing the key pressed.
- void OnPreviewKeyDown(KeyEventArgs e)
- {
- if (_otpEntries is null)
- {
- return;
- }
+ ///
+ /// Processes key-down events for OTP input fields.
+ /// Handles navigation keys, backspace, delete, caps lock, and alphanumeric input.
+ ///
+ /// The key event arguments containing the key pressed.
+ void OnPreviewKeyDown(KeyEventArgs e)
+ {
+ if (_otpEntries is null)
+ {
+ return;
+ }
- string text = e.Key.ToString();
- switch (e.Key)
- {
- case KeyboardKey.Left:
- HandleKeyPress("Left");
- e.Handled = true;
- break;
+ string text = e.Key.ToString();
+ switch (e.Key)
+ {
+ case KeyboardKey.Left:
+ HandleKeyPress("Left");
+ e.Handled = true;
+ break;
- case KeyboardKey.Right:
- HandleKeyPress("Right");
- e.Handled = true;
- break;
+ case KeyboardKey.Right:
+ HandleKeyPress("Right");
+ e.Handled = true;
+ break;
- case KeyboardKey.Back:
- HandleKeyPress("Back");
- e.Handled = true;
- break;
+ case KeyboardKey.Back:
+ HandleKeyPress("Back");
+ e.Handled = true;
+ break;
- case KeyboardKey.Delete:
- HandleKeyPress("Back");
- e.Handled = true;
- break;
+ case KeyboardKey.Delete:
+ HandleKeyPress("Back");
+ e.Handled = true;
+ break;
- case KeyboardKey.Tab:
+ case KeyboardKey.Tab:
_isShiftOn = e.IsShiftKeyPressed;
- HandleKeyPress("Tab");
- e.Handled = true;
- break;
+ HandleKeyPress("Tab");
+ e.Handled = true;
+ break;
- case KeyboardKey.Shift:
- _isShiftOn = true;
- break;
+ case KeyboardKey.Shift:
+ _isShiftOn = true;
+ break;
- default:
- if ((e.Key >= KeyboardKey.A && e.Key <= KeyboardKey.Z) ||
- (e.Key >= KeyboardKey.Num0 && e.Key <= KeyboardKey.Num9))
- {
- HandleKeyPress(text);
- }
- e.Handled = true;
- break;
- }
- }
+ default:
+ if ((e.Key >= KeyboardKey.A && e.Key <= KeyboardKey.Z) ||
+ (e.Key >= KeyboardKey.Num0 && e.Key <= KeyboardKey.Num9))
+ {
+ HandleKeyPress(text);
+ }
+ e.Handled = true;
+ break;
+ }
+ }
#endif
- ///
- /// Handles the handler change event for OTP entry fields.
- /// Sets up platform-specific event handlers for key input validation and handling.
- ///
- /// The OTPEntry control whose handler has changed.
- /// Event arguments containing details of the change.
- void OnHandlerChanged(object? sender, EventArgs e)
- {
- if (sender is OTPEntry textBox)
- {
+ ///
+ /// Handles the handler change event for OTP entry fields.
+ /// Sets up platform-specific event handlers for key input validation and handling.
+ ///
+ /// The OTPEntry control whose handler has changed.
+ /// Event arguments containing details of the change.
+ void OnHandlerChanged(object? sender, EventArgs e)
+ {
+ if (sender is OTPEntry textBox)
+ {
#if MACCATALYST || IOS
- // Unhook from previous handler if exists
- if (_lastPlatformView is not null)
+ // Unhook from previous handler specific to this OTPEntry
+ if (_platformViews.TryGetValue(textBox, out var previousPlatformView))
{
- _lastPlatformView.ShouldChangeCharacters -= ValidateText;
- _lastPlatformView = null;
+ previousPlatformView.ShouldChangeCharacters -= ValidateText;
+ _platformViews.Remove(textBox);
}
#endif
@@ -2343,10 +2413,10 @@ void OnHandlerChanged(object? sender, EventArgs e)
}
#elif MACCATALYST || IOS
- if ((sender as OTPEntry)?.Handler is not null && (sender as OTPEntry)?.Handler?.PlatformView is UIKit.UITextField platformView)
+ if (textBox.Handler?.PlatformView is UIKit.UITextField platformView)
{
platformView.ShouldChangeCharacters += ValidateText;
- _lastPlatformView = platformView;
+ _platformViews[textBox] = platformView;
}
#endif
}
@@ -2390,20 +2460,20 @@ void OnPaste(object sender, Microsoft.UI.Xaml.Controls.TextControlPasteEventArgs
/// Updates the text of all OTP input fields with the masked character if the input type is set to Password.
///
void UpdateMaskCharacter()
- {
- if (_otpEntries is null || Value is null || Type is not OtpInputType.Password)
- {
- return;
- }
-
- foreach (var otpEntry in _otpEntries)
- {
+ {
+ if (_otpEntries is null || Value is null || Type is not OtpInputType.Password)
+ {
+ return;
+ }
+
+ foreach (var otpEntry in _otpEntries)
+ {
if (otpEntry.Text is not "")
{
otpEntry.Text = MaskCharacter.ToString();
}
- }
- }
+ }
+ }
///
/// Handles the paste operation when triggered. This method processes the clipboard content and performs necessary actions depending on the content type or context.
@@ -2422,7 +2492,7 @@ void HandlePaste()
copiedText = await Microsoft.Maui.ApplicationModel.DataTransfer.Clipboard.GetTextAsync();
}
#elif WINDOWS
- if (Clipboard.Default.HasText)
+ if (Clipboard.Default.HasText)
{
copiedText = await Clipboard.Default.GetTextAsync();
_isCtrlPressed = false;
@@ -2438,30 +2508,30 @@ void HandlePaste()
Value = new string(copiedText);
}
}
-
- ///
- /// Updates the keyboard type for each OTP entry based on the input type of the control.
- ///
- void UpdateKeyboardType()
- {
- if (_otpEntries is not null)
- {
- for (int i = 0; i < _otpEntries.Length; i++)
- {
- if (Type == OtpInputType.Password || Type == OtpInputType.Text)
- {
- _otpEntries[i].Keyboard = Keyboard.Text;
- }
- else
- {
- _otpEntries[i].Keyboard = Keyboard.Numeric;
- }
- }
- }
- }
+
+ ///
+ /// Updates the keyboard type for each OTP entry based on the input type of the control.
+ ///
+ void UpdateKeyboardType()
+ {
+ if (_otpEntries is not null)
+ {
+ for (int i = 0; i < _otpEntries.Length; i++)
+ {
+ if (Type == OtpInputType.Password || Type == OtpInputType.Text)
+ {
+ _otpEntries[i].Keyboard = Keyboard.Text;
+ }
+ else
+ {
+ _otpEntries[i].Keyboard = Keyboard.Numeric;
+ }
+ }
+ }
+ }
#endregion
- #region Override methods
+ #region Override methods
///
/// Arranges the layout and positions of OTP input fields and separators within the specified bounds, dynamically calculating positions to ensure proper alignment and spacing.
@@ -2504,7 +2574,7 @@ protected override Size ArrangeContent(Rect bounds)
separatorLabel.Text = Separator;
float separatorX = entryX + _entryWidth + ((_spacing - _separatorWidth) / 2);
- AbsoluteLayout.SetLayoutBounds(separatorLabel, new Rect(separatorX, entryY - (_extraSpacing / 2), _separatorWidth, _entryHeight + _extraSpacing));
+ AbsoluteLayout.SetLayoutBounds(separatorLabel, new Rect(separatorX,entryY-(_extraSpacing/2), _separatorWidth, _entryHeight+_extraSpacing));
}
}
@@ -2525,7 +2595,7 @@ protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
DrawUI(canvas, dirtyRect);
}
- #endregion
+#endregion
#region Interface Implementation
diff --git a/maui/src/Picker/Helper/PickerHelper.cs b/maui/src/Picker/Helper/PickerHelper.cs
index 70d914cd..bd2b30c6 100644
--- a/maui/src/Picker/Helper/PickerHelper.cs
+++ b/maui/src/Picker/Helper/PickerHelper.cs
@@ -234,6 +234,51 @@ internal static View CreateTemplateView(DataTemplate template, PickerItemDetails
return view;
}
+ ///
+ /// Method to create a data template view.
+ ///
+ /// The data template.
+ /// The picker control info.
+ /// Returns the view from the view template
+ internal static View CreateDataTemplateView(DataTemplate template, object info)
+ {
+ View view;
+ var content = template.CreateContent();
+ if (content is ViewCell)
+ {
+ view = ((ViewCell)content).View;
+ }
+ else
+ {
+ view = (View)content;
+ }
+
+ if (view.BindingContext == null && info != null)
+ {
+ view.BindingContext = info;
+ }
+
+ return view;
+ }
+
+ ///
+ /// Method to select the appropriate template or template selector for the view.
+ ///
+ /// data template.
+ /// layout info
+ /// picker info
+ /// Returns the view from the view template
+ internal static View CreateLayoutTemplateViews(DataTemplate template, BindableObject containerView, object context)
+ {
+ if (template is DataTemplateSelector selector)
+ {
+ DataTemplate selectedTemplate = selector.SelectTemplate(context, containerView);
+ return CreateDataTemplateView(selectedTemplate, context);
+ }
+
+ return CreateDataTemplateView(template, context);
+ }
+
///
/// Method to get the name based on parent.
///
diff --git a/maui/src/Picker/Interface/IColumnHeaderView.cs b/maui/src/Picker/Interface/IColumnHeaderView.cs
index b41fdd01..a18c7e5d 100644
--- a/maui/src/Picker/Interface/IColumnHeaderView.cs
+++ b/maui/src/Picker/Interface/IColumnHeaderView.cs
@@ -9,5 +9,10 @@ internal interface IColumnHeaderView
/// Gets the picker column header details for the picker view.
///
PickerColumnHeaderView ColumnHeaderView { get; }
+
+ ///
+ /// Gets the picker column header template.
+ ///
+ DataTemplate ColumnHeaderTemplate { get; }
}
}
\ No newline at end of file
diff --git a/maui/src/Picker/Interface/IFooterView.cs b/maui/src/Picker/Interface/IFooterView.cs
index f692837e..26870417 100644
--- a/maui/src/Picker/Interface/IFooterView.cs
+++ b/maui/src/Picker/Interface/IFooterView.cs
@@ -10,6 +10,11 @@ internal interface IFooterView : IPickerCommon
///
PickerFooterView FooterView { get; }
+ ///
+ /// Gets the settings of picker footer template.
+ ///
+ DataTemplate FooterTemplate { get; }
+
///
/// Method to update after the confirm button clicked.
///
diff --git a/maui/src/Picker/Interface/IHeaderView.cs b/maui/src/Picker/Interface/IHeaderView.cs
index 998e7ccc..f702af2a 100644
--- a/maui/src/Picker/Interface/IHeaderView.cs
+++ b/maui/src/Picker/Interface/IHeaderView.cs
@@ -10,6 +10,11 @@ internal interface IHeaderView : IPickerCommon
///
PickerHeaderView HeaderView { get; }
+ ///
+ /// Gets the settings of the header template.
+ ///
+ DataTemplate HeaderTemplate { get; }
+
///
/// Method to update after the time button clicked.
///
diff --git a/maui/src/Picker/Interface/IPickerView.cs b/maui/src/Picker/Interface/IPickerView.cs
index fa7d0c27..fdf9b239 100644
--- a/maui/src/Picker/Interface/IPickerView.cs
+++ b/maui/src/Picker/Interface/IPickerView.cs
@@ -39,5 +39,10 @@ internal interface IPickerView
/// Gets a value indicating whether the parent value have valid value because dynamic scrolling on dialog opening does not scroll because the picker stack layout does not have a parent value.
///
bool IsValidParent { get; }
+
+ ///
+ /// Gets a value indicating whether the looping is enabled or not.
+ ///
+ bool EnableLooping { get; }
}
}
\ No newline at end of file
diff --git a/maui/src/Picker/Model/Settings/PickerBase.cs b/maui/src/Picker/Model/Settings/PickerBase.cs
index 1537c732..acc8623a 100644
--- a/maui/src/Picker/Model/Settings/PickerBase.cs
+++ b/maui/src/Picker/Model/Settings/PickerBase.cs
@@ -1,4 +1,5 @@
-using System.Collections.ObjectModel;
+using System.Collections;
+using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
@@ -168,6 +169,48 @@ public abstract partial class PickerBase
null,
propertyChanged: OnRelativeViewChanged);
+ ///
+ /// Identifies the dependency property.
+ ///
+ ///
+ /// The identifier for dependency property.
+ ///
+ public static readonly BindableProperty HeaderTemplateProperty =
+ BindableProperty.Create(
+ nameof(HeaderTemplate),
+ typeof(DataTemplate),
+ typeof(PickerBase),
+ null,
+ propertyChanged: OnHeaderTemplateChanged);
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ ///
+ /// The identifier for dependency property.
+ ///
+ public static readonly BindableProperty ColumnHeaderTemplateProperty =
+ BindableProperty.Create(
+ nameof(ColumnHeaderTemplate),
+ typeof(DataTemplate),
+ typeof(PickerBase),
+ null,
+ propertyChanged: OnColumnHeaderTemplateChanged);
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ ///
+ /// The identifier for dependency property.
+ ///
+ public static readonly BindableProperty FooterTemplateProperty =
+ BindableProperty.Create(
+ nameof(FooterTemplate),
+ typeof(DataTemplate),
+ typeof(PickerBase),
+ null,
+ propertyChanged: OnFooterTemplateChanged);
+
///
/// Identifies the dependency property.
///
@@ -194,6 +237,48 @@ public abstract partial class PickerBase
typeof(PickerBase),
null);
+ ///
+ /// Identifies the dependency property.
+ ///
+ ///
+ /// The identifier for dependency property.
+ ///
+ public static readonly BindableProperty EnableLoopingProperty =
+ BindableProperty.Create(
+ nameof(EnableLooping),
+ typeof(bool),
+ typeof(PickerBase),
+ false,
+ propertyChanged: OnEnableLoopingChanged);
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ ///
+ /// The identifier for dependency property.
+ ///
+ public static readonly BindableProperty PopupWidthProperty =
+ BindableProperty.Create(
+ nameof(PopupWidth),
+ typeof(double),
+ typeof(PickerBase),
+ defaultValueCreator: bindable => GetDefaultPopupWidth(bindable),
+ propertyChanged: OnPopupWidthPropertyChanged);
+
+ ///
+ /// Identifies the dependency property.
+ ///
+ ///
+ /// The identifier for dependency property.
+ ///
+ public static readonly BindableProperty PopupHeightProperty =
+ BindableProperty.Create(
+ nameof(PopupHeight),
+ typeof(double),
+ typeof(PickerBase),
+ defaultValueCreator: bindable => GetDefaultPopupHeight(bindable),
+ propertyChanged: OnPopupHeightPropertyChanged);
+
#endregion
#region Internal Bindable Properties
@@ -629,6 +714,42 @@ public View RelativeView
set { SetValue(RelativeViewProperty, value); }
}
+ ///
+ /// Gets or sets the header template or template selector for picker header
+ ///
+ ///
+ /// The BindingContext of the HeaderTemplate is respective picker control. When using header template, the header style customization will not be applicable.
+ ///
+ public DataTemplate HeaderTemplate
+ {
+ get { return (DataTemplate)GetValue(HeaderTemplateProperty); }
+ set { SetValue(HeaderTemplateProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the column header template or template selector for picker column header.
+ ///
+ ///
+ /// The BindingContext of the ColumnHeaderTemplate is respective picker control. When using column header template, the column header style customization will not be applicable.
+ ///
+ public DataTemplate ColumnHeaderTemplate
+ {
+ get { return (DataTemplate)GetValue(ColumnHeaderTemplateProperty); }
+ set { SetValue(ColumnHeaderTemplateProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the footer template or template selector for picker footer.
+ ///
+ ///
+ /// The BindingContext of the FooterTemplate is respective picker control. When using footer template, the footer style customization will not be applicable.
+ ///
+ public DataTemplate FooterTemplate
+ {
+ get { return (DataTemplate)GetValue(FooterTemplateProperty); }
+ set { SetValue(FooterTemplateProperty, value); }
+ }
+
///
/// Gets or sets the picker ok button clicked command.
///
@@ -729,6 +850,39 @@ public ICommand DeclineCommand
set { SetValue(DeclineCommandProperty, value); }
}
+ ///
+ /// Gets or sets a value indicating whether the picker can perform looping.
+ ///
+ public bool EnableLooping
+ {
+ get { return (bool)GetValue(EnableLoopingProperty); }
+ set { SetValue(EnableLoopingProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the width of the popup in the picker.
+ ///
+ ///
+ /// The default width of the popup will be the addition of picker column width.
+ ///
+ public double PopupWidth
+ {
+ get { return (double)GetValue(PopupWidthProperty); }
+ set { SetValue(PopupWidthProperty, value); }
+ }
+
+ ///
+ /// Gets or sets the height of the popup in the picker.
+ ///
+ ///
+ /// The default height of the popup will be the addition of header height, column header height, picker items' five items height, and footer height.
+ ///
+ public double PopupHeight
+ {
+ get { return (double)GetValue(PopupHeightProperty); }
+ set { SetValue(PopupHeightProperty, value); }
+ }
+
#endregion
#region Internal Properties
@@ -763,6 +917,11 @@ public ICommand DeclineCommand
///
bool IPickerView.IsValidParent => _pickerStackLayout != null && _pickerStackLayout.Parent != null && Parent != null;
+ ///
+ /// Gets a value indicating whether the enable looping value of the picker.
+ ///
+ bool IPickerView.EnableLooping => EnableLooping;
+
///
/// Gets or sets the value of header view. This property can be used to customize the of header in Picker.
///
@@ -1315,13 +1474,28 @@ static void OnColumnHeaderViewChanged(BindableObject bindable, object oldValue,
}
}
- picker._pickerContainer?.UpdateColumnHeaderHeight();
- picker._pickerContainer?.UpdateColumnHeaderDraw();
- picker._pickerContainer?.UpdateColumnHeaderDividerColor();
-#if ANDROID || IOS
- //// While adding the picker item height, the picker selected item not updated properly in android and ios. So, we have updated that.
- picker._pickerContainer?.UpdateItemHeight();
+ //// Apply the column header template to the sfpicker control.
+ if (picker.BaseColumnHeaderView.Height > 0 && picker.ColumnHeaderTemplate != null && picker._columnHeaderLayout == null)
+ {
+ picker.AddorRemoveColumnHeaderLayout();
+ }
+
+ if (picker.ColumnHeaderTemplate == null)
+ {
+ picker._pickerContainer?.UpdateColumnHeaderHeight();
+ picker._pickerContainer?.UpdateColumnHeaderDraw();
+ picker._pickerContainer?.UpdateColumnHeaderDividerColor();
+#if WINDOWS
+ //// When looping is enabled, the selected item is not updated properly when the column height changes in Windows. Therefore, the item height is updated.
+ if (picker.EnableLooping)
+ {
+ picker._pickerContainer?.UpdateItemHeight();
+ }
+#elif ANDROID || IOS
+ //// While adding the picker item height, the picker selected item not updated properly in android and ios. So, we have updated that.
+ picker._pickerContainer?.UpdateItemHeight();
#endif
+ }
}
///
@@ -1481,13 +1655,64 @@ static void OnRelativePositionChanged(BindableObject bindable, object oldValue,
}
}
+ ///
+ /// Method invokes on the picker enable looping changed.
+ ///
+ /// The picker setting object.
+ /// Property old value.
+ /// Property new value.
+ static void OnEnableLoopingChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ PickerBase? picker = bindable as PickerBase;
+ if (picker == null)
+ {
+ return;
+ }
+
+ picker._pickerContainer?.UpdateEnableLooping();
+ }
+
+ ///
+ /// Update the PopupWidth.
+ ///
+ /// the Picker settings object
+ /// Property old value
+ /// Property new value
+ static void OnPopupWidthPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ PickerBase? picker = bindable as PickerBase;
+ if (picker == null)
+ {
+ return;
+ }
+
+ picker.UpdatePopupSize();
+ }
+
+ ///
+ /// Update the PopupHeight.
+ ///
+ /// the Picker settings object
+ /// Property old value
+ /// Property new value
+ static void OnPopupHeightPropertyChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ PickerBase? picker = bindable as PickerBase;
+ if (picker == null)
+ {
+ return;
+ }
+
+ picker.UpdatePopupSize();
+ }
+
///
/// Method invokes on the picker popup relative view changed.
///
/// The picker setting object.
- /// Property old value.
- /// Property new value.
- static void OnRelativeViewChanged(BindableObject bindable, object oldvalue, object newvalue)
+ /// Property old value.
+ /// Property new value.
+ static void OnRelativeViewChanged(BindableObject bindable, object oldValue, object newValue)
{
PickerBase? picker = bindable as PickerBase;
if (picker == null)
@@ -1501,6 +1726,119 @@ static void OnRelativeViewChanged(BindableObject bindable, object oldvalue, obje
}
}
+ ///
+ /// Method to invoke the header template changed.
+ ///
+ /// The picker setting object.
+ /// Property old value.
+ /// Property new value.
+ static void OnHeaderTemplateChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ PickerBase? picker = bindable as PickerBase;
+ if (picker == null)
+ {
+ return;
+ }
+
+ if (picker._headerLayout != null && picker.BaseHeaderView.Height > 0 && picker.HeaderTemplate != null)
+ {
+ picker._headerLayout.InitializeTemplateView();
+ }
+ }
+
+ ///
+ /// Method to invoke the column header template changed.
+ ///
+ /// The picker setting object.
+ /// Property old value.
+ /// Property new value.
+ static void OnColumnHeaderTemplateChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ PickerBase? picker = bindable as PickerBase;
+ if (picker == null)
+ {
+ return;
+ }
+
+ if (picker.BaseColumnHeaderView.Height > 0 && picker.ColumnHeaderTemplate != null)
+ {
+ picker.AddorRemoveColumnHeaderLayout();
+ }
+ }
+
+ ///
+ /// Method to invoke the footer template view changed.
+ ///
+ /// The picker setting object.
+ /// Property old value.
+ /// Property new value.
+ static void OnFooterTemplateChanged(BindableObject bindable, object oldValue, object newValue)
+ {
+ PickerBase? picker = bindable as PickerBase;
+ if (picker == null)
+ {
+ return;
+ }
+
+ if (picker._footerLayout != null && picker.FooterView.Height > 0 && picker.FooterTemplate != null)
+ {
+ picker._footerLayout?.InitializeTemplateView();
+ }
+ }
+
+ ///
+ /// Method to set the default width for the popup
+ ///
+ /// Returns the default value of PopupWidth
+ static double GetDefaultPopupWidth(BindableObject bindable)
+ {
+ var pickerBase = (PickerBase)bindable;
+ double popupWidth = 0;
+ double width = 0;
+ int columnCount = pickerBase.BaseColumns.Count;
+ for (int i = 0; i < columnCount; i++)
+ {
+ width += pickerBase.BaseColumns[i].Width <= 0 ? 100 : pickerBase.BaseColumns[i].Width;
+ }
+
+ popupWidth = width < 200 ? 200 : width;
+
+ //// Based on the Header,Column Header and Footer Height popupwidth value gets returned.
+ if (pickerBase.BaseHeaderView.Height != 0 || pickerBase.FooterView.Height != 0 || pickerBase.BaseColumnHeaderView.Height != 0)
+ {
+ return popupWidth;
+ }
+
+ return columnCount == 0 ? 0 : popupWidth;
+ }
+
+ ///
+ /// Method to set the default height of the popup
+ ///
+ /// Returns the default value of PopupHeight
+ static double GetDefaultPopupHeight(BindableObject bindable)
+ {
+ var pickerBase = (PickerBase)bindable;
+ double popupHeight = 0;
+ int count = 0;
+ int columnCount = pickerBase.BaseColumns.Count;
+ for (int i = 0; i < columnCount; i++)
+ {
+ ICollection itemsSource = (ICollection)pickerBase.BaseColumns[i].ItemsSource;
+ if (itemsSource != null)
+ {
+ count = count < itemsSource.Count ? itemsSource.Count : count;
+ if (pickerBase.BaseColumns[i].SelectedIndex <= -1)
+ {
+ count = count + 1;
+ }
+ }
+ }
+
+ popupHeight = pickerBase.BaseHeaderView.Height + pickerBase.BaseColumnHeaderView.Height + (pickerBase.ItemHeight * (count >= 5 ? 5 : count)) + pickerBase.FooterView.Height;
+ return popupHeight;
+ }
+
///
/// Method to invokes while the item source collection changed.
///
@@ -1551,6 +1889,11 @@ void OnColumnPropertyChanged(object? sender, PickerPropertyChangedEventArgs e)
SelectionIndexChanged?.Invoke(this, new PickerSelectionChangedEventArgs { NewValue = column.SelectedIndex, OldValue = oldIndex, ColumnIndex = column._columnIndex });
column._isSelectedItemChanged = false;
column.SelectedItem = PickerHelper.GetSelectedItemDefaultValue(column);
+ if (EnableLooping && newIndex <= -1)
+ {
+ return;
+ }
+
if (newIndex == -1)
{
//// If selected index is -1, then change all column selected item value as null.
@@ -1619,9 +1962,18 @@ void OnColumnPropertyChanged(object? sender, PickerPropertyChangedEventArgs e)
column.Parent = this;
if (column.Parent is SfPicker)
{
- column.SelectedIndex = -1;
- column._isSelectedItemChanged = true;
- return;
+ if (!this.EnableLooping)
+ {
+ column.SelectedIndex = -1;
+ column._isSelectedItemChanged = true;
+ return;
+ }
+ else
+ {
+ _pickerContainer?.UpdateScrollViewDraw();
+ _pickerContainer?.InvalidateDrawable();
+ return;
+ }
}
else
{
@@ -1975,7 +2327,7 @@ void OnFooterSettingsPropertyChanged(object? sender, PickerPropertyChangedEventA
{
if (e.PropertyName == nameof(PickerFooterView.Background))
{
- if (_footerLayout == null)
+ if (_footerLayout == null || FooterTemplate != null)
{
return;
}
@@ -2097,7 +2449,28 @@ void OnColumnHeaderViewPropertyChanged(object? sender, PickerPropertyChangedEven
{
if (e.PropertyName == nameof(PickerColumnHeaderView.Height))
{
- _pickerContainer?.UpdateColumnHeaderHeight();
+ if (ColumnHeaderTemplate != null)
+ {
+ AddorRemoveColumnHeaderLayout();
+ UpdatePopupSize();
+ if (_availableSize == Size.Zero)
+ {
+ return;
+ }
+
+ InvalidatePickerView();
+ }
+ else
+ {
+ _pickerContainer?.UpdateColumnHeaderHeight();
+#if WINDOWS || MACCATALYST
+ if (EnableLooping)
+ {
+ _pickerContainer?.UpdateItemHeight();
+ }
+#endif
+ }
+
#if ANDROID || IOS
//// While adding the picker item height, the picker selected item not updated properly in android and ios. So, we have updated that.
_pickerContainer?.UpdateItemHeight();
@@ -2161,6 +2534,12 @@ void OnPickerPropertyChanged(object? sender, PropertyChangedEventArgs e)
SelectedTextStyle = UpdateSelectedTextStyle();
_isInternalPropertyChange = false;
}
+#if ANDROID
+ else if (e.PropertyName == nameof(HeightRequest))
+ {
+ _pickerContainer?.UpdateItemHeight();
+ }
+#endif
}
///
@@ -2265,7 +2644,7 @@ static ITextElement GetPickerDisabledTextStyle(BindableObject bindable)
PickerTextStyle pickerTextStyle = new PickerTextStyle()
{
FontSize = 14,
- TextColor = Color.FromArgb("#1C1B1F61"),
+ TextColor = Color.FromArgb("#611c1b1f"),
Parent = pickerBase,
};
diff --git a/maui/src/Picker/PickerBase.cs b/maui/src/Picker/PickerBase.cs
index fefca5d5..b4fc2e92 100644
--- a/maui/src/Picker/PickerBase.cs
+++ b/maui/src/Picker/PickerBase.cs
@@ -42,6 +42,11 @@ public abstract partial class PickerBase : SfView, IPicker
///
FooterLayout? _footerLayout;
+ ///
+ /// The column header layout contains the text of the column header.
+ ///
+ ColumnHeaderLayout? _columnHeaderLayout;
+
///
/// The picker container contains picker view layouts.
///
@@ -91,6 +96,18 @@ public abstract partial class PickerBase : SfView, IPicker
#endregion
+ #region Constructor
+#if IOS
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ protected PickerBase()
+ {
+ IgnoreSafeArea = true;
+ }
+#endif
+ #endregion
+
#region Internal Properties
///
@@ -264,6 +281,23 @@ void AddOrRemoveHeaderLayout()
}
}
+ ///
+ /// Method to add or remove column header layout.
+ ///
+ void AddorRemoveColumnHeaderLayout()
+ {
+ if (BaseColumnHeaderView.Height > 0 && _columnHeaderLayout == null && ColumnHeaderTemplate != null)
+ {
+ _columnHeaderLayout = new ColumnHeaderLayout(this, string.Empty);
+ _pickerStackLayout?.Children.Insert(1, _columnHeaderLayout);
+ }
+ else if (_columnHeaderLayout != null && BaseColumnHeaderView.Height <= 0 && ColumnHeaderTemplate != null)
+ {
+ _pickerStackLayout?.Children.Remove(_columnHeaderLayout);
+ _columnHeaderLayout = null;
+ }
+ }
+
///
/// Method to add or remove footer layout.
///
@@ -289,6 +323,12 @@ void InvalidatePickerView()
{
InvalidateMeasure();
_pickerStackLayout?.InvalidateView();
+#if WINDOWS || MACCATALYST
+ if (EnableLooping)
+ {
+ _pickerContainer?.UpdateItemHeight();
+ }
+#endif
}
///
@@ -526,26 +566,8 @@ void UpdatePopupSize()
return;
}
- double width = 0;
- int count = 0;
- int columnCount = BaseColumns.Count;
- for (int i = 0; i < columnCount; i++)
- {
- ICollection itemsSource = (ICollection)BaseColumns[i].ItemsSource;
- if (itemsSource != null)
- {
- count = count < itemsSource.Count ? itemsSource.Count : count;
- if (BaseColumns[i].SelectedIndex <= -1)
- {
- count = count + 1;
- }
- }
-
- width += BaseColumns[i].Width <= 0 ? 100 : BaseColumns[i].Width;
- }
-
- _popup.WidthRequest = width < 200 ? 200 : width;
- _popup.HeightRequest = BaseHeaderView.Height + BaseColumnHeaderView.Height + (ItemHeight * (count >= 5 ? 5 : count)) + FooterView.Height;
+ _popup.WidthRequest = PopupWidth == 0 ? GetDefaultPopupWidth(this) : PopupWidth;
+ _popup.HeightRequest = PopupHeight == 0 ? GetDefaultPopupHeight(this) : PopupHeight;
}
///
@@ -641,6 +663,13 @@ protected override Size MeasureContent(double widthConstraint, double heightCons
HeightRequest = height;
}
+#if IOS
+ if (DesiredSize.Width == width && DesiredSize.Height == height && EnableLooping)
+ {
+ return new Size(width, height);
+ }
+#endif
+
foreach (var child in Children)
{
child.Measure(width, height);
@@ -881,6 +910,21 @@ void IPicker.UpdateSelectedIndexValue(int tappedIndex, int childIndex, bool isIn
}
pickerColumn.SelectedIndex = tappedIndex;
+ //// Call the template view for when selected value changed based on scroll the selected value.
+ if (_headerLayout != null && BaseHeaderView.Height > 0 && HeaderTemplate != null)
+ {
+ _headerLayout?.InitializeTemplateView();
+ }
+
+ if (_columnHeaderLayout != null && BaseColumnHeaderView.Height > 0 && ColumnHeaderTemplate != null)
+ {
+ _columnHeaderLayout?.InitializeTemplateView();
+ }
+
+ if (_footerLayout != null && FooterView.Height > 0 && FooterTemplate != null)
+ {
+ _footerLayout?.InitializeTemplateView();
+ }
}
///
diff --git a/maui/src/Picker/PickerStackLayout.cs b/maui/src/Picker/PickerStackLayout.cs
index 7f4b1a09..65da01d6 100644
--- a/maui/src/Picker/PickerStackLayout.cs
+++ b/maui/src/Picker/PickerStackLayout.cs
@@ -29,6 +29,9 @@ internal PickerStackLayout(IPicker pickerInfo)
//// Due to this inconsistent behavior in windows, set flow direction to LTR for the inner layout of the calendar, so we manually arrange and draw child elements for all the platforms as common.
//// In the Windows platform, the draw view does not arrange based on the flow direction. https://github.com/dotnet/maui/issues/6978
FlowDirection = FlowDirection.LeftToRight;
+#if IOS
+ IgnoreSafeArea = true;
+#endif
}
#endregion
@@ -78,15 +81,32 @@ protected override Size MeasureContent(double widthConstraint, double heightCons
double height = double.IsFinite(heightConstraint) ? heightConstraint : 300;
double headerHeight = GetValidHeight(_pickerInfo.HeaderView.Height);
double footerHeight = GetValidHeight(_pickerInfo.FooterView.Height);
+ double columnHeaderHeight = 0;
+ if (_pickerInfo.ColumnHeaderTemplate != null)
+ {
+ columnHeaderHeight = GetValidHeight(_pickerInfo.ColumnHeaderView.Height);
+ }
+
foreach (var child in Children)
{
if (child is HeaderLayout)
{
child.Measure(width, headerHeight);
}
+ else if (child is ColumnHeaderLayout)
+ {
+ child.Measure(width, columnHeaderHeight);
+ }
else if (child is PickerContainer)
{
- child.Measure(width, height - footerHeight - headerHeight);
+ if (_pickerInfo.ColumnHeaderTemplate != null)
+ {
+ child.Measure(width, height - footerHeight - columnHeaderHeight - headerHeight);
+ }
+ else
+ {
+ child.Measure(width, height - footerHeight - headerHeight);
+ }
}
else if (child is FooterLayout)
{
@@ -108,15 +128,32 @@ protected override Size ArrangeContent(Rect bounds)
double topPosition = bounds.Top;
double headerHeight = GetValidHeight(_pickerInfo.HeaderView.Height);
double footerHeight = GetValidHeight(_pickerInfo.FooterView.Height);
+ double columnHeaderHeight = 0;
+ if (_pickerInfo.ColumnHeaderTemplate != null)
+ {
+ columnHeaderHeight = GetValidHeight(_pickerInfo.ColumnHeaderView.Height);
+ }
+
foreach (var child in Children)
{
if (child is HeaderLayout)
{
child.Arrange(new Rect(bounds.Left, topPosition, width, headerHeight));
}
+ else if (child is ColumnHeaderLayout)
+ {
+ child.Arrange(new Rect(bounds.Left, topPosition + headerHeight, width, columnHeaderHeight));
+ }
else if (child is PickerContainer)
{
- child.Arrange(new Rect(bounds.Left, topPosition + headerHeight, width, bounds.Height - headerHeight - footerHeight));
+ if (_pickerInfo.ColumnHeaderTemplate != null)
+ {
+ child.Arrange(new Rect(bounds.Left, topPosition + headerHeight + columnHeaderHeight, width, bounds.Height - headerHeight - columnHeaderHeight - footerHeight));
+ }
+ else
+ {
+ child.Arrange(new Rect(bounds.Left, topPosition + headerHeight, width, bounds.Height - headerHeight - footerHeight));
+ }
}
else if (child is FooterLayout)
{
diff --git a/maui/src/Picker/SfDatePicker.cs b/maui/src/Picker/SfDatePicker.cs
index 83fd463f..6cb1a6f1 100644
--- a/maui/src/Picker/SfDatePicker.cs
+++ b/maui/src/Picker/SfDatePicker.cs
@@ -1720,9 +1720,20 @@ static void OnColumnHeaderViewChanged(BindableObject bindable, object oldValue,
TextStyle = newStyle.TextStyle,
};
- picker._dayColumn.HeaderText = SfPickerResources.GetLocalizedString(picker.ColumnHeaderView.DayHeaderText);
- picker._monthColumn.HeaderText = SfPickerResources.GetLocalizedString(picker.ColumnHeaderView.MonthHeaderText);
- picker._yearColumn.HeaderText = SfPickerResources.GetLocalizedString(picker.ColumnHeaderView.YearHeaderText);
+ if (picker._dayColumn != null)
+ {
+ picker._dayColumn.HeaderText = SfPickerResources.GetLocalizedString(picker.ColumnHeaderView.DayHeaderText);
+ }
+
+ if (picker._monthColumn != null)
+ {
+ picker._monthColumn.HeaderText = SfPickerResources.GetLocalizedString(picker.ColumnHeaderView.MonthHeaderText);
+ }
+
+ if (picker._yearColumn != null)
+ {
+ picker._yearColumn.HeaderText = SfPickerResources.GetLocalizedString(picker.ColumnHeaderView.YearHeaderText);
+ }
}
}
@@ -1878,7 +1889,7 @@ static void OnMaximumDatePropertyChanged(BindableObject bindable, object oldValu
static void OnDayIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfDatePicker? datepicker = bindable as SfDatePicker;
- if (datepicker == null)
+ if (datepicker == null || (int)newValue <= 0)
{
return;
}
@@ -1908,7 +1919,7 @@ static void OnDayIntervalPropertyChanged(BindableObject bindable, object oldValu
static void OnMonthIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfDatePicker? datepicker = bindable as SfDatePicker;
- if (datepicker == null)
+ if (datepicker == null || (int)newValue <= 0)
{
return;
}
@@ -1938,7 +1949,7 @@ static void OnMonthIntervalPropertyChanged(BindableObject bindable, object oldVa
static void OnYearIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfDatePicker? datepicker = bindable as SfDatePicker;
- if (datepicker == null)
+ if (datepicker == null || (int)newValue <= 0)
{
return;
}
diff --git a/maui/src/Picker/SfDateTimePicker.cs b/maui/src/Picker/SfDateTimePicker.cs
index d7fa9581..0c100481 100644
--- a/maui/src/Picker/SfDateTimePicker.cs
+++ b/maui/src/Picker/SfDateTimePicker.cs
@@ -2647,7 +2647,7 @@ static void OnMaximumDatePropertyChanged(BindableObject bindable, object oldValu
static void OnDayIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfDateTimePicker? picker = bindable as SfDateTimePicker;
- if (picker == null)
+ if (picker == null || (int)newValue <= 0)
{
return;
}
@@ -2677,7 +2677,7 @@ static void OnDayIntervalPropertyChanged(BindableObject bindable, object oldValu
static void OnMonthIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfDateTimePicker? picker = bindable as SfDateTimePicker;
- if (picker == null)
+ if (picker == null || (int)newValue <= 0)
{
return;
}
@@ -2707,7 +2707,7 @@ static void OnMonthIntervalPropertyChanged(BindableObject bindable, object oldVa
static void OnYearIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfDateTimePicker? picker = bindable as SfDateTimePicker;
- if (picker == null)
+ if (picker == null || (int)newValue <= 0)
{
return;
}
@@ -2736,7 +2736,7 @@ static void OnYearIntervalPropertyChanged(BindableObject bindable, object oldVal
static void OnHourIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfDateTimePicker? picker = bindable as SfDateTimePicker;
- if (picker == null)
+ if (picker == null || (int)newValue <= 0)
{
return;
}
@@ -2765,7 +2765,7 @@ static void OnHourIntervalPropertyChanged(BindableObject bindable, object oldVal
static void OnMinuteIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfDateTimePicker? picker = bindable as SfDateTimePicker;
- if (picker == null)
+ if (picker == null || (int)newValue <= 0)
{
return;
}
@@ -2798,7 +2798,7 @@ static void OnMinuteIntervalPropertyChanged(BindableObject bindable, object oldV
static void OnSecondIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfDateTimePicker? picker = bindable as SfDateTimePicker;
- if (picker == null)
+ if (picker == null || (int)newValue <= 0)
{
return;
}
diff --git a/maui/src/Picker/SfTimePicker.cs b/maui/src/Picker/SfTimePicker.cs
index a09a68d9..1e0bf113 100644
--- a/maui/src/Picker/SfTimePicker.cs
+++ b/maui/src/Picker/SfTimePicker.cs
@@ -1870,7 +1870,7 @@ static void OnFormatPropertyChanged(BindableObject bindable, object oldValue, ob
static void OnHourIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfTimePicker? picker = bindable as SfTimePicker;
- if (picker == null)
+ if (picker == null || (int)newValue <= 0)
{
return;
}
@@ -1898,7 +1898,7 @@ static void OnHourIntervalPropertyChanged(BindableObject bindable, object oldVal
static void OnMinuteIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfTimePicker? picker = bindable as SfTimePicker;
- if (picker == null)
+ if (picker == null || (int)newValue <= 0)
{
return;
}
@@ -1925,7 +1925,7 @@ static void OnMinuteIntervalPropertyChanged(BindableObject bindable, object oldV
static void OnSecondIntervalPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
SfTimePicker? picker = bindable as SfTimePicker;
- if (picker == null)
+ if (picker == null || (int)newValue <= 0)
{
return;
}
diff --git a/maui/src/Picker/Views/FooterLayout/FooterLayout.cs b/maui/src/Picker/Views/FooterLayout/FooterLayout.cs
index 11f93491..68e53704 100644
--- a/maui/src/Picker/Views/FooterLayout/FooterLayout.cs
+++ b/maui/src/Picker/Views/FooterLayout/FooterLayout.cs
@@ -67,8 +67,20 @@ internal FooterLayout(IFooterView footerViewInfo)
{
DrawingOrder = DrawingOrder.AboveContent;
_footerViewInfo = footerViewInfo;
- AddOrRemoveFooterButtons();
+ //// Create a template view by checking the template property else default view is created.
+ if (_footerViewInfo.FooterTemplate != null)
+ {
+ InitializeTemplateView();
+ }
+ else
+ {
+ AddOrRemoveFooterButtons();
+ }
+
ThemeElement.InitializeThemeResources(this, "SfPickerTheme");
+#if IOS
+ IgnoreSafeArea = true;
+#endif
}
#endregion
@@ -80,6 +92,12 @@ internal FooterLayout(IFooterView footerViewInfo)
///
internal void AddOrRemoveFooterButtons()
{
+ //// Need to prevent the add confirm or cancel button view in footerlayout once template is not null.
+ if (_footerViewInfo.FooterTemplate != null)
+ {
+ return;
+ }
+
if (_footerViewInfo.FooterView.ShowOkButton)
{
AddConfirmButton();
@@ -106,14 +124,14 @@ internal void RemoveFooterButtons()
///
internal void UpdateButtonTextStyle()
{
- if (_cancelButtonView != null)
+ if (_cancelButtonView != null && _footerViewInfo.FooterTemplate == null)
{
_cancelButtonView.UpdateStyle(_footerViewInfo.FooterView.TextStyle);
_cancelButtonView.HighlightTextColor = _footerViewInfo.FooterView.TextStyle.TextColor;
_cancelButtonView.TextStyle.FontAutoScalingEnabled = _footerViewInfo.FooterView.TextStyle.FontAutoScalingEnabled;
}
- if (_confirmButtonView != null)
+ if (_confirmButtonView != null && _footerViewInfo.FooterTemplate == null)
{
_confirmButtonView.UpdateStyle(_footerViewInfo.FooterView.TextStyle);
_confirmButtonView.HighlightTextColor = _footerViewInfo.FooterView.TextStyle.TextColor;
@@ -134,7 +152,7 @@ internal void UpdateSeparatorColor()
///
internal void UpdateConfirmButtonText()
{
- if (_confirmButtonView == null)
+ if (_confirmButtonView == null || _footerViewInfo.FooterTemplate != null)
{
return;
}
@@ -150,7 +168,7 @@ internal void UpdateConfirmButtonText()
///
internal void UpdateCancelButtonText()
{
- if (_cancelButtonView == null)
+ if (_cancelButtonView == null || _footerViewInfo.FooterTemplate != null)
{
return;
}
@@ -172,6 +190,51 @@ internal void UpdateFooterStyle()
UpdateButtonTextStyle();
}
+ ///
+ /// Method to create a template view.
+ ///
+ internal void InitializeTemplateView()
+ {
+ View? footerTemplateView = null;
+ if (_footerViewInfo.FooterTemplate == null)
+ {
+ return;
+ }
+
+ //// Clear the previous data in footerlayout.
+ if (Children.Count > 0)
+ {
+ for (int i = Children.Count - 1; i >= 0; i--)
+ {
+ Children.RemoveAt(i);
+ }
+ }
+
+ DataTemplate footerTemplate = _footerViewInfo.FooterTemplate;
+
+ switch (_footerViewInfo)
+ {
+ case SfDatePicker datepicker:
+ footerTemplateView = PickerHelper.CreateLayoutTemplateViews(footerTemplate, _footerViewInfo.FooterView, datepicker);
+ break;
+ case SfDateTimePicker:
+ SfDateTimePicker datetimepicker = (SfDateTimePicker)_footerViewInfo;
+ footerTemplateView = PickerHelper.CreateLayoutTemplateViews(footerTemplate, _footerViewInfo.FooterView, datetimepicker);
+ break;
+ case SfPicker picker:
+ footerTemplateView = PickerHelper.CreateLayoutTemplateViews(footerTemplate, _footerViewInfo.FooterView, picker);
+ break;
+ case SfTimePicker timepicker:
+ footerTemplateView = PickerHelper.CreateLayoutTemplateViews(footerTemplate, _footerViewInfo.FooterView, timepicker);
+ break;
+ }
+
+ if (footerTemplateView != null && Children.Count == 0)
+ {
+ Children.Add(footerTemplateView);
+ }
+ }
+
#endregion
#region Private Methods
@@ -181,7 +244,7 @@ internal void UpdateFooterStyle()
///
void AddConfirmButton()
{
- if (_confirmButtonView != null)
+ if (_confirmButtonView != null || _footerViewInfo.FooterTemplate != null)
{
return;
}
@@ -207,7 +270,7 @@ void AddConfirmButton()
///
void AddCancelButton()
{
- if (_cancelButtonView != null)
+ if (_cancelButtonView != null || _footerViewInfo.FooterTemplate != null)
{
return;
}
@@ -233,7 +296,7 @@ void AddCancelButton()
///
void RemoveConfirmButton()
{
- if (_confirmButton == null)
+ if (_confirmButton == null || _footerViewInfo.FooterTemplate != null)
{
return;
}
@@ -269,7 +332,7 @@ void RemoveConfirmButton()
///
void RemoveCancelButton()
{
- if (_cancelButton == null)
+ if (_cancelButton == null || _footerViewInfo.FooterTemplate != null)
{
return;
}
@@ -371,6 +434,17 @@ protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
/// The layout size.
protected override Size ArrangeContent(Rect bounds)
{
+ //// Arrange the template for the entire footer layout and return the size.
+ if (_footerViewInfo.FooterTemplate != null)
+ {
+ foreach (var child in Children)
+ {
+ child.Arrange(bounds);
+ }
+
+ return bounds.Size;
+ }
+
//// It holds the size of the confirm button text size.
Size confirmButtonTextSize = Size.Zero;
//// It holds the size of the cancel button text size.
@@ -460,6 +534,17 @@ protected override Size MeasureContent(double widthConstraint, double heightCons
{
double width = double.IsFinite(widthConstraint) ? widthConstraint : 0;
double height = double.IsFinite(heightConstraint) ? heightConstraint : 0;
+ //// Return the full width and height when the template has a value.
+ if (_footerViewInfo.FooterTemplate != null)
+ {
+ foreach (var child in Children)
+ {
+ child.Measure(width, height);
+ }
+
+ return new Size(width, height);
+ }
+
Size confirmButtonTextSize = Size.Zero;
Size cancelButtonTextSize = Size.Zero;
if (_footerViewInfo.FooterView.ShowOkButton && _confirmButtonView != null)
diff --git a/maui/src/Picker/Views/HeaderLayout/HeaderLayout.cs b/maui/src/Picker/Views/HeaderLayout/HeaderLayout.cs
index 27435a26..13abc97b 100644
--- a/maui/src/Picker/Views/HeaderLayout/HeaderLayout.cs
+++ b/maui/src/Picker/Views/HeaderLayout/HeaderLayout.cs
@@ -51,7 +51,18 @@ internal HeaderLayout(IHeaderView pickerInfo)
DrawingOrder = DrawingOrder.BelowContent;
}
- AddChildren();
+ //// Create a template view by checking the template property else default view is created.
+ if (_pickerInfo.HeaderTemplate != null)
+ {
+ InitializeTemplateView();
+ }
+ else
+ {
+ AddChildren();
+ }
+#if IOS
+ IgnoreSafeArea = true;
+#endif
}
#endregion
@@ -71,7 +82,7 @@ internal void InvalidateHeaderView()
///
internal void UpdateHeaderDateText()
{
- if (Children.Count != 2)
+ if (Children.Count != 2 || _pickerInfo.HeaderTemplate != null)
{
return;
}
@@ -89,7 +100,7 @@ internal void UpdateHeaderDateText()
///
internal void UpdateHeaderTimeText()
{
- if (Children.Count != 2)
+ if (Children.Count != 2 || _pickerInfo.HeaderTemplate != null)
{
return;
}
@@ -107,7 +118,7 @@ internal void UpdateHeaderTimeText()
///
internal void UpdateIconButtonTextStyle()
{
- if (Children.Count != 2)
+ if (Children.Count != 2 || _pickerInfo.HeaderTemplate != null)
{
return;
}
@@ -131,6 +142,53 @@ internal void ResetHeaderHighlight()
OnDateButtonClicked("Date");
}
+ ///
+ /// Method to create a template view.
+ ///
+ internal void InitializeTemplateView()
+ {
+ View? headerTemplateView = null;
+ //// Need to ensure the template is not null
+ if (_pickerInfo.HeaderTemplate == null)
+ {
+ return;
+ }
+
+ //// Clear the previous data in headerlayout.
+ if (Children.Count > 0)
+ {
+ for (int i = Children.Count - 1; i >= 0; i--)
+ {
+ Children.RemoveAt(i);
+ }
+ }
+
+ DataTemplate headerTemplate = _pickerInfo.HeaderTemplate;
+
+ switch (_pickerInfo)
+ {
+ case SfDatePicker datePicker:
+ headerTemplateView = PickerHelper.CreateLayoutTemplateViews(headerTemplate, _pickerInfo.HeaderView, datePicker);
+ break;
+ case SfDateTimePicker:
+ SfDateTimePicker dateTimePicker = (SfDateTimePicker)_pickerInfo;
+ headerTemplateView = PickerHelper.CreateLayoutTemplateViews(headerTemplate, _pickerInfo.HeaderView, dateTimePicker);
+ break;
+ case SfPicker picker:
+ headerTemplateView = PickerHelper.CreateLayoutTemplateViews(headerTemplate, _pickerInfo.HeaderView, picker);
+ break;
+ case SfTimePicker timePicker:
+ headerTemplateView = PickerHelper.CreateLayoutTemplateViews(headerTemplate, _pickerInfo.HeaderView, timePicker);
+ break;
+ }
+
+ if (headerTemplateView != null && Children.Count == 0)
+ {
+ Children.Add(headerTemplateView);
+ InvalidateDrawable();
+ }
+ }
+
#endregion
#region Private Methods
@@ -140,7 +198,7 @@ internal void ResetHeaderHighlight()
///
void AddChildren()
{
- if (Children.Count != 0)
+ if (Children.Count != 0 || _pickerInfo.HeaderTemplate != null)
{
return;
}
@@ -222,7 +280,7 @@ void OnTimeNodeButtonClicked(SemanticsNode node)
///
void OnTimeButtonClicked()
{
- if (Children.Count != 2)
+ if (Children.Count != 2 || _pickerInfo.HeaderTemplate != null)
{
return;
}
@@ -247,7 +305,7 @@ void OnTimeButtonClicked()
///
void OnDateButtonClicked()
{
- if (Children.Count != 2)
+ if (Children.Count != 2 || _pickerInfo.HeaderTemplate != null)
{
return;
}
@@ -292,13 +350,13 @@ protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
//// To avoid the separator line overlapping with the text, the height of the rectangle is reduced by the stroke thickness.
Rect rectangle = new Rect(xPosition, yPosition, width, height - (isDividerEnabled ? StrokeThickness : 0));
Color fillColor = _pickerInfo.HeaderView.Background.ToColor();
- if (fillColor != Colors.Transparent)
+ if (fillColor != Colors.Transparent && _pickerInfo.HeaderTemplate == null)
{
canvas.FillColor = fillColor;
canvas.FillRectangle(rectangle);
}
- if (!string.IsNullOrEmpty(_pickerInfo.HeaderView.Text))
+ if (!string.IsNullOrEmpty(_pickerInfo.HeaderView.Text) && _pickerInfo.HeaderTemplate == null)
{
string headerText = PickerHelper.TrimText(_pickerInfo.HeaderView.Text, width, _pickerInfo.HeaderView.TextStyle);
SemanticProperties.SetDescription(this, headerText);
@@ -339,6 +397,16 @@ protected override Size ArrangeContent(Rect bounds)
buttonXPosition = width;
}
+ if (_pickerInfo.HeaderTemplate != null)
+ {
+ foreach (var child in Children)
+ {
+ child.Arrange(new Rect(0, 0, bounds.Width, height));
+ }
+
+ return bounds.Size;
+ }
+
foreach (var child in Children)
{
child.Arrange(new Rect(buttonXPosition, 0, width, height));
@@ -373,6 +441,16 @@ protected override Size MeasureContent(double widthConstraint, double heightCons
double buttonWidth = width / 2;
//// Stroke thickness denotes the below divider line space.
double buttonHeight = height - 1;
+ if (_pickerInfo.HeaderTemplate != null)
+ {
+ foreach (var child in Children)
+ {
+ child.Measure(width, buttonHeight);
+ }
+
+ return new Size(width, height);
+ }
+
foreach (var child in Children)
{
child.Measure(buttonWidth, buttonHeight);
diff --git a/maui/src/Picker/Views/PickerLayout/ColumnHeaderLayout.cs b/maui/src/Picker/Views/PickerLayout/ColumnHeaderLayout.cs
index 34282e8a..a3a7eafb 100644
--- a/maui/src/Picker/Views/PickerLayout/ColumnHeaderLayout.cs
+++ b/maui/src/Picker/Views/PickerLayout/ColumnHeaderLayout.cs
@@ -42,7 +42,15 @@ internal ColumnHeaderLayout(IColumnHeaderView columnHeaderInfo, string headerTex
{
DrawingOrder = DrawingOrder.BelowContent;
_columnHeaderInfo = columnHeaderInfo;
+ if (columnHeaderInfo.ColumnHeaderTemplate != null)
+ {
+ InitializeTemplateView();
+ }
+
_columnHeaderText = headerText;
+#if IOS
+ IgnoreSafeArea = true;
+#endif
}
#endregion
@@ -63,7 +71,7 @@ internal void TriggerColumnHeaderDraw()
/// The column header text.
internal void UpdateColumnHeaderText(string columnHeaderText)
{
- if (_columnHeaderText == columnHeaderText)
+ if (_columnHeaderText == columnHeaderText || _columnHeaderInfo.ColumnHeaderTemplate != null)
{
return;
}
@@ -72,6 +80,48 @@ internal void UpdateColumnHeaderText(string columnHeaderText)
InvalidateDrawable();
}
+ ///
+ /// Method to create a template view.
+ ///
+ internal void InitializeTemplateView()
+ {
+ View? colmnHeaderTemplateView = null;
+ if (_columnHeaderInfo.ColumnHeaderTemplate == null)
+ {
+ return;
+ }
+
+ //// Clear the previous data in columnheaderlayout.
+ if (Children.Count > 0)
+ {
+ Children.Clear();
+ }
+
+ DataTemplate columnHeaderTemplate = _columnHeaderInfo.ColumnHeaderTemplate;
+
+ switch (_columnHeaderInfo)
+ {
+ case SfDatePicker datepicker:
+ colmnHeaderTemplateView = PickerHelper.CreateLayoutTemplateViews(columnHeaderTemplate, _columnHeaderInfo.ColumnHeaderView, datepicker);
+ break;
+ case SfDateTimePicker:
+ SfDateTimePicker datetimepicker = (SfDateTimePicker)_columnHeaderInfo;
+ colmnHeaderTemplateView = PickerHelper.CreateLayoutTemplateViews(columnHeaderTemplate, _columnHeaderInfo.ColumnHeaderView, datetimepicker);
+ break;
+ case SfPicker picker:
+ colmnHeaderTemplateView = PickerHelper.CreateLayoutTemplateViews(columnHeaderTemplate, _columnHeaderInfo.ColumnHeaderView, picker);
+ break;
+ case SfTimePicker timepicker:
+ colmnHeaderTemplateView = PickerHelper.CreateLayoutTemplateViews(columnHeaderTemplate, _columnHeaderInfo.ColumnHeaderView, timepicker);
+ break;
+ }
+
+ if (Children.Count == 0 && colmnHeaderTemplateView != null)
+ {
+ Children.Add(colmnHeaderTemplateView);
+ }
+ }
+
#endregion
#region Private Methods
@@ -122,7 +172,7 @@ static string TrimText(string text, double width, PickerTextStyle textStyle)
/// The dirtyRectangle.
protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
{
- if (dirtyRect.Width == 0 || dirtyRect.Height == 0)
+ if (dirtyRect.Width == 0 || dirtyRect.Height == 0 || _columnHeaderInfo.ColumnHeaderTemplate != null)
{
return;
}
diff --git a/maui/src/Picker/Views/PickerLayout/PickerContainer.cs b/maui/src/Picker/Views/PickerLayout/PickerContainer.cs
index 3bdce8a6..f233ce7b 100644
--- a/maui/src/Picker/Views/PickerLayout/PickerContainer.cs
+++ b/maui/src/Picker/Views/PickerLayout/PickerContainer.cs
@@ -34,6 +34,9 @@ internal PickerContainer(IPicker pickerInfo)
{
DrawingOrder = DrawingOrder.BelowContent;
_pickerInfo = pickerInfo;
+#if IOS
+ IgnoreSafeArea = true;
+#endif
}
#endregion
@@ -368,6 +371,17 @@ internal void UpdatePickerSelectionView()
UpdatePickerSelectionViewDraw();
}
+ ///
+ /// Method to update the enable looping.
+ ///
+ internal void UpdateEnableLooping()
+ {
+ foreach (PickerLayout pickerLayout in this.Children)
+ {
+ pickerLayout.UpdateEnableLooping();
+ }
+ }
+
#endregion
#region Private Methods
@@ -460,7 +474,9 @@ protected override void OnDraw(ICanvas canvas, RectF dirtyRectangle)
double columnHeaderHeight = _pickerInfo.ColumnHeaderView.Height;
//// The view port item count is calculated based on the view port height.
//// Assume the view port height is 100 and item height is 50, then the view port item count is 2.
- double viewPortItemCount = Math.Round((dirtyRectangle.Height - columnHeaderHeight) / itemHeight);
+ //// Update the viewPort ItemCount based on whether the column header template is applied.
+ //// If the template is not null, the column header layout is added as a child of the StackLayout, so adjust the viewPortItemCount position accordingly.
+ double viewPortItemCount = _pickerInfo.ColumnHeaderTemplate != null ? Math.Round(dirtyRectangle.Height / itemHeight) : Math.Round((dirtyRectangle.Height - columnHeaderHeight) / itemHeight);
//// The selectionIndex count is calculated based on the view port item count.
//// Assume the view port item count is 2, then the top count is 0.
int selectionIndex = (int)Math.Ceiling(viewPortItemCount / 2) - 1;
@@ -473,7 +489,9 @@ protected override void OnDraw(ICanvas canvas, RectF dirtyRectangle)
//// The top padding is calculated based on the top count and item height.
//// Assume the selectionIndex is 0 and item height is 50, then the top padding is 0.
- double yPosition = (selectionIndex * itemHeight) + columnHeaderHeight;
+ //// Update the Y position based on whether the column header template is applied.
+ //// If the template is not null, the column header layout is added as a child of the StackLayout, so adjust the Y position accordingly.
+ double yPosition = _pickerInfo.ColumnHeaderTemplate != null ? (selectionIndex * itemHeight) : (selectionIndex * itemHeight) + columnHeaderHeight;
float xPosition = dirtyRectangle.Left;
float width = dirtyRectangle.Width;
float totalColumnWidth = (float)GetDefaultColumnWidth(width).X;
diff --git a/maui/src/Picker/Views/PickerLayout/PickerDrawableView.cs b/maui/src/Picker/Views/PickerLayout/PickerDrawableView.cs
index 938ac4ec..5c38e579 100644
--- a/maui/src/Picker/Views/PickerLayout/PickerDrawableView.cs
+++ b/maui/src/Picker/Views/PickerLayout/PickerDrawableView.cs
@@ -118,6 +118,113 @@ internal void UpdatePickerViewDraw()
#region Private Methods
+ ///
+ /// Method to draw the item source element inside the scroll view.
+ ///
+ /// The canvas.
+ /// The dirty rect.
+ /// The selected index.
+ /// The yposition.
+ /// The item height.
+ /// The maximum distance.
+ /// The unselected textstyle.
+ /// The selected textstyle.
+ /// The minimum threshold font size.
+ void DrawItem(ICanvas canvas, RectF dirtyRect, int index, double yPosition, double itemHeight, int maxDistance, PickerTextStyle unselectedTextStyle, PickerTextStyle selectedTextStyle, float minimumThresholdFontSize)
+ {
+ Rect rectangle = new Rect(0, yPosition, dirtyRect.Width, itemHeight);
+ PickerTextStyle blackoutStyle = _pickerLayoutInfo.PickerInfo.DisabledTextStyle;
+ blackoutStyle.FontSize = unselectedTextStyle.FontSize;
+
+ bool isSelected = index == _selectedIndex;
+
+ if (isSelected)
+ {
+ PickerTextStyle defaultSelectedStyle = new PickerTextStyle { FontSize = selectedTextStyle.FontSize, TextColor = selectedTextStyle.TextColor, FontFamily = selectedTextStyle.FontFamily, FontAttributes = selectedTextStyle.FontAttributes, FontAutoScalingEnabled = selectedTextStyle.FontAutoScalingEnabled };
+ if (_pickerLayoutInfo.Column.SelectedItem == null || _pickerLayoutInfo.Column.SelectedIndex <= -1)
+ {
+ defaultSelectedStyle = _pickerLayoutInfo.PickerInfo.TextStyle;
+ }
+
+ canvas.DrawText(_sizeBasedItemsSource[index], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, defaultSelectedStyle);
+ }
+ else
+ {
+ int distance = Math.Abs(index - _selectedIndex);
+ if (_pickerLayoutInfo.PickerInfo.EnableLooping && distance > _sizeBasedItemsSource.Count / 2)
+ {
+ distance = _sizeBasedItemsSource.Count - distance;
+ }
+
+ if (distance <= maxDistance || _pickerLayoutInfo.PickerInfo.EnableLooping)
+ {
+ switch (_pickerLayoutInfo.PickerInfo.TextDisplayMode)
+ {
+ case PickerTextDisplayMode.Default:
+ PickerTextStyle textStyle = unselectedTextStyle;
+ canvas.DrawText(_sizeBasedItemsSource[index], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, UpdateBlackoutStyle(_sizeBasedItemsSource[index], false) ? blackoutStyle : textStyle);
+ break;
+
+ case PickerTextDisplayMode.Fade:
+ {
+ PickerTextStyle fadeStyle = new PickerTextStyle()
+ {
+ TextColor = unselectedTextStyle.TextColor,
+ FontSize = unselectedTextStyle.FontSize,
+ FontAttributes = unselectedTextStyle.FontAttributes,
+ FontFamily = unselectedTextStyle.FontFamily,
+ FontAutoScalingEnabled = unselectedTextStyle.FontAutoScalingEnabled,
+ };
+
+ float opacity = 1.0f - (distance / (float)maxDistance);
+ fadeStyle.TextColor = fadeStyle.TextColor.WithAlpha(opacity);
+ canvas.DrawText(_sizeBasedItemsSource[index], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, UpdateBlackoutStyle(_sizeBasedItemsSource[index], false) ? blackoutStyle : fadeStyle);
+ }
+
+ break;
+
+ case PickerTextDisplayMode.Shrink:
+ {
+ PickerTextStyle shrinkStyle = new PickerTextStyle()
+ {
+ TextColor = unselectedTextStyle.TextColor,
+ FontSize = unselectedTextStyle.FontSize,
+ FontAttributes = unselectedTextStyle.FontAttributes,
+ FontFamily = unselectedTextStyle.FontFamily,
+ FontAutoScalingEnabled = unselectedTextStyle.FontAutoScalingEnabled,
+ };
+
+ float size = (float)(shrinkStyle.FontSize - (distance / (float)maxDistance * (shrinkStyle.FontSize - minimumThresholdFontSize)));
+ shrinkStyle.FontSize = Math.Max(minimumThresholdFontSize, size);
+ canvas.DrawText(_sizeBasedItemsSource[index], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, UpdateBlackoutStyle(_sizeBasedItemsSource[index], false) ? blackoutStyle : shrinkStyle);
+ }
+
+ break;
+
+ case PickerTextDisplayMode.FadeAndShrink:
+ {
+ PickerTextStyle fadeandShrinkStyle = new PickerTextStyle()
+ {
+ TextColor = unselectedTextStyle.TextColor,
+ FontSize = unselectedTextStyle.FontSize,
+ FontAttributes = unselectedTextStyle.FontAttributes,
+ FontFamily = unselectedTextStyle.FontFamily,
+ FontAutoScalingEnabled = unselectedTextStyle.FontAutoScalingEnabled,
+ };
+
+ float opacity = 1.0f - (distance / (float)maxDistance);
+ float size = (float)(fadeandShrinkStyle.FontSize - (distance / (float)maxDistance * (fadeandShrinkStyle.FontSize - minimumThresholdFontSize)));
+ fadeandShrinkStyle.FontSize = Math.Max(minimumThresholdFontSize, size);
+ fadeandShrinkStyle.TextColor = fadeandShrinkStyle.TextColor.WithAlpha(opacity);
+ canvas.DrawText(_sizeBasedItemsSource[index], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, UpdateBlackoutStyle(_sizeBasedItemsSource[index], false) ? blackoutStyle : fadeandShrinkStyle);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
///
/// Used for checking whether value is disabled or not.
///
@@ -195,9 +302,12 @@ protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
double maxTextWidth = _drawnWidth - padding;
foreach (string value in _itemsSource)
{
- //// Initially checks that the given value is need to be trimmed by using font size and text length.
- string unSelectedText = value.Length * (_pickerLayoutInfo.PickerInfo.TextStyle.FontSize * 0.6) > maxTextWidth ? PickerHelper.TrimText(value, maxTextWidth, _pickerLayoutInfo.PickerInfo.TextStyle) : value;
- string selectedText = value.Length * (_pickerLayoutInfo.PickerInfo.SelectedTextStyle.FontSize * 0.6) > maxTextWidth ? PickerHelper.TrimText(value, maxTextWidth, _pickerLayoutInfo.PickerInfo.SelectedTextStyle) : value;
+ string unSelectedText = value.Length * (_pickerLayoutInfo.PickerInfo.TextStyle.FontSize * 0.6) > maxTextWidth
+ ? PickerHelper.TrimText(value, maxTextWidth, _pickerLayoutInfo.PickerInfo.TextStyle)
+ : value;
+ string selectedText = value.Length * (_pickerLayoutInfo.PickerInfo.SelectedTextStyle.FontSize * 0.6) > maxTextWidth
+ ? PickerHelper.TrimText(value, maxTextWidth, _pickerLayoutInfo.PickerInfo.SelectedTextStyle)
+ : value;
string textToAdd = unSelectedText.Length < selectedText.Length ? unSelectedText : selectedText;
_sizeBasedItemsSource.Add(textToAdd);
@@ -205,87 +315,59 @@ protected override void OnDraw(ICanvas canvas, RectF dirtyRect)
}
canvas.SaveState();
- //// The item height.
double itemHeight = _pickerLayoutInfo.PickerInfo.ItemHeight;
- double yPosition = 0;
- int startIndex = _getStartingIndex();
+ double currentViewPortCount = _pickerView.GetPickerItemViewPortCount();
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && _sizeBasedItemsSource.Count > currentViewPortCount;
float minimumThresholdFontSize = 8;
var unselectedTextStyle = _pickerLayoutInfo.PickerInfo.TextStyle;
var selectedTextStyle = _pickerLayoutInfo.PickerInfo.SelectedTextStyle;
- int maxDistance = (int)(_pickerView.GetPickerItemViewPortCount() / 2) + 1;
+ int maxDistance = (int)(currentViewPortCount / 2) + 1;
- //// Draw item source item based on the item source collection. And change the text style based on selected item and selected index.
- for (int i = startIndex; i < _sizeBasedItemsSource.Count; i++)
- {
- Rect rectangle = new Rect(0, yPosition, dirtyRect.Width, itemHeight);
- PickerTextStyle textStyle;
- PickerTextStyle blackoutStyle = _pickerLayoutInfo.PickerInfo.DisabledTextStyle;
- blackoutStyle.FontSize = unselectedTextStyle.FontSize;
+ double yPosition = 0;
+ int index;
- if (i == _selectedIndex)
+ if (enableLooping)
+ {
+ if (currentViewPortCount % 2 == 0)
{
- PickerTextStyle defaultSelectedStyle = new PickerTextStyle { FontSize = selectedTextStyle.FontSize, TextColor = selectedTextStyle.TextColor, FontFamily = selectedTextStyle.FontFamily, FontAttributes = selectedTextStyle.FontAttributes, FontAutoScalingEnabled = selectedTextStyle.FontAutoScalingEnabled };
- if (_pickerLayoutInfo.Column.SelectedItem == null || _pickerLayoutInfo.Column.SelectedIndex <= -1)
- {
- defaultSelectedStyle = _pickerLayoutInfo.PickerInfo.TextStyle;
- }
-
- canvas.DrawText(_sizeBasedItemsSource[i], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, defaultSelectedStyle);
+ index = _selectedIndex - (int)Math.Floor(currentViewPortCount / 2) + 1;
}
else
{
- int distance = Math.Abs(i - _selectedIndex);
-
- if (distance <= maxDistance)
- {
- switch (_pickerLayoutInfo.PickerInfo.TextDisplayMode)
- {
- case PickerTextDisplayMode.Default:
- textStyle = _pickerLayoutInfo.PickerInfo.TextStyle;
- canvas.DrawText(_sizeBasedItemsSource[i], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, UpdateBlackoutStyle(_sizeBasedItemsSource[i], false) ? blackoutStyle : textStyle);
- break;
-
- case PickerTextDisplayMode.Fade:
- {
- PickerTextStyle fadeStyle = new PickerTextStyle() { TextColor = unselectedTextStyle.TextColor, FontSize = unselectedTextStyle.FontSize, FontAttributes = unselectedTextStyle.FontAttributes, FontFamily = unselectedTextStyle.FontFamily, FontAutoScalingEnabled = unselectedTextStyle.FontAutoScalingEnabled };
- float opacity = 1.0f - (distance / (float)maxDistance);
- fadeStyle.TextColor = fadeStyle.TextColor.WithAlpha(opacity);
- canvas.DrawText(_sizeBasedItemsSource[i], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, UpdateBlackoutStyle(_sizeBasedItemsSource[i], false) ? blackoutStyle : fadeStyle);
- }
-
- break;
+ index = _selectedIndex - (int)Math.Floor(currentViewPortCount / 2);
+ }
- case PickerTextDisplayMode.Shrink:
- {
- PickerTextStyle shrinkStyle = new PickerTextStyle() { TextColor = unselectedTextStyle.TextColor, FontSize = unselectedTextStyle.FontSize, FontAttributes = unselectedTextStyle.FontAttributes, FontFamily = unselectedTextStyle.FontFamily, FontAutoScalingEnabled = unselectedTextStyle.FontAutoScalingEnabled };
- float size = (float)(shrinkStyle.FontSize - (distance / (float)maxDistance * (shrinkStyle.FontSize - minimumThresholdFontSize)));
- shrinkStyle.FontSize = Math.Max(minimumThresholdFontSize, size);
- canvas.DrawText(_sizeBasedItemsSource[i], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, UpdateBlackoutStyle(_sizeBasedItemsSource[i], false) ? blackoutStyle : shrinkStyle);
- }
+ if (index < 0)
+ {
+ index += _sizeBasedItemsSource.Count;
+ }
+ }
+ else
+ {
+ index = _getStartingIndex();
+ }
- break;
+ int totalItemsToDraw = (int)(dirtyRect.Height / itemHeight);
+ int itemsDrawn = 0;
- case PickerTextDisplayMode.FadeAndShrink:
- {
- PickerTextStyle fadeandShrinkStyle = new PickerTextStyle() { TextColor = unselectedTextStyle.TextColor, FontSize = unselectedTextStyle.FontSize, FontAttributes = unselectedTextStyle.FontAttributes, FontFamily = unselectedTextStyle.FontFamily, FontAutoScalingEnabled = unselectedTextStyle.FontAutoScalingEnabled };
- float opacity = 1.0f - (distance / (float)maxDistance);
- float size = (float)(fadeandShrinkStyle.FontSize - (distance / (float)maxDistance * (fadeandShrinkStyle.FontSize - minimumThresholdFontSize)));
- fadeandShrinkStyle.FontSize = Math.Max(minimumThresholdFontSize, size);
- fadeandShrinkStyle.TextColor = fadeandShrinkStyle.TextColor.WithAlpha(opacity);
- canvas.DrawText(_sizeBasedItemsSource[i], rectangle, HorizontalAlignment.Center, VerticalAlignment.Center, UpdateBlackoutStyle(_sizeBasedItemsSource[i], false) ? blackoutStyle : fadeandShrinkStyle);
- }
-
- break;
- }
- }
+ while (itemsDrawn < totalItemsToDraw)
+ {
+ if (enableLooping && index >= _sizeBasedItemsSource.Count)
+ {
+ index = 0;
}
- yPosition += itemHeight;
- if (yPosition >= dirtyRect.Height)
+ if (!enableLooping && index >= _sizeBasedItemsSource.Count)
{
break;
}
+
+ DrawItem(canvas, dirtyRect, index, yPosition, itemHeight, maxDistance, unselectedTextStyle, selectedTextStyle, minimumThresholdFontSize);
+
+ index++;
+ yPosition += itemHeight;
+ itemsDrawn++;
}
canvas.RestoreState();
diff --git a/maui/src/Picker/Views/PickerLayout/PickerLayout.cs b/maui/src/Picker/Views/PickerLayout/PickerLayout.cs
index 560dbebe..284ff203 100644
--- a/maui/src/Picker/Views/PickerLayout/PickerLayout.cs
+++ b/maui/src/Picker/Views/PickerLayout/PickerLayout.cs
@@ -54,8 +54,15 @@ internal PickerLayout(IPicker pickerViewInfo, PickerColumn pickerColumn)
_itemsSource = new ObservableCollection();
UpdateItemSource(false);
DrawingOrder = DrawingOrder.AboveContent;
- ColumnHeaderLayout columnHeaderLayout = new ColumnHeaderLayout(_pickerViewInfo, _column.HeaderText);
- Add(columnHeaderLayout);
+#if IOS
+ IgnoreSafeArea = true;
+#endif
+ if (_pickerViewInfo.ColumnHeaderTemplate == null)
+ {
+ ColumnHeaderLayout columnHeaderLayout = new ColumnHeaderLayout(_pickerViewInfo, _column.HeaderText);
+ Add(columnHeaderLayout);
+ }
+
_pickerScrollView = new PickerScrollView(this, _pickerViewInfo, _itemsSource);
Add(_pickerScrollView);
}
@@ -289,6 +296,20 @@ internal void UpdateKeyboardLayoutFocus()
}
}
+ ///
+ /// Method to update the enable looping.
+ ///
+ internal void UpdateEnableLooping()
+ {
+ foreach (var child in this.Children)
+ {
+ if (child is PickerScrollView pickerScrollView)
+ {
+ pickerScrollView.UpdateEnableLooping();
+ }
+ }
+ }
+
#endregion
#region Private Methods
@@ -327,15 +348,29 @@ protected override Size ArrangeContent(Rect bounds)
{
if (child is ColumnHeaderLayout)
{
- child.Arrange(new Rect(0, 0, width, headerColumHeight));
+ if (_pickerViewInfo.ColumnHeaderTemplate != null)
+ {
+ child.Arrange(new Rect(0, 0, 0, 0));
+ }
+ else
+ {
+ child.Arrange(new Rect(0, 0, width, headerColumHeight));
+ }
}
else if (child is PickerScrollView)
{
double childWidth = width - StrokeThickness;
childWidth = childWidth < 0 ? 0 : childWidth;
- double childHeight = height - headerColumHeight;
- childHeight = childHeight < 0 ? 0 : childHeight;
- child.Arrange(new Rect(0, headerColumHeight, childWidth, childHeight));
+ if (_pickerViewInfo.ColumnHeaderTemplate != null)
+ {
+ child.Arrange(new Rect(0, 0, childWidth, height));
+ }
+ else
+ {
+ double childHeight = height - headerColumHeight;
+ childHeight = childHeight < 0 ? 0 : childHeight;
+ child.Arrange(new Rect(0, headerColumHeight, childWidth, childHeight));
+ }
}
}
@@ -356,15 +391,29 @@ protected override Size MeasureContent(double widthConstraint, double heightCons
{
if (child is ColumnHeaderLayout)
{
- child.Measure(widthConstraint, headerColumHeight);
+ if (_pickerViewInfo.ColumnHeaderTemplate != null)
+ {
+ child.Measure(0, 0);
+ }
+ else
+ {
+ child.Measure(widthConstraint, headerColumHeight);
+ }
}
else if (child is PickerScrollView)
{
double childWidth = widthConstraint - StrokeThickness;
childWidth = childWidth < 0 ? 0 : childWidth;
- double childHeight = heightConstraint - headerColumHeight;
- childHeight = childHeight < 0 ? 0 : childHeight;
- child.Measure(childWidth, childHeight);
+ if (_pickerViewInfo.ColumnHeaderTemplate != null)
+ {
+ child.Measure(childWidth, heightConstraint);
+ }
+ else
+ {
+ double childHeight = heightConstraint - headerColumHeight;
+ childHeight = childHeight < 0 ? 0 : childHeight;
+ child.Measure(childWidth, childHeight);
+ }
}
}
@@ -381,7 +430,7 @@ protected override void OnDraw(ICanvas canvas, RectF dirtyRectangle)
float width = dirtyRectangle.Width;
float height = dirtyRectangle.Height;
float headerColumHeight = (float)_pickerViewInfo.ColumnHeaderView.Height;
- float topPosition = dirtyRectangle.Top + headerColumHeight;
+ float topPosition = _pickerViewInfo.ColumnHeaderTemplate != null ? dirtyRectangle.Top + 1 : dirtyRectangle.Top + headerColumHeight;
float leftPosition = dirtyRectangle.Left;
canvas.SaveState();
@@ -424,9 +473,9 @@ void IPickerLayout.UpdateSelectedIndexValue(int tappedIndex, bool isInitialLoadi
{
tappedIndex = _itemsSource.Count - 1;
}
- else if (tappedIndex < 0)
+ else if (tappedIndex <= -1)
{
- tappedIndex = 0;
+ tappedIndex = -1;
}
_pickerViewInfo.UpdateSelectedIndexValue(tappedIndex, _column._columnIndex, isInitialLoading);
diff --git a/maui/src/Picker/Views/PickerLayout/PickerScrollView.cs b/maui/src/Picker/Views/PickerLayout/PickerScrollView.cs
index 13bcc786..05455a6f 100644
--- a/maui/src/Picker/Views/PickerLayout/PickerScrollView.cs
+++ b/maui/src/Picker/Views/PickerLayout/PickerScrollView.cs
@@ -31,6 +31,18 @@ internal class PickerScrollView : SfPickerView
///
double _availableHeight = 0;
+#if WINDOWS || MACCATALYST
+ ///
+ /// Check whether the looping is enabled or not.
+ ///
+ bool _isLoopingEnabled = false;
+
+ ///
+ /// The new scroll position value.
+ ///
+ double _newScrollPosition = 0;
+#endif
+
#if __ANDROID__
///
@@ -141,7 +153,48 @@ internal void UpdateItemHeight()
double itemHeight = _pickerLayoutInfo.PickerInfo.ItemHeight;
int itemsCount = PickerHelper.GetItemsCount(_pickerLayoutInfo.Column.ItemsSource);
int selectedIndex = PickerHelper.GetValidSelectedIndex(_pickerLayoutInfo.Column.SelectedIndex, itemsCount);
- ScrollToAsync(0, selectedIndex * itemHeight, false);
+ double viewPortItemCount = Math.Round(_pickerView.GetViewPortHeight() / itemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && itemsCount > viewPortItemCount;
+ double scrollPosition = selectedIndex * itemHeight;
+ //// Check if looping is enabled and the current scroll end position is reaches the view height.
+ if (enableLooping && scrollPosition < (viewPortItemCount * itemHeight) && scrollPosition >= 0)
+ {
+ //// Adjust the scroll end position by adding the total height of all items to the current scroll end position.
+ scrollPosition = (itemsCount * itemHeight) + scrollPosition;
+#if WINDOWS || MACCATALYST
+ //// In Window and Mac, need to update the new scroll position to avoid the scroll restriction.
+ if (!_isLoopingEnabled && enableLooping)
+ {
+ _newScrollPosition = scrollPosition;
+ }
+#endif
+ }
+
+ ScrollToAsync(0, enableLooping ? scrollPosition : selectedIndex * itemHeight, false);
+ }
+
+ ///
+ /// Method to update the enable looping.
+ ///
+ internal void UpdateEnableLooping()
+ {
+#if WINDOWS || MACCATALYST
+ //// While looping is enabled, call the measure and arrange to increase the picker view height to get proper scroll position.
+ int itemsCount = PickerHelper.GetItemsCount(_pickerLayoutInfo.Column.ItemsSource);
+ double viewPortItemCount = Math.Round(_pickerView.GetViewPortHeight() / _pickerLayoutInfo.PickerInfo.ItemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && itemsCount > viewPortItemCount;
+ if (enableLooping)
+ {
+ _isLoopingEnabled = false;
+ }
+#endif
+ UpdateSelectedIndexPosition();
+ if (_pickerLayoutInfo.PickerInfo.ItemTemplate != null)
+ {
+ _pickerView.UpdateItemTemplate();
+ }
+
+ _pickerView.UpdateItemHeight();
}
///
@@ -185,10 +238,47 @@ internal void UpdateKeyboardViewFocus()
void OnViewScrolling(object? sender, ScrolledEventArgs e)
{
double itemHeight = _pickerLayoutInfo.PickerInfo.ItemHeight;
+ int itemsCount = PickerHelper.GetItemsCount(_pickerLayoutInfo.Column.ItemsSource);
+ double viewPortItemCount = Math.Round(_pickerView.GetViewPortHeight() / itemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && itemsCount > viewPortItemCount;
+ if (enableLooping)
+ {
+ double totalContentHeight = itemsCount * itemHeight;
+ //// When scrolling upward, update the scroll position based on the total content height.
+ if (e.ScrollY < (viewPortItemCount * itemHeight))
+ {
+ ScrollToAsync(0, e.ScrollY + totalContentHeight, false);
+ }
+ //// When scrolling downward, update the scroll position based on the total content height.
+ else if (e.ScrollY > totalContentHeight + (viewPortItemCount * itemHeight))
+ {
+ ScrollToAsync(0, e.ScrollY - totalContentHeight, false);
+ }
+ }
+
//// Assume the scrollY position is 109 and the item height is 20, While using Math.Round then the selectionIndex is 109 / 20 = 5.4 => 5(SelectionIndex).
//// Assume the scrollY position is 110 and the item height is 20, While using Math.Round then the selectionIndex is 110 / 20 = 5.5 => 6(SelectionIndex).
int selectionIndex = (int)Math.Round(e.ScrollY / itemHeight);
- int itemsCount = PickerHelper.GetItemsCount(_pickerLayoutInfo.Column.ItemsSource);
+#if WINDOWS || MACCATALYST
+ //// While looping change dynamically, update the selected index based on new position.
+ if (enableLooping && _isLoopingEnabled == false && (_newScrollPosition - e.ScrollY) > 0 && e.ScrollY != (itemsCount * itemHeight) - itemHeight)
+ {
+ selectionIndex = (int)Math.Round(_newScrollPosition / itemHeight);
+ _isLoopingEnabled = true;
+ _newScrollPosition = 0;
+ }
+#endif
+
+ //// While looping, check the selected index value.
+ if (enableLooping)
+ {
+ selectionIndex = selectionIndex % itemsCount;
+ if (selectionIndex < 0)
+ {
+ selectionIndex = 0;
+ }
+ }
+
selectionIndex = PickerHelper.GetValidSelectedIndex(selectionIndex, itemsCount);
if (UpdateBlackoutSelection())
{
@@ -196,6 +286,18 @@ void OnViewScrolling(object? sender, ScrolledEventArgs e)
}
_pickerView.UpdateSelectedIndexValue(selectionIndex);
+ if (enableLooping && _pickerLayoutInfo.PickerInfo.ItemTemplate != null)
+ {
+ _pickerView.UpdateItemTemplate();
+ }
+
+#if !WINDOWS
+ //// Need to trigger the measure to update the height.
+ if (enableLooping)
+ {
+ _pickerView.UpdateItemHeight();
+ }
+#endif
}
#if __ANDROID__
@@ -218,6 +320,19 @@ void OnTouchCompleted()
double selectionIndex = Math.Round(_touchEndPosition / itemHeight);
//// From above example the selectionIndex is 5. So that the current item position is 5 * 20 = 100.
double currentItemPosition = selectionIndex * itemHeight;
+ int itemsCount = PickerHelper.GetItemsCount(_pickerLayoutInfo.Column.ItemsSource);
+ double viewPortItemCount = Math.Round(_pickerView.GetViewPortHeight() / itemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && itemsCount > viewPortItemCount;
+ //// While looping, check the selected index value.
+ if (enableLooping)
+ {
+ selectionIndex = selectionIndex % itemsCount;
+ if (selectionIndex < 0)
+ {
+ selectionIndex = 0;
+ }
+ }
+
//// From above example the scrollEndPosition is 109. So that the positionDifference is 109 - 100 = 9.
double positionDifference = _touchEndPosition - currentItemPosition;
//// For windows, While using custom animation for windows, triggers scroll event. So that default scroll to async method is used.
@@ -296,11 +411,33 @@ void UpdateSelectedIndex()
double totalContentSize = (itemsCount * itemHeight) + DesiredSize.Height - itemHeight;
double scrollPosition = selectedIndex * itemHeight;
double contentHeight = Math.Floor(ContentSize.Height);
+ double viewPortItemCount = Math.Round(_pickerView.GetViewPortHeight() / itemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && itemsCount > viewPortItemCount;
if (contentHeight < Math.Floor(totalContentSize) && contentHeight - DesiredSize.Height <= Math.Ceiling(scrollPosition))
{
return;
}
+ //// Check if looping is enabled and the current scroll end position is reaches the view height.
+ if (enableLooping && scrollPosition < (viewPortItemCount * itemHeight) && scrollPosition >= 0)
+ {
+ //// Adjust the scroll end position by adding the total height of all items to the current scroll end position.
+ scrollPosition = (itemsCount * itemHeight) + scrollPosition;
+#if WINDOWS || MACCATALYST
+ if (!_isLoopingEnabled && enableLooping)
+ {
+ _newScrollPosition = scrollPosition;
+ }
+#endif
+ }
+
+#if MACCATALYST
+ if (!_isLoopingEnabled && enableLooping)
+ {
+ _isLoopingEnabled = true;
+ }
+#endif
+
//// No need to animate the scroll position while changing the selected index value.
ScrollToAsync(0, scrollPosition, false);
}
@@ -343,6 +480,21 @@ void UpdateSelectedIndexPosition()
}
double newScrollPosition = selectedIndex * itemHeight;
+ double viewPortItemCount = Math.Round(_pickerView.GetViewPortHeight() / itemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && itemsCount > viewPortItemCount;
+ //// Check if looping is enabled and the current scroll end position is reaches the view height.
+ if (enableLooping && newScrollPosition < (viewPortItemCount * itemHeight) && newScrollPosition >= 0)
+ {
+ //// Adjust the scroll end position by adding the total height of all items to the current scroll end position.
+ newScrollPosition = (itemsCount * itemHeight) + newScrollPosition;
+#if WINDOWS || MACCATALYST
+ if (!_isLoopingEnabled && enableLooping)
+ {
+ _newScrollPosition = newScrollPosition;
+ }
+#endif
+ }
+
//// Checking position rather than index because switching to default mode and change screen height and again move into dialog mode then the selected item does not shown correctly center of selection highlight.
if (newScrollPosition == ScrollY)
{
@@ -369,7 +521,9 @@ void OnScrollPropertyChanged(object? sender, PropertyChangedEventArgs e)
int itemsCount = PickerHelper.GetItemsCount(_pickerLayoutInfo.Column.ItemsSource);
double totalContentSize = (itemsCount * itemHeight) + DesiredSize.Height - itemHeight;
double availableContentSize = Math.Floor(ContentSize.Height);
- if (availableContentSize - 1 > totalContentSize || availableContentSize + 1 < totalContentSize)
+ double viewPortItemCount = Math.Round(_pickerView.GetViewPortHeight() / itemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && itemsCount > viewPortItemCount;
+ if (!enableLooping && (availableContentSize - 1 > totalContentSize || availableContentSize + 1 < totalContentSize))
{
return;
}
@@ -613,17 +767,32 @@ internal override void OnPickerViewScrollStart()
/// The scroll end position.
internal override void OnPickerViewScrollEnd(double scrollEndPosition)
{
+ double itemHeight = _pickerLayoutInfo.PickerInfo.ItemHeight;
+ int itemsCount = PickerHelper.GetItemsCount(_pickerLayoutInfo.Column.ItemsSource);
+ double viewPortItemCount = Math.Round(_pickerView.GetViewPortHeight() / itemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && itemsCount > viewPortItemCount;
+#if WINDOWS || MACCATALYST
+ if (enableLooping && _isLoopingEnabled == false && (_newScrollPosition - scrollEndPosition) > 0 && scrollEndPosition != (itemsCount * itemHeight) - itemHeight)
+ {
+ //// Set the scroll end position to the new scroll position to continue the looping behavior.
+ scrollEndPosition = _newScrollPosition;
+ }
+#endif
+ //// If the selected index is less than 0, then set the scroll end position to -40 to avoid selected index rendering in center position.
+ if (!enableLooping && _pickerLayoutInfo.Column.SelectedIndex <= -1)
+ {
+ scrollEndPosition = -40;
+ }
+
#if __ANDROID__
_touchEndPosition = scrollEndPosition;
OnTouchCompleted();
return;
#else
- double itemHeight = _pickerLayoutInfo.PickerInfo.ItemHeight;
- int itemsCount = PickerHelper.GetItemsCount(_pickerLayoutInfo.Column.ItemsSource);
double totalContentSize = (itemsCount * itemHeight) + _availableHeight - itemHeight;
//// On windows, while change mode from default(resize the height) to dialog native change, base measure automatically change the scroll position, so it moves to other positions, so checking the content size before the scroll to fix the issue.
double availableContentSize = Math.Floor(ContentSize.Height);
- if (availableContentSize - 1 > totalContentSize || availableContentSize + 1 < totalContentSize)
+ if (!enableLooping && (availableContentSize - 1 > totalContentSize || availableContentSize + 1 < totalContentSize))
{
return;
}
@@ -638,6 +807,16 @@ internal override void OnPickerViewScrollEnd(double scrollEndPosition)
double selectionIndex = Math.Round(scrollEndPosition / itemHeight);
//// From above example the selectionIndex is 5. So that the current item position is 5 * 20 = 100.
double currentItemPosition = selectionIndex * itemHeight;
+ //// While looping is enabled, ensure the selection index is within valid range.
+ if (enableLooping)
+ {
+ selectionIndex = selectionIndex % itemsCount;
+ if (selectionIndex < 0)
+ {
+ selectionIndex += itemsCount;
+ }
+ }
+
//// From above example the scrollEndPosition is 109. So that the positionDifference is 109 - 100 = 9.
double positionDifference = scrollEndPosition - currentItemPosition;
//// While scroll view fast scrolling and we started new scrolling before end of the previous scrolling then the scroll view is not scrolled to the exact position.
@@ -645,7 +824,7 @@ internal override void OnPickerViewScrollEnd(double scrollEndPosition)
void UpdateProperty(double value)
{
//// From above example the positionDifference is 9. So that the center position is 109 - (9 * 1) = 100. So that the scroll position is 100.
- double centerPosition = scrollEndPosition - (positionDifference * value);
+ double centerPosition = Math.Round(scrollEndPosition - (positionDifference * value));
//// From above example the center position is 100. So that the scroll position is 100.
//// The scroll to async does not trigger the scroll event. Because in android we handled scroll using touch event.
//// In iOS and Mac we handled scroll end using drag end event.
@@ -675,9 +854,12 @@ void Finished(double value, bool isFinished)
/// Returns size of the scroll view.
protected override Size MeasureOverride(double widthConstraint, double heightConstraint)
{
+ //// FB65959 - UpdateSelectedIndexPosition resets selection even when the size does not change.
#if MACCATALYST || IOS
if (DesiredSize.Width == widthConstraint && DesiredSize.Height == heightConstraint)
{
+ //// We need to update blackout selection for mac and ios to update when both height and width or same.
+ UpdateBlackoutSelection();
return new Size(widthConstraint, heightConstraint);
}
#endif
diff --git a/maui/src/Picker/Views/PickerLayout/PickerView.cs b/maui/src/Picker/Views/PickerLayout/PickerView.cs
index d4ec9164..fc418bb3 100644
--- a/maui/src/Picker/Views/PickerLayout/PickerView.cs
+++ b/maui/src/Picker/Views/PickerLayout/PickerView.cs
@@ -100,6 +100,9 @@ internal PickerView(IPickerLayout pickerLayoutInfo, IPicker pickerViewInfo, Obse
}
Focus();
+#if IOS
+ IgnoreSafeArea = true;
+#endif
}
#endregion
@@ -143,7 +146,13 @@ internal void UpdateSelectedIndexValue(int index)
int maximumViewPortCount = GetMaximumViewPort();
double maximumViewPortHeight = maximumViewPortCount * _pickerLayoutInfo.PickerInfo.ItemHeight;
- if (maximumViewPortHeight + GetViewPortHeight() - _pickerLayoutInfo.PickerInfo.ItemHeight < Height)
+ double viewPortItemCount = Math.Round(GetViewPortHeight() / _pickerLayoutInfo.PickerInfo.ItemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && _itemsSource.Count > viewPortItemCount;
+ if (!enableLooping && maximumViewPortHeight + GetViewPortHeight() - _pickerLayoutInfo.PickerInfo.ItemHeight < Height)
+ {
+ ArrangeContent(new Rect(0, 0, Width, Height));
+ }
+ else if (enableLooping)
{
ArrangeContent(new Rect(0, 0, Width, Height));
}
@@ -165,7 +174,13 @@ internal void UpdateSelectedIndexOnAnimationEnd(int index)
int maximumViewPortCount = GetMaximumViewPort();
double maximumViewPortHeight = maximumViewPortCount * _pickerLayoutInfo.PickerInfo.ItemHeight;
- if (maximumViewPortHeight + GetViewPortHeight() - _pickerLayoutInfo.PickerInfo.ItemHeight < Height)
+ double viewPortItemCount = Math.Round(GetViewPortHeight() / _pickerLayoutInfo.PickerInfo.ItemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && _itemsSource.Count > viewPortItemCount;
+ if (!enableLooping && maximumViewPortHeight + GetViewPortHeight() - _pickerLayoutInfo.PickerInfo.ItemHeight < Height)
+ {
+ ArrangeContent(new Rect(0, 0, Width, Height));
+ }
+ else if (enableLooping)
{
ArrangeContent(new Rect(0, 0, Width, Height));
}
@@ -301,6 +316,20 @@ internal double GetPickerItemViewPortCount()
return Math.Round(GetViewPortHeight() / itemHeight);
}
+ ///
+ /// Method to get the valid view port height.
+ ///
+ /// Returns view port height.
+ internal double GetViewPortHeight()
+ {
+ if (_pickerViewPortHeight <= 0)
+ {
+ return 0;
+ }
+
+ return _pickerViewPortHeight;
+ }
+
#endregion
#region Private Methods
@@ -369,21 +398,74 @@ int GetMaximumViewPort()
void GeneratePickerItemsTemplate()
{
DataTemplate template = _pickerLayoutInfo.PickerInfo.ItemTemplate;
- if (template is DataTemplateSelector itemTemplateSelector)
+ double currentViewPortCount = GetPickerItemViewPortCount();
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && _itemsSource.Count > currentViewPortCount;
+ if (enableLooping)
{
- for (int index = 0; index < _itemsSource.Count; index++)
+ int index = 0;
+ if (currentViewPortCount % 2 == 0)
{
- PickerItemDetails itemDetails = GetColumnDetails(index);
- DataTemplate templateSelector = itemTemplateSelector.SelectTemplate(itemDetails, this);
- CreatePickerTemplateView(templateSelector, itemDetails);
+ index = _selectedIndex - (int)Math.Floor(3 * currentViewPortCount / 2) + 1;
+ }
+ else
+ {
+ index = _selectedIndex - (int)Math.Floor(3 * currentViewPortCount / 2);
+ }
+
+ if (index < 0)
+ {
+ index += _itemsSource.Count;
+ if (index < 0)
+ {
+ index += _itemsSource.Count;
+ }
+ }
+
+ double itemHeight = _pickerLayoutInfo.PickerInfo.ItemHeight;
+ int totalItemsToDraw = (int)((currentViewPortCount * 3) * itemHeight / itemHeight);
+ int itemsDrawn = 0;
+
+ while (itemsDrawn < totalItemsToDraw)
+ {
+ if (template is DataTemplateSelector itemTemplateSelector)
+ {
+ PickerItemDetails itemDetails = GetColumnDetails(index);
+ DataTemplate templateSelector = itemTemplateSelector.SelectTemplate(itemDetails, this);
+ CreatePickerTemplateView(templateSelector, itemDetails);
+ }
+ else
+ {
+ PickerItemDetails itemDetails = GetColumnDetails(index);
+ CreatePickerTemplateView(template, itemDetails);
+ }
+
+ index++;
+ if (index >= _itemsSource.Count)
+ {
+ index = 0;
+ }
+
+ itemsDrawn++;
}
}
else
{
- for (int index = 0; index < _itemsSource.Count; index++)
+ if (template is DataTemplateSelector itemTemplateSelector)
+ {
+ for (int index = 0; index < _itemsSource.Count; index++)
+ {
+ PickerItemDetails itemDetails = GetColumnDetails(index);
+ DataTemplate templateSelector = itemTemplateSelector.SelectTemplate(itemDetails, this);
+ CreatePickerTemplateView(templateSelector, itemDetails);
+ }
+ }
+ else
{
- PickerItemDetails itemDetails = GetColumnDetails(index);
- CreatePickerTemplateView(template, itemDetails);
+ for (int index = 0; index < _itemsSource.Count; index++)
+ {
+ PickerItemDetails itemDetails = GetColumnDetails(index);
+ CreatePickerTemplateView(template, itemDetails);
+ }
}
}
}
@@ -410,20 +492,6 @@ PickerItemDetails GetColumnDetails(int index)
return columnDetails;
}
- ///
- /// Method to get the valid view port height.
- ///
- /// Returns view port height.
- double GetViewPortHeight()
- {
- if (_pickerViewPortHeight <= 0)
- {
- return 0;
- }
-
- return _pickerViewPortHeight;
- }
-
#endregion
#region Override Methods
@@ -459,6 +527,13 @@ protected override Size MeasureContent(double widthConstraint, double heightCons
int maximumViewPortCount = (int)(viewPortItemCount * 3);
//// The maximum items height based on the maximum view port count.
double pickerHeight = maximumViewPortCount * itemHeight;
+ //// Ensure the valid looping or not.
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && viewPortItemCount < _itemsSource.Count;
+ if (enableLooping && DesiredSize.Width != widthConstraint && _pickerLayoutInfo.PickerInfo.ItemTemplate != null)
+ {
+ UpdateItemTemplate();
+ }
+
foreach (var child in Children)
{
if (!(child is PickerDrawableView))
@@ -479,8 +554,8 @@ protected override Size MeasureContent(double widthConstraint, double heightCons
//// Need to add the empty space to the picker view. So by adding the picker view port height and it subtracted by single item height then we get total picker scroll view height.
//// Need to show space before 0th item and after last item for showing the selected item at middle position.
//// So that the total picker scroll view height is calculated by adding the picker view port height and it subtracted by single item height.
- DesiredSize = new Size(widthConstraint, totalHeight);
- return new Size(widthConstraint, totalHeight);
+ DesiredSize = new Size(widthConstraint, enableLooping ? totalHeight + (viewPortHeight * 2) : totalHeight);
+ return new Size(widthConstraint, enableLooping ? totalHeight + (viewPortHeight * 2) : totalHeight);
}
///
@@ -500,6 +575,8 @@ protected override Size ArrangeContent(Rect bounds)
int maximumViewPortCount = (int)(viewPortItemCount * 3);
//// The maximum items height based on the maximum view port count.
double pickerHeight = maximumViewPortCount * itemHeight;
+ //// Ensure the valid looping or not.
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && viewPortItemCount < _itemsSource.Count;
//// The drawingStartIndex is calculated based on the view port item count.
//// From above example the view port item count is 5, then the drawingStartIndex is 2. While using the Math.Ceiling the drawingStartIndex is 5/2 = 2.5 => 3.
int drawingStartIndex = (int)Math.Ceiling(viewPortItemCount / 2) - 1;
@@ -518,34 +595,47 @@ protected override Size ArrangeContent(Rect bounds)
double lastIndex = _itemsSource.Count - maximumViewPortCount;
lastIndex = lastIndex < 0 ? 0 : lastIndex;
double maxPosition = (lastIndex * itemHeight) + yPosition;
+ if (enableLooping)
+ {
+ yPosition = enableLooping ? _pickerLayoutInfo.ScrollOffset - (Math.Floor(2 * viewPortItemCount / 2) * itemHeight) : yPosition;
+ }
+
foreach (var child in Children)
{
if (child is PickerDrawableView)
{
- if (lastIndex != 0)
+ if (enableLooping)
{
- //// Get the current scrolled item based on scroll offset.
- int selectedIndex = (int)(_pickerLayoutInfo.ScrollOffset / itemHeight);
- //// Calculate the virtualized drawable view start index based on view port count and selected index.
- double startingIndex = selectedIndex - viewPortItemCount;
- startingIndex = startingIndex < 0 ? 0 : startingIndex;
- //// Does not need to move after the last index value.
- startingIndex = startingIndex > lastIndex ? lastIndex : startingIndex;
- double startPosition = startingIndex * itemHeight;
- double topPosition = startPosition + yPosition;
- if (topPosition > maxPosition)
- {
- topPosition = maxPosition;
- }
-
- _initialNodeTopPosition = topPosition;
- child.Arrange(new Rect(0, topPosition, bounds.Width, pickerHeight));
+ _initialNodeTopPosition = _pickerLayoutInfo.ScrollOffset;
+ child.Arrange(new Rect(0, _pickerLayoutInfo.ScrollOffset, bounds.Width, lastIndex != 0 ? pickerHeight : _itemsSource.Count * itemHeight));
}
else
{
- //// Arrange the drawable view only for item source items without top and bottom empty spacing.
- child.Arrange(new Rect(0, yPosition, bounds.Width, _itemsSource.Count * itemHeight));
- _initialNodeTopPosition = yPosition;
+ if (lastIndex != 0)
+ {
+ //// Get the current scrolled item based on scroll offset.
+ int selectedIndex = (int)(_pickerLayoutInfo.ScrollOffset / itemHeight);
+ //// Calculate the virtualized drawable view start index based on view port count and selected index.
+ double startingIndex = selectedIndex - viewPortItemCount;
+ startingIndex = startingIndex < 0 ? 0 : startingIndex;
+ //// Does not need to move after the last index value.
+ startingIndex = startingIndex > lastIndex ? lastIndex : startingIndex;
+ double startPosition = startingIndex * itemHeight;
+ double topPosition = startPosition + yPosition;
+ if (topPosition > maxPosition)
+ {
+ topPosition = maxPosition;
+ }
+
+ _initialNodeTopPosition = topPosition;
+ child.Arrange(new Rect(0, topPosition, bounds.Width, pickerHeight));
+ }
+ else
+ {
+ //// Arrange the drawable view only for item source items without top and bottom empty spacing.
+ child.Arrange(new Rect(0, yPosition, bounds.Width, _itemsSource.Count * itemHeight));
+ _initialNodeTopPosition = yPosition;
+ }
}
}
else
@@ -574,25 +664,77 @@ protected override Size ArrangeContent(Rect bounds)
double itemHeight = _pickerLayoutInfo.PickerInfo.ItemHeight;
double yPosition = _initialNodeTopPosition;
int startIndex = GetStartingIndex();
- //// Draw item source item based on the item source collection.
- for (int i = startIndex; i < _itemsSource.Count; i++)
- {
- Rect rectangle = new Rect(0, yPosition, width, itemHeight);
- //// The yPosition is adjusted based on the item height.
- //// Assume the item height is 20, then the yPosition is 40. So that the next item is drawn from 60.
- yPosition += itemHeight;
- if (yPosition >= height)
+ double viewPortItemCount = Math.Round(GetViewPortHeight() / itemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && _itemsSource.Count > viewPortItemCount;
+ if (enableLooping)
+ {
+ if (viewPortItemCount % 2 == 0)
{
- break;
+ startIndex = _selectedIndex - (int)Math.Floor(viewPortItemCount / 2) + 1;
+ }
+ else
+ {
+ startIndex = _selectedIndex - (int)Math.Floor(viewPortItemCount / 2);
}
- SemanticsNode node = new SemanticsNode()
+ //// For looping mode, we create nodes based on the current scroll position.
+ double currentYPosition = _pickerLayoutInfo.ScrollOffset;
+ for (int i = 0; i <= (_itemsSource.Count * 3); i++)
{
- Text = _itemsSource[i],
- Bounds = rectangle,
- Id = i,
- };
- _semanticsNodes.Add(node);
+ int adjustedIndex = (startIndex + i) % _itemsSource.Count;
+ if (adjustedIndex < 0)
+ {
+ adjustedIndex += _itemsSource.Count;
+ }
+
+ double totalContentHeight = _itemsSource.Count * itemHeight;
+ //// When scrolling upward, update the scroll position based on the total content height.
+ if (currentYPosition < (viewPortItemCount * itemHeight))
+ {
+ currentYPosition = totalContentHeight;
+ }
+ //// When scrolling downward, update the scroll position based on the total content height.
+ else if (currentYPosition >= totalContentHeight + (viewPortItemCount * itemHeight))
+ {
+ currentYPosition = currentYPosition - totalContentHeight;
+ }
+
+ //// Create rectangle based on the current yPosition.
+ Rect rectangle = new Rect(0, currentYPosition, width, itemHeight);
+
+ SemanticsNode node = new SemanticsNode()
+ {
+ Text = _itemsSource[adjustedIndex],
+ Bounds = rectangle,
+ Id = adjustedIndex,
+ };
+ _semanticsNodes.Add(node);
+ //// The yPosition is adjusted based on the item height.
+ currentYPosition += itemHeight;
+ }
+ }
+ else
+ {
+ //// Draw item source item based on the item source collection.
+ for (int i = startIndex; i < _itemsSource.Count; i++)
+ {
+ Rect rectangle = new Rect(0, yPosition, width, itemHeight);
+ //// The yPosition is adjusted based on the item height.
+ //// Assume the item height is 20, then the yPosition is 40. So that the next item is drawn from 60.
+ yPosition += itemHeight;
+ if (yPosition >= height)
+ {
+ break;
+ }
+
+ SemanticsNode node = new SemanticsNode()
+ {
+ Text = _itemsSource[i],
+ Bounds = rectangle,
+ Id = i,
+ };
+ _semanticsNodes.Add(node);
+ }
}
return _semanticsNodes;
@@ -611,20 +753,35 @@ protected override void ScrollToCore(SemanticsNode node)
double nodeHeight = node.Bounds.Height;
double nodeTopPosition = node.Bounds.Top;
double scrollYPosition = scrollView.ScrollY;
- if ((nodeTopPosition > scrollYPosition && nodeTopPosition < scrollYPosition + scrollHeight) ||
- (nodeTopPosition + nodeHeight > scrollYPosition && nodeTopPosition + nodeHeight < scrollYPosition + scrollHeight))
- {
- return;
- }
-
+ double itemHeight = _pickerLayoutInfo.PickerInfo.ItemHeight;
+ double viewPortItemCount = Math.Round(GetViewPortHeight() / itemHeight);
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && _itemsSource.Count > viewPortItemCount;
double maxScrollPosition = scrollView.ContentSize.Height - scrollView.Height;
- if (maxScrollPosition > node.Bounds.Top)
+ if (!enableLooping)
{
- scrollView.ScrollToAsync(0, node.Bounds.Top, false);
+ if ((nodeTopPosition > scrollYPosition && nodeTopPosition < scrollYPosition + scrollHeight) ||
+ (nodeTopPosition + nodeHeight > scrollYPosition && nodeTopPosition + nodeHeight < scrollYPosition + scrollHeight))
+ {
+ return;
+ }
+
+ if (maxScrollPosition > node.Bounds.Top)
+ {
+ scrollView.ScrollToAsync(0, node.Bounds.Top, false);
+ }
+ else
+ {
+ scrollView.ScrollToAsync(0, maxScrollPosition, false);
+ }
}
else
{
- scrollView.ScrollToAsync(0, maxScrollPosition, false);
+ if (nodeTopPosition >= scrollYPosition && nodeTopPosition <= scrollYPosition + scrollHeight - nodeHeight)
+ {
+ return;
+ }
+
+ scrollView.ScrollToAsync(0, nodeTopPosition, false);
}
}
}
@@ -664,6 +821,8 @@ void ITapGestureListener.OnTap(TapEventArgs e)
//// Assume the view port height is 109 and the item height is 20, While using Math.Round then the view port item count is 109 / 20 = 5.4 => 5(view port item count).
//// Assume the view port height is 110 and the item height is 20, While using Math.Round then the view port item count is 110 / 20 = 5.5 => 6(view port item count).
double viewPortItemCount = Math.Round(GetViewPortHeight() / itemHeight);
+ int itemCount = _itemsSource.Count;
+ bool enableLooping = _pickerLayoutInfo.PickerInfo.EnableLooping && itemCount > viewPortItemCount;
//// The drawingStartIndex is calculated based on the view port item count.
//// From above example the view port item count is 5, then the drawingStartIndex is 2. While using the Math.Ceiling the drawingStartIndex is 5/2 = 2.5 => 3.
int drawingStartIndex = (int)Math.Ceiling(viewPortItemCount / 2) - 1;
@@ -677,10 +836,11 @@ void ITapGestureListener.OnTap(TapEventArgs e)
//// It means tapped point is before the first item then need to render first item as selected item.
if (e.TapPoint.Y < firstItemYPosition)
{
- selectedIndex = 0;
+ int offset = (int)((firstItemYPosition - e.TapPoint.Y) / itemHeight) + 1;
+ selectedIndex = enableLooping ? (itemCount - offset) % itemCount : 0;
}
//// The tapped point is after the last item then need to render last item as selected item.
- else if (e.TapPoint.Y > Height - firstItemYPosition - itemHeight)
+ else if (!enableLooping && e.TapPoint.Y > Height - firstItemYPosition - itemHeight)
{
selectedIndex = _itemsSource.Count;
}
@@ -691,6 +851,20 @@ void ITapGestureListener.OnTap(TapEventArgs e)
selectedIndex = (int)((e.TapPoint.Y - firstItemYPosition) / itemHeight);
}
+ //// While looping, check the selected index value.
+ if (enableLooping)
+ {
+ selectedIndex = selectedIndex % itemCount;
+ if (selectedIndex < 0)
+ {
+ selectedIndex = 0;
+ }
+ }
+ else
+ {
+ selectedIndex = _pickerLayoutInfo.Column.SelectedIndex < 0 ? selectedIndex - 1 : selectedIndex;
+ }
+
_pickerLayoutInfo.UpdateSelectedIndexValue(selectedIndex);
}
diff --git a/maui/src/Syncfusion.Maui.Toolkit.csproj b/maui/src/Syncfusion.Maui.Toolkit.csproj
index 168da36b..021b7d24 100644
--- a/maui/src/Syncfusion.Maui.Toolkit.csproj
+++ b/maui/src/Syncfusion.Maui.Toolkit.csproj
@@ -35,7 +35,7 @@
This package contains the Syncfusion Toolkit controls for .NET MAUI
syncfusion;maui;ios;android;maccatalyst;dotnet-maui;syncfusion-maui
- 14.2
+ 15.0
15.0
21.0
10.0.17763.0
diff --git a/maui/src/TabView/Control/HorizontalContent/SfHorizontalContent.cs b/maui/src/TabView/Control/HorizontalContent/SfHorizontalContent.cs
index 63999c2a..0627ba7a 100644
--- a/maui/src/TabView/Control/HorizontalContent/SfHorizontalContent.cs
+++ b/maui/src/TabView/Control/HorizontalContent/SfHorizontalContent.cs
@@ -283,11 +283,13 @@ protected override Size MeasureContent(double widthConstraint, double heightCons
ContentWidth = widthConstraint;
UpdateTabItemContentSize();
UpdateTabItemContentPosition();
+ _tabBar?.UpdateTabIndicatorWidth();
}
if (heightConstraint > 0 && heightConstraint != double.PositiveInfinity )
{
UpdateTabItemContentSize();
UpdateTabItemContentPosition();
+ _tabBar?.UpdateTabIndicatorWidth();
}
return base.MeasureContent(widthConstraint, heightConstraint);
diff --git a/maui/src/TabView/Control/HorizontalContent/SfHorizontalContent.iOS.cs b/maui/src/TabView/Control/HorizontalContent/SfHorizontalContent.iOS.cs
index 4a6cde43..2790c6ca 100644
--- a/maui/src/TabView/Control/HorizontalContent/SfHorizontalContent.iOS.cs
+++ b/maui/src/TabView/Control/HorizontalContent/SfHorizontalContent.iOS.cs
@@ -3,6 +3,7 @@
using PointerEventArgs = Syncfusion.Maui.Toolkit.Internals.PointerEventArgs;
using UIKit;
using Syncfusion.Maui.Toolkit.Platform;
+using Syncfusion.Maui.Toolkit.TextInputLayout;
namespace Syncfusion.Maui.Toolkit.TabView
{
@@ -46,6 +47,14 @@ void ITapGestureListener.ShouldHandleTap(object view)
{
_canProcessTouch = false;
}
+ else if (touchView is Syncfusion.Maui.Toolkit.Platform.LayoutViewExt layoutViewExt)
+ {
+ if (layoutViewExt.Drawable is SfTextInputLayout || layoutViewExt.Drawable is Editor )
+ {
+ // for SfTextInputLayout || for Editor inside LayoutViewExt
+ this._canProcessTouch = false;
+ }
+ }
else if (touchView is Microsoft.Maui.Platform.MauiImageView)
{
HandleMauiImageViewOnTap(view);
@@ -56,12 +65,68 @@ void ITapGestureListener.ShouldHandleTap(object view)
if (touchView is not Syncfusion.Maui.Toolkit.Platform.LayoutViewExt &&
touchView is not Syncfusion.Maui.Toolkit.Platform.NativePlatformGraphicsView)
{
- HandleOtherViewsOnTap(touchView, view);
+ if (touchView is MauiTextField || touchView is MauiTextView || touchView is UIKit.UITextField)
+ {
+ this._canProcessTouch = false;
+ }
+ }
+ else if (view is UIKit.UITouch uiTouch)
+ {
+ // For SfTextInputLayout or similar views that require precise touch interactions.
+ var touchLocation = uiTouch.LocationInView(uiTouch.View?.Superview);
+ var textInputView = FindSfTextInputLayout(uiTouch.View?.Superview);
+ this._canProcessTouch = true;
+ if (textInputView != null)
+ {
+ if (uiTouch.GestureRecognizers != null)
+ {
+ foreach (var gesture in uiTouch.GestureRecognizers)
+ {
+ if (gesture is UILongPressGestureRecognizer)
+ {
+ this.RemoveGestureListener(this);
+ this._isTapGestureRemoved = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (uiTouch.GestureRecognizers != null)
+ {
+ foreach (var gesture in uiTouch.GestureRecognizers)
+ {
+ if (gesture is UILongPressGestureRecognizer)
+ {
+ this._canProcessTouch = false;
+ }
+ }
+ }
+ }
}
}
#endif
}
+ ///
+ /// Finds and returns the native that represents a
+ ///
+ /// The root to start the search from.
+ ///
+ /// The matching that wraps a if found; otherwise, null.
+ ///
+ private UIView? FindSfTextInputLayout(UIView? root)
+ {
+ if (root == null)
+ {
+ return null;
+ }
+ else
+ {
+ return root;
+ }
+ }
+
///
/// This method triggers on any touch interaction on .
///
diff --git a/maui/src/TabView/Control/SfTabBar.cs b/maui/src/TabView/Control/SfTabBar.cs
index c29acd7c..34ac532b 100644
--- a/maui/src/TabView/Control/SfTabBar.cs
+++ b/maui/src/TabView/Control/SfTabBar.cs
@@ -1469,7 +1469,8 @@ void OnTabItemPropertyChanged(object? sender, System.ComponentModel.PropertyChan
}
}
CalculateTabItemWidth();
- }
+ UpdateTabIndicatorWidth();
+ }
else if (e.PropertyName.Equals(nameof(SfTabItem.Width), StringComparison.Ordinal) ||
e.PropertyName.Equals(nameof(SfTabItem.WidthRequest), StringComparison.Ordinal))
{
@@ -2086,7 +2087,7 @@ void CalculateTabItemWidthForDefaultWidthMode()
{
foreach (var item in Items)
{
- if (item != null)
+ if (item != null && item.IsVisible)
{
SetHeaderDisplayMode(item);
UpdateTabItemWidth(item, width);
@@ -2553,7 +2554,7 @@ double CalculateTabXPosition()
///
/// Update indicator width based on items.
///
- void UpdateTabIndicatorWidth()
+ internal void UpdateTabIndicatorWidth()
{
if (_tabSelectionIndicator != null &&
_tabHeaderItemContent != null &&
diff --git a/maui/src/TextInputLayout/SfTextInputLayout.Methods.cs b/maui/src/TextInputLayout/SfTextInputLayout.Methods.cs
index 5c0270bd..fb165a74 100644
--- a/maui/src/TextInputLayout/SfTextInputLayout.Methods.cs
+++ b/maui/src/TextInputLayout/SfTextInputLayout.Methods.cs
@@ -4,7 +4,6 @@
using Syncfusion.Maui.Toolkit.NumericUpDown;
using Syncfusion.Maui.Toolkit.NumericEntry;
using Syncfusion.Maui.Toolkit.Themes;
-using System;
using Path = Microsoft.Maui.Controls.Shapes.Path;
namespace Syncfusion.Maui.Toolkit.TextInputLayout
@@ -691,9 +690,6 @@ void ConfigureWindowsDatePicker(Microsoft.UI.Xaml.Controls.CalendarDatePicker wi
windowDatePicker.HorizontalContentAlignment = Microsoft.UI.Xaml.HorizontalAlignment.Center;
windowDatePicker.Resources["TextControlBorderThemeThicknessFocused"] = windowDatePicker.BorderThickness;
windowDatePicker.Resources["SystemColorHighlightColorBrush"] = windowDatePicker.Background;
- windowDatePicker.Resources["CalendarDatePickerCalendarGlyphForeground"] = windowDatePicker.Background;
- windowDatePicker.Resources["CalendarDatePickerCalendarGlyphForegroundPointerOver"] = windowDatePicker.Background;
- windowDatePicker.Resources["CalendarDatePickerCalendarGlyphForegroundPressed"] = windowDatePicker.Background;
}
void ConfigureWindowsTimePicker(Microsoft.UI.Xaml.Controls.TimePicker windowTimePicker)
@@ -855,9 +851,7 @@ void UnwireEvents()
}
UnWireLabelStyleEvents();
-
#if ANDROID
- // Unwire assistive label handler changed events
if (_helperLabel != null)
{
_helperLabel.HandlerChanged -= OnAssistiveLabelHandlerChanged;
@@ -1058,7 +1052,7 @@ void UpdateContentPosition()
_viewBounds.X = (int)_leadViewWidth;
UpdatePosition();
_viewBounds.Y = 0;
- _viewBounds.Width = (int)Math.Max(1, Width - _leadViewWidth - _trailViewWidth);
+ _viewBounds.Width = (int)(Width - _leadViewWidth - _trailViewWidth);
_viewBounds.Height = (int)Height;
if (EnablePasswordVisibilityToggle && !ShowUpDownButton)
@@ -1075,10 +1069,7 @@ void UpdateContentPosition()
_viewBounds.Width -= (float)(IconSize * (IsUpDownVerticalAlignment ? 1 : 2));
}
- // Ensure width doesn't become negative after all subtractions
- _viewBounds.Width = (float)Math.Max(1, _viewBounds.Width);
-
- if (_viewBounds.Height >= 0 && _viewBounds.Width >= 0)
+ if (_viewBounds.Height >= 0 || _viewBounds.Width >= 0)
{
AbsoluteLayout.SetLayoutBounds(Content, _viewBounds);
}
@@ -1134,7 +1125,7 @@ void UpdateLeadingViewPosition()
LeadingView.VerticalOptions = LayoutOptions.End;
}
- if (_viewBounds.Height >= 0 && _viewBounds.Width >= 0)
+ if (_viewBounds.Height >= 0 || _viewBounds.Width >= 0)
{
AbsoluteLayout.SetLayoutBounds(LeadingView, _viewBounds);
}
@@ -1168,7 +1159,7 @@ void UpdateTrailingViewPosition()
TrailingView.VerticalOptions = LayoutOptions.End;
}
- if (_viewBounds.Height >= 0 && _viewBounds.Width >= 0)
+ if (_viewBounds.Height >= 0 || _viewBounds.Width >= 0)
{
AbsoluteLayout.SetLayoutBounds(TrailingView, _viewBounds);
}
@@ -1471,7 +1462,7 @@ void UpdateHelperTextPosition()
_helperTextRect.Y = (int)(Height - TotalAssistiveTextHeight());
}
- _helperTextRect.Width = (int)Math.Max(1, Width - startPadding - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth);
+ _helperTextRect.Width = (int)(Width - startPadding - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth);
_helperTextRect.Height = HelperTextSize.Height;
if (IsRTL)
@@ -1488,13 +1479,11 @@ double GetAssistiveTextWidth()
if (Width >= 0)
{
- var calculatedWidth = Width - (IsNone ? 0 : StartX + DefaultAssistiveLabelPadding) - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth;
- return Math.Max(1, calculatedWidth);
+ return Width - (IsNone ? 0 : StartX + DefaultAssistiveLabelPadding) - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth;
}
else if (WidthRequest != -1)
{
- var calculatedWidth = WidthRequest - (IsNone ? 0 : StartX + DefaultAssistiveLabelPadding) - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth;
- return Math.Max(1, calculatedWidth);
+ return WidthRequest - (IsNone ? 0 : StartX + DefaultAssistiveLabelPadding) - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth;
}
else
{
@@ -1514,13 +1503,11 @@ double GetHintTextWidth()
if (Width >= 0)
{
- var calculatedWidth = Width - (IsNone ? 0 : ((2 * StartX) + DefaultAssistiveLabelPadding)) - _trailViewWidth - _leadViewWidth;
- return Math.Max(1, calculatedWidth);
+ return Width - (IsNone ? 0 : ((2 * StartX) + DefaultAssistiveLabelPadding)) - _trailViewWidth - _leadViewWidth;
}
else if (WidthRequest != -1)
{
- var calculatedWidth = WidthRequest - (IsNone ? 0 : 2 * (StartX + DefaultAssistiveLabelPadding)) - _trailViewWidth - _leadViewWidth;
- return Math.Max(1, calculatedWidth);
+ return WidthRequest - (IsNone ? 0 : 2 * (StartX + DefaultAssistiveLabelPadding)) - _trailViewWidth - _leadViewWidth;
}
else
{
@@ -1546,7 +1533,7 @@ void UpdateErrorTextPosition()
_errorTextRect.Y = (int)(Height - TotalAssistiveTextHeight());
}
- _errorTextRect.Width = (int)Math.Max(1, Width - startPadding - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth);
+ _errorTextRect.Width = (int)(Width - startPadding - CounterTextPadding - DefaultAssistiveLabelPadding - ((ShowCharCount) ? CounterTextSize.Width + CounterTextPadding : 0) - _trailViewWidth - _leadViewWidth);
_errorTextRect.Height = ErrorTextSize.Height;
if (IsRTL)
@@ -1606,11 +1593,11 @@ void UpdateOutlineRectF()
UpdateTrailViewWidthForBorder();
if (BaseLineMaxHeight <= 2)
{
- _outlineRectF.Width = (float)Math.Max(1, (Width - (BaseLineMaxHeight * 2)) - _leadViewWidth - _trailViewWidth);
+ _outlineRectF.Width = (float)((Width - (BaseLineMaxHeight * 2)) - _leadViewWidth - _trailViewWidth);
}
else
{
- _outlineRectF.Width = (float)Math.Max(1, (Width - (BaseLineMaxHeight)) - _leadViewWidth - _trailViewWidth);
+ _outlineRectF.Width = (float)((Width - (BaseLineMaxHeight)) - _leadViewWidth - _trailViewWidth);
}
_outlineRectF.Height = (float)(Height - _outlineRectF.Y - TotalAssistiveTextHeight() - AssistiveLabelPadding);
@@ -1622,14 +1609,14 @@ void UpdateOutlineBackgroundRectF()
{
_backgroundRectF.X = (float)(_outlineRectF.X + (FocusedStrokeThickness / 2));
_backgroundRectF.Y = (float)(_outlineRectF.Y + (FocusedStrokeThickness / 2));
- _backgroundRectF.Width = (float)Math.Max(1, _outlineRectF.Width - FocusedStrokeThickness);
+ _backgroundRectF.Width = (float)(_outlineRectF.Width - (FocusedStrokeThickness));
_backgroundRectF.Height = (float)(_outlineRectF.Height - (FocusedStrokeThickness));
}
else
{
_backgroundRectF.X = (float)(_outlineRectF.X + (UnfocusedStrokeThickness / 2));
_backgroundRectF.Y = (float)(_outlineRectF.Y + (UnfocusedStrokeThickness / 2));
- _backgroundRectF.Width = (float)Math.Max(1, _outlineRectF.Width - UnfocusedStrokeThickness);
+ _backgroundRectF.Width = (float)(_outlineRectF.Width - (UnfocusedStrokeThickness));
_backgroundRectF.Height = (float)(_outlineRectF.Height - (UnfocusedStrokeThickness));
}
@@ -1644,7 +1631,7 @@ void UpdateBackgroundRectF()
UpdateTrailViewWidthForBorder();
_backgroundRectF.X = (float)_leadViewWidth;
_backgroundRectF.Y = 0;
- _backgroundRectF.Width = (float)Math.Max(1, Width - _leadViewWidth - _trailViewWidth);
+ _backgroundRectF.Width = (float)(Width - _leadViewWidth - _trailViewWidth);
if (BaseLineMaxHeight <= 2)
{
_backgroundRectF.Height = (float)(Height - TotalAssistiveTextHeight() - AssistiveLabelPadding);
diff --git a/maui/src/TextInputLayout/SfTextInputLayout.Properties.cs b/maui/src/TextInputLayout/SfTextInputLayout.Properties.cs
index 2cc6443a..13957f61 100644
--- a/maui/src/TextInputLayout/SfTextInputLayout.Properties.cs
+++ b/maui/src/TextInputLayout/SfTextInputLayout.Properties.cs
@@ -2203,8 +2203,8 @@ static void OnIsHintFloatedPropertyChanged(BindableObject bindable, object oldVa
#elif MACCATALYST
minOpacity = 0.011;
#endif
-
- double targetOpacity = value ? 1 : minOpacity;
+
+ double targetOpacity = value ? 1 : minOpacity;
if (inputLayout.Content is InputView || inputLayout.Content is Microsoft.Maui.Controls.Picker)
{
@@ -2394,7 +2394,6 @@ static void OnPropertyChanged(BindableObject bindable, object oldValue, object n
if (bindable is SfTextInputLayout inputLayout && inputLayout._initialLoaded)
{
inputLayout.UpdateViewBounds();
- // Update assistive labels when relevant properties change
inputLayout.UpdateAssistiveLabels();
inputLayout.ResetSemantics();
}
diff --git a/maui/src/TextInputLayout/SfTextInputLayout.cs b/maui/src/TextInputLayout/SfTextInputLayout.cs
index c132a89c..b89a9c3d 100644
--- a/maui/src/TextInputLayout/SfTextInputLayout.cs
+++ b/maui/src/TextInputLayout/SfTextInputLayout.cs
@@ -904,8 +904,8 @@ protected override void OnContentChanged(object oldValue, object newValue)
if (DeviceInfo.Platform != DevicePlatform.WinUI)
{
picker.Opacity = IsHintFloated ? 1 : minOpacity;
- }
- }
+ }
+ }
base.OnContentChanged(oldValue, newValue);
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Buttons/SfButtonUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Buttons/SfButtonUnitTests.cs
index 27afea9a..ef4a8e03 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Buttons/SfButtonUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Buttons/SfButtonUnitTests.cs
@@ -699,32 +699,6 @@ public void CalculateWidth_ShouldReturnWidth(double width, double expectedValue)
Assert.Equal(expectedValue, actualValue);
}
- [Theory]
- [InlineData(150, 142, "SampleImage1.png", Alignment.Left)]
- [InlineData(150, 108, "SampleImage1.png", Alignment.Right)]
- [InlineData(180, 164, "SampleImage1.png", Alignment.Top)]
- [InlineData(250, 234, "SampleImage1.png", Alignment.Bottom)]
- [InlineData(200, 184, "SampleImage1.png", null)]
- public void CalculateAvailableTextWidth_ShouldReturnExpectedWidth(double width, double expectedValue, string imagePath, Alignment? alignment = null)
- {
- // Arrange
- var button = new SfButton();
- button.ImageSource = ImageSource.FromFile(imagePath);
-
- if (alignment != null)
- {
- button.ShowIcon = true;
- button.ImageAlignment = (Alignment)alignment;
- }
-
- // Set up as needed
- button.Padding = new Thickness(4, 4, 4, 4);
-
- var actualValue = InvokePrivateMethod(button, "CalculateAvailableTextWidth", width);
-
- Assert.Equal(expectedValue, actualValue);
- }
-
[Theory]
[InlineData(true, false, false, false, "#000000")]
[InlineData(true, true, false, false, "#FF0000")]
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Calendar/CalendarCommonPublicAPIUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Calendar/CalendarCommonPublicAPIUnitTests.cs
index 71ba9afd..e34b1a4a 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Calendar/CalendarCommonPublicAPIUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Calendar/CalendarCommonPublicAPIUnitTests.cs
@@ -915,12 +915,195 @@ public void HeaderTemplate_GetAndSet_DataTemplate()
Assert.Equal(expectedValues, actualValue);
}
-
- #endregion
-
- #region Events
-
- [Fact]
+ [Theory]
+ [InlineData(CalendarView.Month)]
+ [InlineData(CalendarView.Year)]
+ [InlineData(CalendarView.Decade)]
+ [InlineData(CalendarView.Century)]
+ public void SelectionCellTemplate_GetAndSet_DataTemplate(CalendarView view)
+ {
+ SfCalendar calendar = new SfCalendar() { View = view };
+
+ // Create the Grid
+ var grid = new Grid
+ {
+ BackgroundColor = Colors.Green
+ };
+
+ // Create the Label
+ var label = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Black,
+ Text = "Custom View"
+ };
+ grid.Children.Add(label);
+ DataTemplate expectedValue = new DataTemplate(() => grid);
+ calendar.SelectionCellTemplate = expectedValue;
+ DataTemplate actualValue = calendar.SelectionCellTemplate;
+ Assert.Equal(expectedValue, actualValue);
+ }
+
+ [Fact]
+ public void SelectionCellTemplate_CreatesView_WhenCalled()
+ {
+ SfCalendar calendar = new SfCalendar();
+
+ // Create the Grid
+ var grid = new Grid
+ {
+ BackgroundColor = Colors.Green
+ };
+
+ // Create the Label
+ var label = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Black,
+ Text = "Custom View"
+ };
+ grid.Children.Add(label);
+ calendar.SelectionCellTemplate = new DataTemplate(() => grid);
+
+ List visibleDates = new List();
+ visibleDates.Add(new DateTime(2025, 03, 19));
+ visibleDates.Add(new DateTime(2025, 03, 20));
+
+ List disableDates = new List();
+ disableDates.Add(new DateTime(2025, 03, 19));
+
+ List CalendarIconDetail = new List();
+ CalendarIconDetail.Add(new CalendarIconDetails() { Fill = Colors.Yellow });
+
+ MonthView monthView = new MonthView(calendar, visibleDates, new DateTime(2025, 03, 20), disableDates, CalendarIconDetail, true);
+ var details = InvokePrivateMethod(monthView, "GetMonthCellDetails", new DateTime(2025, 03, 20).Month, new DateTime(2025, 03, 20));
+ CalendarCellDetails? selectedCellDetail = null;
+ if (details != null)
+ {
+ selectedCellDetail = (CalendarCellDetails)details;
+ }
+ if (selectedCellDetail != null)
+ {
+ View? view = CalendarViewHelper.CreateSelectionCellTemplate(new DateTime(2025, 03, 20), calendar.SelectionCellTemplate, calendar.MonthView, selectedCellDetail, new RectF(10, 20, 40, 20));
+ Assert.NotNull(view);
+ }
+ }
+
+ [Fact]
+ public void SelectionCellTemplate_CreatesView_WhenCalled_WithoutSelectionCellTemplate()
+ {
+ SfCalendar calendar = new SfCalendar();
+
+ List visibleDates = new List();
+ visibleDates.Add(new DateTime(2025, 03, 19));
+ visibleDates.Add(new DateTime(2025, 03, 20));
+
+ List disableDates = new List();
+ disableDates.Add(new DateTime(2025, 03, 19));
+
+ List CalendarIconDetail = new List();
+ CalendarIconDetail.Add(new CalendarIconDetails() { Fill = Colors.Yellow });
+
+ MonthView monthView = new MonthView(calendar, visibleDates, new DateTime(2025, 03, 20), disableDates, CalendarIconDetail, true);
+ var details = InvokePrivateMethod(monthView, "GetMonthCellDetails", new DateTime(2025, 03, 20).Month, new DateTime(2025, 03, 20));
+
+ CalendarCellDetails? selectedCellDetail = null;
+ if (details != null)
+ {
+ selectedCellDetail = (CalendarCellDetails)details;
+ }
+ if (selectedCellDetail != null)
+ {
+ View? view = CalendarViewHelper.CreateSelectionCellTemplate(new DateTime(2025, 03, 20), calendar.SelectionCellTemplate, calendar.MonthView, selectedCellDetail, new RectF(10, 20, 40, 20));
+ Assert.Null(view);
+ }
+ }
+
+ [Theory]
+ [InlineData(CalendarView.Year)]
+ [InlineData(CalendarView.Decade)]
+ [InlineData(CalendarView.Century)]
+ public void SelectionCellTemplate_CreatesView_WhenCalled_YearView(CalendarView viewOfYear)
+ {
+ SfCalendar calendar = new SfCalendar() { View = viewOfYear };
+
+ // Create the Grid
+ var grid = new Grid
+ {
+ BackgroundColor = Colors.Green
+ };
+
+ // Create the Label
+ var label = new Label
+ {
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center,
+ TextColor = Colors.Black,
+ Text = "Custom View"
+ };
+ grid.Children.Add(label);
+ calendar.SelectionCellTemplate = new DataTemplate(() => grid);
+
+ List visibleDates = new List();
+ visibleDates.Add(new DateTime(2025, 03, 01));
+ visibleDates.Add(new DateTime(2025, 04, 01));
+ visibleDates.Add(new DateTime(2025, 05, 01));
+
+ List disableDates = new List();
+ disableDates.Add(new DateTime(2025, 03, 01));
+
+ YearView yearView = new YearView(calendar, visibleDates, disableDates, true);
+ var details = InvokePrivateMethod(yearView, "GetYearCellDetails", new DateTime(2025, 04, 01), visibleDates[0]);
+ CalendarCellDetails? selectedCellDetail = null;
+ if (details != null)
+ {
+ selectedCellDetail = (CalendarCellDetails)details;
+ }
+ if (selectedCellDetail != null)
+ {
+ View? view = CalendarViewHelper.CreateSelectionCellTemplate(new DateTime(2025, 03, 20), calendar.SelectionCellTemplate, calendar.MonthView, selectedCellDetail, new RectF(10, 20, 40, 20));
+ Assert.NotNull(view);
+ }
+ }
+
+ [Theory]
+ [InlineData(CalendarView.Year)]
+ [InlineData(CalendarView.Decade)]
+ [InlineData(CalendarView.Century)]
+ public void SelectionCellTemplate_CreatesView_WhenCalled_OnYearView_WithoutSelectionCellTemplate(CalendarView viewOfYear)
+ {
+ SfCalendar calendar = new SfCalendar() { View = viewOfYear };
+
+ List visibleDates = new List();
+ visibleDates.Add(new DateTime(2025, 03, 01));
+ visibleDates.Add(new DateTime(2025, 04, 01));
+ visibleDates.Add(new DateTime(2025, 05, 01));
+
+ List disableDates = new List();
+ disableDates.Add(new DateTime(2025, 03, 01));
+
+ YearView yearView = new YearView(calendar, visibleDates, disableDates, true);
+ var details = InvokePrivateMethod(yearView, "GetYearCellDetails", new DateTime(2025, 04, 01), visibleDates[0]);
+ CalendarCellDetails? selectedCellDetail = null;
+ if (details != null)
+ {
+ selectedCellDetail = (CalendarCellDetails)details;
+ }
+ if (selectedCellDetail != null)
+ {
+ View? view = CalendarViewHelper.CreateSelectionCellTemplate(new DateTime(2025, 03, 20), calendar.SelectionCellTemplate, calendar.MonthView, selectedCellDetail, new RectF(10, 20, 40, 20));
+ Assert.Null(view);
+ }
+ }
+
+
+ #endregion
+
+ #region Events
+
+ [Fact]
public void SelectionChangedInvoked()
{
SfCalendar calendar = new SfCalendar();
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/ChartAxisUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/ChartAxisUnitTests.cs
index 69e35bd5..e686609b 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/ChartAxisUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/ChartAxisUnitTests.cs
@@ -2,757 +2,891 @@
namespace Syncfusion.Maui.Toolkit.UnitTest.Charts
{
- public class ChartAxisUnitTests
- {
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void IsVisible_SetValue_ReturnsExpectedValue(bool isVisible)
- {
+ public class ChartAxisUnitTests
+ {
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void IsVisible_SetValue_ReturnsExpectedValue(bool isVisible)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
IsVisible = isVisible
};
Assert.Equal(isVisible, numericalAxis.IsVisible);
- }
-
- [Theory]
- [InlineData(3)]
- [InlineData(6)]
- [InlineData(-2)]
- public void AutoScrollingDelta_SetValue_ReturnsExpectedValue(double autoScrollingDelta)
- {
+ }
+
+ [Theory]
+ [InlineData(3)]
+ [InlineData(6)]
+ [InlineData(-2)]
+ public void AutoScrollingDelta_SetValue_ReturnsExpectedValue(double autoScrollingDelta)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
AutoScrollingDelta = autoScrollingDelta
};
Assert.Equal(autoScrollingDelta, numericalAxis.AutoScrollingDelta);
- }
+ }
- [Theory]
- [InlineData(ChartAutoScrollingMode.Start)]
- [InlineData(ChartAutoScrollingMode.End)]
- public void AutoScrollingDeltaMode_SetValue_ReturnsExpectedValue(ChartAutoScrollingMode autoScrollingMode)
- {
+ [Theory]
+ [InlineData(ChartAutoScrollingMode.Start)]
+ [InlineData(ChartAutoScrollingMode.End)]
+ public void AutoScrollingDeltaMode_SetValue_ReturnsExpectedValue(ChartAutoScrollingMode autoScrollingMode)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
AutoScrollingMode = autoScrollingMode
};
Assert.Equal(autoScrollingMode, numericalAxis.AutoScrollingMode);
- }
-
- [Theory]
- [InlineData(3)]
- [InlineData(-1)]
- [InlineData(double.MaxValue)]
- [InlineData(double.MinValue)]
- [InlineData(double.NaN)]
- public void AxisLineOffSet_SetValue_ReturnsExpectedValue(double axisLineOffset)
- {
+ }
+
+ [Theory]
+ [InlineData(3)]
+ [InlineData(-1)]
+ [InlineData(double.MaxValue)]
+ [InlineData(double.MinValue)]
+ [InlineData(double.NaN)]
+ public void AxisLineOffSet_SetValue_ReturnsExpectedValue(double axisLineOffset)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
AxisLineOffset = axisLineOffset
};
Assert.Equal(axisLineOffset, numericalAxis.AxisLineOffset);
- }
-
- [Fact]
- public void AxisLineStyle_SetValue_ReturnsExpectedValue()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- var style = new ChartLineStyle() { StrokeWidth = 2, Stroke = Colors.Red };
- numericalAxis.AxisLineStyle = style;
-
- Assert.Equal(Colors.Red, numericalAxis.AxisLineStyle.Stroke);
- }
-
- [Fact]
- public void CrossAxisName_SetValue_ReturnsExpectedValue()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- string crossAxisName = "secondaryAxis";
- numericalAxis.CrossAxisName = crossAxisName;
-
- Assert.Equal(crossAxisName, numericalAxis.CrossAxisName);
- }
-
- [Theory]
- [InlineData(double.MaxValue)]
- [InlineData(double.MinValue)]
- [InlineData(double.NaN)]
- [InlineData(10)]
- public void CrossesAt_SetValue_ReturnsExpectedValue(double crossesAt)
- {
+ }
+
+ [Fact]
+ public void AxisLineStyle_SetValue_ReturnsExpectedValue()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ var style = new ChartLineStyle() { StrokeWidth = 2, Stroke = Colors.Red };
+ numericalAxis.AxisLineStyle = style;
+
+ Assert.Equal(Colors.Red, numericalAxis.AxisLineStyle.Stroke);
+ }
+
+ [Fact]
+ public void CrossAxisName_SetValue_ReturnsExpectedValue()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ string crossAxisName = "secondaryAxis";
+ numericalAxis.CrossAxisName = crossAxisName;
+
+ Assert.Equal(crossAxisName, numericalAxis.CrossAxisName);
+ }
+
+ [Theory]
+ [InlineData(double.MaxValue)]
+ [InlineData(double.MinValue)]
+ [InlineData(double.NaN)]
+ [InlineData(10)]
+ public void CrossesAt_SetValue_ReturnsExpectedValue(double crossesAt)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
CrossesAt = crossesAt
};
Assert.Equal(crossesAt, numericalAxis.CrossesAt);
- }
-
- [Theory]
- [InlineData(EdgeLabelsDrawingMode.Center)]
- [InlineData(EdgeLabelsDrawingMode.Shift)]
- [InlineData(EdgeLabelsDrawingMode.Fit)]
- [InlineData(EdgeLabelsDrawingMode.Hide)]
- public void EdgeLabelDrawingMode_SetValue_ReturnsExpectedValue(EdgeLabelsDrawingMode edgeLabelsDrawingMode)
- {
+ }
+
+ [Theory]
+ [InlineData(EdgeLabelsDrawingMode.Center)]
+ [InlineData(EdgeLabelsDrawingMode.Shift)]
+ [InlineData(EdgeLabelsDrawingMode.Fit)]
+ [InlineData(EdgeLabelsDrawingMode.Hide)]
+ public void EdgeLabelDrawingMode_SetValue_ReturnsExpectedValue(EdgeLabelsDrawingMode edgeLabelsDrawingMode)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
EdgeLabelsDrawingMode = edgeLabelsDrawingMode
};
Assert.Equal(edgeLabelsDrawingMode, numericalAxis.EdgeLabelsDrawingMode);
- }
+ }
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void AutoIntervalOnZooming_SetValue_ReturnsExpectedValue(bool enableAutoIntervalOnZooming)
- {
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void AutoIntervalOnZooming_SetValue_ReturnsExpectedValue(bool enableAutoIntervalOnZooming)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
EnableAutoIntervalOnZooming = enableAutoIntervalOnZooming
};
Assert.Equal(enableAutoIntervalOnZooming, numericalAxis.EnableAutoIntervalOnZooming);
- }
+ }
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void IsInversed_SetValue_ReturnsExpectedValue(bool isInversed)
- {
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void IsInversed_SetValue_ReturnsExpectedValue(bool isInversed)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
IsInversed = isInversed
};
Assert.Equal(isInversed, numericalAxis.IsInversed);
- }
-
- [Theory]
- [InlineData(3)]
- [InlineData(-1)]
- [InlineData(double.MaxValue)]
- [InlineData(double.MinValue)]
- [InlineData(double.NaN)]
- public void AxisLabelExtent_SetValue_ReturnsExpectedValue(double labelExtent)
- {
+ }
+
+ [Theory]
+ [InlineData(3)]
+ [InlineData(-1)]
+ [InlineData(double.MaxValue)]
+ [InlineData(double.MinValue)]
+ [InlineData(double.NaN)]
+ public void AxisLabelExtent_SetValue_ReturnsExpectedValue(double labelExtent)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
LabelExtent = labelExtent
};
Assert.Equal(labelExtent, numericalAxis.LabelExtent);
- }
-
- [Theory]
- [InlineData(0)]
- [InlineData(65)]
- [InlineData(280)]
- [InlineData(double.NaN)]
- public void AxisLabelRotation_SetValue_ReturnsExpectedValue(double labelRotation)
- {
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(65)]
+ [InlineData(280)]
+ [InlineData(double.NaN)]
+ public void AxisLabelRotation_SetValue_ReturnsExpectedValue(double labelRotation)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
LabelRotation = labelRotation
};
Assert.Equal(labelRotation, numericalAxis.LabelRotation);
- }
-
- [Theory]
- [InlineData(AxisLabelsIntersectAction.Hide)]
- [InlineData(AxisLabelsIntersectAction.Wrap)]
- [InlineData(AxisLabelsIntersectAction.MultipleRows)]
- public void LabelsIntersectAction_SetValue_ReturnsExpectedValue(AxisLabelsIntersectAction action)
- {
- CategoryAxis categoryAxis = new CategoryAxis();
- NumericalAxis numericalAxis = new NumericalAxis();
- categoryAxis.LabelsIntersectAction = action;
- Assert.Equal(action, categoryAxis.LabelsIntersectAction);
-
- numericalAxis.LabelsIntersectAction = action;
- Assert.Equal(action, numericalAxis.LabelsIntersectAction);
- }
-
- [Theory]
- [InlineData(AxisElementPosition.Outside)]
- [InlineData(AxisElementPosition.Inside)]
- public void LabelsPosition_SetValue_ReturnsExpectedValue(AxisElementPosition labelPosition)
- {
+ }
+
+ [Theory]
+ [InlineData(AxisLabelsIntersectAction.Hide)]
+ [InlineData(AxisLabelsIntersectAction.Wrap)]
+ [InlineData(AxisLabelsIntersectAction.MultipleRows)]
+ public void LabelsIntersectAction_SetValue_ReturnsExpectedValue(AxisLabelsIntersectAction action)
+ {
+ CategoryAxis categoryAxis = new CategoryAxis();
+ NumericalAxis numericalAxis = new NumericalAxis();
+ categoryAxis.LabelsIntersectAction = action;
+ Assert.Equal(action, categoryAxis.LabelsIntersectAction);
+ numericalAxis.LabelsIntersectAction = action;
+ Assert.Equal(action, numericalAxis.LabelsIntersectAction);
+ }
+
+ [Theory]
+ [InlineData(AxisElementPosition.Outside)]
+ [InlineData(AxisElementPosition.Inside)]
+ public void LabelsPosition_SetValue_ReturnsExpectedValue(AxisElementPosition labelPosition)
+ {
CategoryAxis categoryAxis = new CategoryAxis
{
LabelsPosition = labelPosition
};
Assert.Equal(labelPosition, categoryAxis.LabelsPosition);
- }
+ }
- [Fact]
- public void LabelStyle_SetValue_ReturnsExpectedValue()
- {
- CategoryAxis categoryAxis = new CategoryAxis();
- var labelStyle = new ChartAxisLabelStyle
- {
- TextColor = Colors.Red,
- FontSize = 14
- };
- categoryAxis.LabelStyle = labelStyle;
-
- Assert.Equal(Colors.Red, categoryAxis.LabelStyle.TextColor);
- Assert.Equal(14, categoryAxis.LabelStyle.FontSize);
- }
+ [Fact]
+ public void LabelStyle_SetValue_ReturnsExpectedValue()
+ {
+ CategoryAxis categoryAxis = new CategoryAxis();
+ var labelStyle = new ChartAxisLabelStyle
+ {
+ TextColor = Colors.Red,
+ FontSize = 14
+ };
+ categoryAxis.LabelStyle = labelStyle;
- [Fact]
- public void MajorGridLineStyle_SetValue_ReturnsExpectedValue()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- var gridLineStyle = new ChartLineStyle
- {
- Stroke = Colors.Black,
- StrokeWidth = 2,
- StrokeDashArray = [3, 3]
- };
- numericalAxis.MajorGridLineStyle = gridLineStyle;
-
- Assert.Equal(Colors.Black, numericalAxis.MajorGridLineStyle.Stroke);
- Assert.Equal(2, numericalAxis.MajorGridLineStyle.StrokeWidth);
- Assert.Equal(3, numericalAxis.MajorGridLineStyle.StrokeDashArray[0]);
- Assert.Equal(3, numericalAxis.MajorGridLineStyle.StrokeDashArray[1]);
- }
+ Assert.Equal(Colors.Red, categoryAxis.LabelStyle.TextColor);
+ Assert.Equal(14, categoryAxis.LabelStyle.FontSize);
+ }
- [Fact]
- public void MajorTickStyle_SetValue_ReturnsExpectedValue()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- var tickStyle = new ChartAxisTickStyle
- {
- Stroke = Colors.Red,
- StrokeWidth = 1
- };
- numericalAxis.MajorTickStyle = tickStyle;
-
- Assert.Equal(Colors.Red, numericalAxis.MajorTickStyle.Stroke);
- Assert.Equal(1, numericalAxis.MajorTickStyle.StrokeWidth);
- }
+ [Fact]
+ public void MajorGridLineStyle_SetValue_ReturnsExpectedValue()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ var gridLineStyle = new ChartLineStyle
+ {
+ Stroke = Colors.Black,
+ StrokeWidth = 2,
+ StrokeDashArray = [3, 3]
+ };
+ numericalAxis.MajorGridLineStyle = gridLineStyle;
- [Fact]
- public void Name_SetValue_ReturnsExpectedValue()
- {
+ Assert.Equal(Colors.Black, numericalAxis.MajorGridLineStyle.Stroke);
+ Assert.Equal(2, numericalAxis.MajorGridLineStyle.StrokeWidth);
+ Assert.Equal(3, numericalAxis.MajorGridLineStyle.StrokeDashArray[0]);
+ Assert.Equal(3, numericalAxis.MajorGridLineStyle.StrokeDashArray[1]);
+ }
+
+ [Fact]
+ public void MajorTickStyle_SetValue_ReturnsExpectedValue()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ var tickStyle = new ChartAxisTickStyle
+ {
+ Stroke = Colors.Red,
+ StrokeWidth = 1
+ };
+ numericalAxis.MajorTickStyle = tickStyle;
+
+ Assert.Equal(Colors.Red, numericalAxis.MajorTickStyle.Stroke);
+ Assert.Equal(1, numericalAxis.MajorTickStyle.StrokeWidth);
+ }
+
+ [Fact]
+ public void Name_SetValue_ReturnsExpectedValue()
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
Name = "PrimaryAxis"
};
Assert.Equal("PrimaryAxis", numericalAxis.Name);
- }
+ }
- [Fact]
- public void PlotOffsetEnd_SetValue_ReturnsExpectedValue()
- {
+ [Fact]
+ public void PlotOffsetEnd_SetValue_ReturnsExpectedValue()
+ {
CategoryAxis categoryAxis = new CategoryAxis
{
PlotOffsetEnd = 30
};
Assert.Equal(30, categoryAxis.PlotOffsetEnd);
- }
+ }
- [Fact]
- public void PlotOffsetStart_SetValue_ReturnsExpectedValue()
- {
+ [Fact]
+ public void PlotOffsetStart_SetValue_ReturnsExpectedValue()
+ {
CategoryAxis categoryAxis = new CategoryAxis
{
PlotOffsetStart = 30
};
Assert.Equal(30, categoryAxis.PlotOffsetStart);
- }
+ }
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void RenderNextToCrossingValue_SetValue_ReturnsExpectedValue(bool renderNextToCrossingValue)
- {
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void RenderNextToCrossingValue_SetValue_ReturnsExpectedValue(bool renderNextToCrossingValue)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
RenderNextToCrossingValue = renderNextToCrossingValue
};
Assert.Equal(renderNextToCrossingValue, numericalAxis.RenderNextToCrossingValue);
- }
+ }
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void ShowMajorGridLines_SetValue_ReturnsExpectedValue(bool showMajorGridLines)
- {
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void ShowMajorGridLines_SetValue_ReturnsExpectedValue(bool showMajorGridLines)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
ShowMajorGridLines = showMajorGridLines
};
Assert.Equal(showMajorGridLines, numericalAxis.ShowMajorGridLines);
- }
+ }
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void ShowTrackballLabel_SetValue_ReturnsExpectedValue(bool showTrackballLabel)
- {
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void ShowTrackballLabel_SetValue_ReturnsExpectedValue(bool showTrackballLabel)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
ShowTrackballLabel = showTrackballLabel
};
Assert.Equal(showTrackballLabel, numericalAxis.ShowTrackballLabel);
- }
+ }
- [Theory]
- [InlineData(AxisElementPosition.Outside)]
- [InlineData(AxisElementPosition.Inside)]
- public void TickPosition_SetValue_ReturnsExpectedValue(AxisElementPosition tickPosition)
- {
+ [Theory]
+ [InlineData(AxisElementPosition.Outside)]
+ [InlineData(AxisElementPosition.Inside)]
+ public void TickPosition_SetValue_ReturnsExpectedValue(AxisElementPosition tickPosition)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
TickPosition = tickPosition
};
Assert.Equal(tickPosition, numericalAxis.TickPosition);
- }
-
- [Fact]
- public void AxisTitle_SetValue_ReturnsExpectedValue()
- {
- CategoryAxis categoryAxis = new CategoryAxis();
- var axisTitle = new ChartAxisTitle() { Text = "Category" };
- categoryAxis.Title = axisTitle;
-
- Assert.NotNull(categoryAxis.Title);
- Assert.Equal("Category", categoryAxis.Title.Text);
- }
-
- [Fact]
- public void TrackballLabelStyle_SetValue_ReturnsExpectedValue()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- var trackballLabelStyle = new ChartAxisLabelStyle
- {
- TextColor = Colors.Blue,
- FontSize = 12
- };
- numericalAxis.TrackballLabelStyle = trackballLabelStyle;
-
- Assert.Equal(Colors.Blue, numericalAxis.TrackballLabelStyle.TextColor);
- Assert.Equal(12, numericalAxis.TrackballLabelStyle.FontSize);
- }
-
- [Fact]
- public void TrackballLabelTemplate_SetValue_ReturnsExpectedValue()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- var trackballLabelTemplate = new DataTemplate(() => new Label { Text = "Trackball Label" });
- numericalAxis.TrackballLabelTemplate = trackballLabelTemplate;
-
- Assert.Equal(trackballLabelTemplate, numericalAxis.TrackballLabelTemplate);
- }
-
- //[Fact]
- //public void VisibleLabels_SetValue_ReturnsExpectedValue()
- //{
- // NumericalAxis numericalAxis = new NumericalAxis();
- // numericalAxis.Minimum = 0;
- // numericalAxis.Maximum = 100;
- // numericalAxis.Interval = 20;
-
- // Assert.NotNull(numericalAxis.VisibleLabels);
- // Assert.Equal("100", numericalAxis.VisibleLabels[4].Content.ToString());
- //}
-
- [Theory]
- [InlineData(100)]
- [InlineData(double.MaxValue)]
- [InlineData(double.MinValue)]
- public void VisibleMaximum_SetValue_ReturnsExpectedValue(double visibleMaximum)
- {
+ }
+
+ [Fact]
+ public void AxisTitle_SetValue_ReturnsExpectedValue()
+ {
+ CategoryAxis categoryAxis = new CategoryAxis();
+ var axisTitle = new ChartAxisTitle() { Text = "Category" };
+ categoryAxis.Title = axisTitle;
+
+ Assert.NotNull(categoryAxis.Title);
+ Assert.Equal("Category", categoryAxis.Title.Text);
+ }
+
+ [Fact]
+ public void TrackballLabelStyle_SetValue_ReturnsExpectedValue()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ var trackballLabelStyle = new ChartAxisLabelStyle
+ {
+ TextColor = Colors.Blue,
+ FontSize = 12
+ };
+ numericalAxis.TrackballLabelStyle = trackballLabelStyle;
+
+ Assert.Equal(Colors.Blue, numericalAxis.TrackballLabelStyle.TextColor);
+ Assert.Equal(12, numericalAxis.TrackballLabelStyle.FontSize);
+ }
+
+ [Fact]
+ public void TrackballLabelTemplate_SetValue_ReturnsExpectedValue()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ var trackballLabelTemplate = new DataTemplate(() => new Label { Text = "Trackball Label" });
+ numericalAxis.TrackballLabelTemplate = trackballLabelTemplate;
+
+ Assert.Equal(trackballLabelTemplate, numericalAxis.TrackballLabelTemplate);
+ }
+
+ //[Fact]
+ //public void VisibleLabels_SetValue_ReturnsExpectedValue()
+ //{
+ // NumericalAxis numericalAxis = new NumericalAxis();
+ // numericalAxis.Minimum = 0;
+ // numericalAxis.Maximum = 100;
+ // numericalAxis.Interval = 20;
+
+ // Assert.NotNull(numericalAxis.VisibleLabels);
+ // Assert.Equal("100", numericalAxis.VisibleLabels[4].Content.ToString());
+ //}
+
+ [Theory]
+ [InlineData(100)]
+ [InlineData(double.MaxValue)]
+ [InlineData(double.MinValue)]
+ public void VisibleMaximum_SetValue_ReturnsExpectedValue(double visibleMaximum)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
Maximum = visibleMaximum
};
Assert.Equal(visibleMaximum, numericalAxis.Maximum);
- }
-
- [Fact]
- public void VisibleMinimum_SetValue_ReturnsExpectedValue_Get()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- var minimum = 10d;
- numericalAxis.Minimum = minimum;
-
- Assert.Equal(10, numericalAxis.Minimum);
- }
-
- [Theory]
- [InlineData(0.5)]
- [InlineData(1.0)]
- [InlineData(0.0)]
- public void ZoomFactor_SetValue_ReturnsExpectedValue(double zoomFactor)
- {
+ }
+
+ [Fact]
+ public void VisibleMinimum_SetValue_ReturnsExpectedValue_Get()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ var minimum = 10d;
+ numericalAxis.Minimum = minimum;
+
+ Assert.Equal(10, numericalAxis.Minimum);
+ }
+
+ [Theory]
+ [InlineData(0.5)]
+ [InlineData(1.0)]
+ [InlineData(0.0)]
+ public void ZoomFactor_SetValue_ReturnsExpectedValue(double zoomFactor)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
ZoomFactor = zoomFactor
};
Assert.Equal(zoomFactor, numericalAxis.ZoomFactor);
- }
- [Theory]
- [InlineData(0.5)]
- [InlineData(1.0)]
- [InlineData(0.0)]
- public void ZoomPosition_SetValue_ReturnsExpectedValue(double zoomPosition)
- {
+ }
+ [Theory]
+ [InlineData(0.5)]
+ [InlineData(1.0)]
+ [InlineData(0.0)]
+ public void ZoomPosition_SetValue_ReturnsExpectedValue(double zoomPosition)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
ZoomPosition = zoomPosition
};
Assert.Equal(zoomPosition, numericalAxis.ZoomPosition);
- }
+ }
- // CategoryAxis
+ // CategoryAxis
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void ArrangeByIndex_SetValue_ReturnsExpectedValue(bool arrangeByIndex)
- {
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void ArrangeByIndex_SetValue_ReturnsExpectedValue(bool arrangeByIndex)
+ {
CategoryAxis categoryAxis = new CategoryAxis
{
ArrangeByIndex = arrangeByIndex
};
Assert.Equal(arrangeByIndex, categoryAxis.ArrangeByIndex);
- }
-
- [Theory]
- [InlineData(10)]
- [InlineData(20)]
- [InlineData(50)]
- public void Interval_SetValue_ReturnsExpectedValue(double interval)
- {
+ }
+
+ [Theory]
+ [InlineData(10)]
+ [InlineData(20)]
+ [InlineData(50)]
+ public void Interval_SetValue_ReturnsExpectedValue(double interval)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
Interval = interval
};
Assert.Equal(interval, numericalAxis.Interval);
- }
+ }
- [Theory]
- [InlineData(LabelPlacement.BetweenTicks)]
- [InlineData(LabelPlacement.OnTicks)]
- public void LabelPlacement_SetValue_ReturnsExpectedValue(LabelPlacement labelPlacement)
- {
+ [Theory]
+ [InlineData(LabelPlacement.BetweenTicks)]
+ [InlineData(LabelPlacement.OnTicks)]
+ public void LabelPlacement_SetValue_ReturnsExpectedValue(LabelPlacement labelPlacement)
+ {
CategoryAxis categoryAxis = new CategoryAxis
{
LabelPlacement = labelPlacement
};
Assert.Equal(labelPlacement, categoryAxis.LabelPlacement);
- }
-
- [Fact]
- public void PlotBands_SetValue_ReturnsExpectedValue()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- var plotBand1 = new NumericalPlotBand() { Start = 20, End = 40, Fill = Colors.Bisque };
- numericalAxis.PlotBands =
+ }
+
+ [Fact]
+ public void PlotBands_SetValue_ReturnsExpectedValue()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ var plotBand1 = new NumericalPlotBand() { Start = 20, End = 40, Fill = Colors.Bisque };
+ numericalAxis.PlotBands =
[
plotBand1,
- ];
+ ];
- Assert.NotNull(numericalAxis.PlotBands);
- Assert.Single(numericalAxis.PlotBands);
- Assert.Contains(numericalAxis.PlotBands, band => band.Start == 20 && band.End == 40);
- }
+ Assert.NotNull(numericalAxis.PlotBands);
+ Assert.Single(numericalAxis.PlotBands);
+ Assert.Contains(numericalAxis.PlotBands, band => band.Start == 20 && band.End == 40);
+ }
- // Range axis
+ // Range axis
- [Theory]
- [InlineData(EdgeLabelsVisibilityMode.Default)]
- [InlineData(EdgeLabelsVisibilityMode.AlwaysVisible)]
- [InlineData(EdgeLabelsVisibilityMode.Visible)]
- public void EdgeLabelsVisibilityMode_SetValue_ReturnsExpectedValue(EdgeLabelsVisibilityMode visibilityMode)
- {
+ [Theory]
+ [InlineData(EdgeLabelsVisibilityMode.Default)]
+ [InlineData(EdgeLabelsVisibilityMode.AlwaysVisible)]
+ [InlineData(EdgeLabelsVisibilityMode.Visible)]
+ public void EdgeLabelsVisibilityMode_SetValue_ReturnsExpectedValue(EdgeLabelsVisibilityMode visibilityMode)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
EdgeLabelsVisibilityMode = visibilityMode
};
Assert.Equal(visibilityMode, numericalAxis.EdgeLabelsVisibilityMode);
- }
- [Fact]
- public void MinorGridLineStyle_SetValue_ReturnsExpectedValue()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- var minorGridLineStyle = new ChartLineStyle
- {
- Stroke = Colors.Gray,
- StrokeWidth = 1,
- StrokeDashArray = [2, 2]
- };
- numericalAxis.MinorGridLineStyle = minorGridLineStyle;
-
- Assert.Equal(Colors.Gray, numericalAxis.MinorGridLineStyle.Stroke);
- Assert.Equal(1, numericalAxis.MinorGridLineStyle.StrokeWidth);
- }
-
- [Theory]
- [InlineData(1)]
- [InlineData(2)]
- [InlineData(5)]
- public void MinorTicksPerInterval_SetValue_ReturnsExpectedValue(int minorTicksPerInterval)
- {
+ }
+ [Fact]
+ public void MinorGridLineStyle_SetValue_ReturnsExpectedValue()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ var minorGridLineStyle = new ChartLineStyle
+ {
+ Stroke = Colors.Gray,
+ StrokeWidth = 1,
+ StrokeDashArray = [2, 2]
+ };
+ numericalAxis.MinorGridLineStyle = minorGridLineStyle;
+
+ Assert.Equal(Colors.Gray, numericalAxis.MinorGridLineStyle.Stroke);
+ Assert.Equal(1, numericalAxis.MinorGridLineStyle.StrokeWidth);
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(2)]
+ [InlineData(5)]
+ public void MinorTicksPerInterval_SetValue_ReturnsExpectedValue(int minorTicksPerInterval)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
MinorTicksPerInterval = minorTicksPerInterval
};
Assert.Equal(minorTicksPerInterval, numericalAxis.MinorTicksPerInterval);
- }
+ }
- [Fact]
- public void MinorTickStyle_SetValue_ReturnsExpectedValue()
- {
- NumericalAxis numericalAxis = new NumericalAxis();
- var minorTickStyle = new ChartAxisTickStyle
- {
- Stroke = Colors.Blue,
- StrokeWidth = 0.5
- };
- numericalAxis.MinorTickStyle = minorTickStyle;
-
- Assert.Equal(Colors.Blue, numericalAxis.MinorTickStyle.Stroke);
- Assert.Equal(0.5, numericalAxis.MinorTickStyle.StrokeWidth);
- }
+ [Fact]
+ public void MinorTickStyle_SetValue_ReturnsExpectedValue()
+ {
+ NumericalAxis numericalAxis = new NumericalAxis();
+ var minorTickStyle = new ChartAxisTickStyle
+ {
+ Stroke = Colors.Blue,
+ StrokeWidth = 0.5
+ };
+ numericalAxis.MinorTickStyle = minorTickStyle;
- [Theory]
- [InlineData(true)]
- [InlineData(false)]
- public void ShowMinorGridLines_SetValue_ReturnsExpectedValue(bool showMinorGridLines)
- {
+ Assert.Equal(Colors.Blue, numericalAxis.MinorTickStyle.Stroke);
+ Assert.Equal(0.5, numericalAxis.MinorTickStyle.StrokeWidth);
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void ShowMinorGridLines_SetValue_ReturnsExpectedValue(bool showMinorGridLines)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
ShowMinorGridLines = showMinorGridLines
};
Assert.Equal(showMinorGridLines, numericalAxis.ShowMinorGridLines);
- }
+ }
- // Numerical Axis
+ // Numerical Axis
- [Fact]
- public void NumericalAxis_ActualMaximum_SetValue_ReturnsExpectedValue()
- {
+ [Fact]
+ public void NumericalAxis_ActualMaximum_SetValue_ReturnsExpectedValue()
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
ActualMaximum = 100
};
Assert.Equal(100, numericalAxis.ActualMaximum);
- }
+ }
- [Fact]
- public void NumericalAxis_ActualMinimum_SetValue_ReturnsExpectedValue()
- {
+ [Fact]
+ public void NumericalAxis_ActualMinimum_SetValue_ReturnsExpectedValue()
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
ActualMinimum = 10
};
Assert.Equal(10, numericalAxis.ActualMinimum);
- }
+ }
- [Theory]
- [InlineData(100)]
- [InlineData(double.NaN)]
- public void NumericalAxis_Maximum_SetValue_ReturnsExpectedValue(double maximum)
- {
+ [Theory]
+ [InlineData(100)]
+ [InlineData(double.NaN)]
+ public void NumericalAxis_Maximum_SetValue_ReturnsExpectedValue(double maximum)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
Maximum = maximum
};
Assert.Equal(maximum, numericalAxis.Maximum);
- }
+ }
- [Theory]
- [InlineData(10)]
- [InlineData(double.NaN)]
- public void NumericalAxis_Minimum_SetValue_ReturnsExpectedValue(double minimum)
- {
+ [Theory]
+ [InlineData(10)]
+ [InlineData(double.NaN)]
+ public void NumericalAxis_Minimum_SetValue_ReturnsExpectedValue(double minimum)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
Minimum = minimum
};
Assert.Equal(minimum, numericalAxis.Minimum);
- }
-
- [Theory]
- [InlineData(NumericalPadding.None)]
- [InlineData(NumericalPadding.Round)]
- [InlineData(NumericalPadding.Additional)]
- public void NumericalAxis_RangePadding_SetValue_ReturnsExpectedValue(NumericalPadding rangePadding)
- {
+ }
+
+ [Theory]
+ [InlineData(NumericalPadding.None)]
+ [InlineData(NumericalPadding.Round)]
+ [InlineData(NumericalPadding.Additional)]
+ public void NumericalAxis_RangePadding_SetValue_ReturnsExpectedValue(NumericalPadding rangePadding)
+ {
NumericalAxis numericalAxis = new NumericalAxis
{
RangePadding = rangePadding
};
Assert.Equal(rangePadding, numericalAxis.RangePadding);
- }
-
- [Theory]
- [InlineData(2)]
- [InlineData(10)]
- [InlineData(100)]
- public void LogarithmicBase_SetValue_ReturnsExpectedValue(double logarithmicBase)
- {
+ }
+
+ [Theory]
+ [InlineData(2)]
+ [InlineData(10)]
+ [InlineData(100)]
+ public void LogarithmicBase_SetValue_ReturnsExpectedValue(double logarithmicBase)
+ {
LogarithmicAxis logarithmicAxis = new LogarithmicAxis
{
LogarithmicBase = logarithmicBase
};
Assert.Equal(logarithmicBase, logarithmicAxis.LogarithmicBase);
- }
+ }
- // DateTimeAxis
+ // DateTimeAxis
- [Fact]
- public void DateTimeAxis_ActualMaximum_SetValue_ReturnsExpectedValue()
- {
+ [Fact]
+ public void DateTimeAxis_ActualMaximum_SetValue_ReturnsExpectedValue()
+ {
DateTimeAxis dateTimeAxis = new DateTimeAxis
{
ActualMaximum = new DateTime(2023, 12, 31)
};
Assert.Equal(new DateTime(2023, 12, 31), dateTimeAxis.ActualMaximum);
- }
+ }
- [Fact]
- public void DateTimeAxis_ActualMinimum_SetValue_ReturnsExpectedValue()
- {
+ [Fact]
+ public void DateTimeAxis_ActualMinimum_SetValue_ReturnsExpectedValue()
+ {
DateTimeAxis dateTimeAxis = new DateTimeAxis
{
ActualMinimum = new DateTime(2023, 1, 1)
};
Assert.Equal(new DateTime(2023, 1, 1), dateTimeAxis.ActualMinimum);
- }
-
- [Theory]
- [InlineData(DateTimeIntervalType.Auto)]
- [InlineData(DateTimeIntervalType.Milliseconds)]
- [InlineData(DateTimeIntervalType.Seconds)]
- [InlineData(DateTimeIntervalType.Minutes)]
- [InlineData(DateTimeIntervalType.Hours)]
- [InlineData(DateTimeIntervalType.Days)]
- [InlineData(DateTimeIntervalType.Months)]
- [InlineData(DateTimeIntervalType.Years)]
- public void AutoScrollingDeltaType_SetValue_ReturnsExpectedValue(DateTimeIntervalType intervalType)
- {
+ }
+
+ [Theory]
+ [InlineData(DateTimeIntervalType.Auto)]
+ [InlineData(DateTimeIntervalType.Milliseconds)]
+ [InlineData(DateTimeIntervalType.Seconds)]
+ [InlineData(DateTimeIntervalType.Minutes)]
+ [InlineData(DateTimeIntervalType.Hours)]
+ [InlineData(DateTimeIntervalType.Days)]
+ [InlineData(DateTimeIntervalType.Months)]
+ [InlineData(DateTimeIntervalType.Years)]
+ public void AutoScrollingDeltaType_SetValue_ReturnsExpectedValue(DateTimeIntervalType intervalType)
+ {
DateTimeAxis dateTimeAxis = new DateTimeAxis
{
AutoScrollingDeltaType = intervalType
};
Assert.Equal(intervalType, dateTimeAxis.AutoScrollingDeltaType);
- }
-
- [Theory]
- [InlineData(DateTimeIntervalType.Auto)]
- [InlineData(DateTimeIntervalType.Milliseconds)]
- [InlineData(DateTimeIntervalType.Seconds)]
- [InlineData(DateTimeIntervalType.Minutes)]
- [InlineData(DateTimeIntervalType.Hours)]
- [InlineData(DateTimeIntervalType.Days)]
- [InlineData(DateTimeIntervalType.Months)]
- [InlineData(DateTimeIntervalType.Years)]
- public void IntervalType_SetValue_ReturnsExpectedValue(DateTimeIntervalType intervalType)
- {
+ }
+
+ [Theory]
+ [InlineData(DateTimeIntervalType.Auto)]
+ [InlineData(DateTimeIntervalType.Milliseconds)]
+ [InlineData(DateTimeIntervalType.Seconds)]
+ [InlineData(DateTimeIntervalType.Minutes)]
+ [InlineData(DateTimeIntervalType.Hours)]
+ [InlineData(DateTimeIntervalType.Days)]
+ [InlineData(DateTimeIntervalType.Months)]
+ [InlineData(DateTimeIntervalType.Years)]
+ public void IntervalType_SetValue_ReturnsExpectedValue(DateTimeIntervalType intervalType)
+ {
DateTimeAxis dateTimeAxis = new DateTimeAxis
{
IntervalType = intervalType
};
Assert.Equal(intervalType, dateTimeAxis.IntervalType);
- }
+ }
+
+ [Theory]
+ [InlineData("2023-12-31")]
+ [InlineData(null)]
+ public void DateTimeAxis_Maximum_SetValue_ReturnsExpectedValue(string? maximum)
+ {
+ DateTimeAxis dateTimeAxis = new DateTimeAxis();
+ DateTime? maxDate = maximum != null ? DateTime.Parse(maximum) : null;
+ dateTimeAxis.Maximum = maxDate;
+
+ Assert.Equal(maxDate, dateTimeAxis.Maximum);
+ }
+
+ [Theory]
+ [InlineData("2023-01-01")]
+ [InlineData(null)]
+ public void DateTimeAxis_Minimum_SetValue_ReturnsExpectedValue(string? minimum)
+ {
+ var dateTimeAxis = new DateTimeAxis();
+ DateTime? minDate = minimum != null ? DateTime.Parse(minimum) : null;
+ dateTimeAxis.Minimum = minDate;
+
+ Assert.Equal(minDate, dateTimeAxis.Minimum);
+ }
+
+ [Fact]
+ public void DateTimeAxis_PlotBands_SetValue_ReturnsExpectedValue()
+ {
+ DateTimeAxis dateTimeAxis = new DateTimeAxis();
+ var plotBand1 = new DateTimePlotBand() { Start = new DateTime(2023, 1, 1), End = new DateTime(2023, 12, 31), Fill = Colors.Bisque };
+ dateTimeAxis.PlotBands =
+ [
+ plotBand1,
+ ];
+
+ Assert.NotNull(dateTimeAxis.PlotBands);
+ Assert.Single(dateTimeAxis.PlotBands);
+ Assert.Contains(dateTimeAxis.PlotBands, band => band.Start == new DateTime(2023, 1, 1) && band.End == new DateTime(2023, 12, 31));
+ }
+
+ [Theory]
+ [InlineData(DateTimeRangePadding.None)]
+ [InlineData(DateTimeRangePadding.Round)]
+ [InlineData(DateTimeRangePadding.Additional)]
+ [InlineData(DateTimeRangePadding.Auto)]
+ [InlineData(DateTimeRangePadding.RoundStart)]
+ [InlineData(DateTimeRangePadding.RoundEnd)]
+ [InlineData(DateTimeRangePadding.PrependInterval)]
+ [InlineData(DateTimeRangePadding.AppendInterval)]
+ public void DateTimeAxis_RangePadding_SetValue_ReturnsExpectedValue(DateTimeRangePadding rangePadding)
+ {
+ DateTimeAxis dateTimeAxis = new DateTimeAxis
+ {
+ RangePadding = rangePadding
+ };
- [Theory]
- [InlineData("2023-12-31")]
- [InlineData(null)]
- public void DateTimeAxis_Maximum_SetValue_ReturnsExpectedValue(string? maximum)
- {
- DateTimeAxis dateTimeAxis = new DateTimeAxis();
- DateTime? maxDate = maximum != null ? DateTime.Parse(maximum) : null;
- dateTimeAxis.Maximum = maxDate;
+ Assert.Equal(rangePadding, dateTimeAxis.RangePadding);
+ }
- Assert.Equal(maxDate, dateTimeAxis.Maximum);
- }
+ [Theory]
+ [InlineData(2)]
+ [InlineData(4)]
+ [InlineData(0)]
+ public void DateTimeCategoryInterval_SetValue_ReturnsExpectedValue(double interval)
+ {
+ DateTimeCategoryAxis dateTimeCategoryAxis = new DateTimeCategoryAxis
+ {
+ Interval = interval
+ };
- [Theory]
- [InlineData("2023-01-01")]
- [InlineData(null)]
- public void DateTimeAxis_Minimum_SetValue_ReturnsExpectedValue(string? minimum)
- {
- var dateTimeAxis = new DateTimeAxis();
- DateTime? minDate = minimum != null ? DateTime.Parse(minimum) : null;
- dateTimeAxis.Minimum = minDate;
+ Assert.Equal(interval, dateTimeCategoryAxis.Interval);
+ }
- Assert.Equal(minDate, dateTimeAxis.Minimum);
- }
+ [Theory]
+ [InlineData(DateTimeIntervalType.Auto)]
+ [InlineData(DateTimeIntervalType.Milliseconds)]
+ [InlineData(DateTimeIntervalType.Seconds)]
+ [InlineData(DateTimeIntervalType.Minutes)]
+ [InlineData(DateTimeIntervalType.Hours)]
+ [InlineData(DateTimeIntervalType.Days)]
+ [InlineData(DateTimeIntervalType.Months)]
+ [InlineData(DateTimeIntervalType.Years)]
+ public void DateTimeCategoryIntervalType_SetValue_ReturnsExpectedValue(DateTimeIntervalType intervalType)
+ {
+ DateTimeCategoryAxis dateTimeCategory = new DateTimeCategoryAxis
+ {
+ IntervalType = intervalType
+ };
+
+ Assert.Equal(intervalType, dateTimeCategory.IntervalType);
+ }
- [Fact]
- public void DateTimeAxis_PlotBands_SetValue_ReturnsExpectedValue()
- {
- DateTimeAxis dateTimeAxis = new DateTimeAxis();
- var plotBand1 = new DateTimePlotBand() { Start = new DateTime(2023, 1, 1), End = new DateTime(2023, 12, 31), Fill = Colors.Bisque };
- dateTimeAxis.PlotBands =
+ [Fact]
+ public void DateTimeCategory_PlotBands_SetValue_ReturnsExpectedValue()
+ {
+ DateTimeCategoryAxis dateTimeCategory = new DateTimeCategoryAxis();
+ var plotBand1 = new NumericalPlotBand() { Start = 1, End = 5, Fill = Colors.Bisque };
+ dateTimeCategory.PlotBands =
[
plotBand1,
- ];
+ ];
+
+ Assert.NotNull(dateTimeCategory.PlotBands);
+ Assert.Single(dateTimeCategory.PlotBands);
+ Assert.Contains(dateTimeCategory.PlotBands, band => band.Start == 1 && band.End == 5);
+ }
+
+ [Theory]
+ [InlineData(AxisLabelsIntersectAction.Hide)]
+ [InlineData(AxisLabelsIntersectAction.Wrap)]
+ [InlineData(AxisLabelsIntersectAction.MultipleRows)]
+ public void DateTimeCategoryLabelsIntersectAction_SetValue_ReturnsExpectedValue(AxisLabelsIntersectAction action)
+ {
+ DateTimeCategoryAxis dateTimeCategoryAxis = new DateTimeCategoryAxis();
+ dateTimeCategoryAxis.LabelsIntersectAction = action;
+ Assert.Equal(action, dateTimeCategoryAxis.LabelsIntersectAction);
+ }
+
+ [Theory]
+ [InlineData(AxisElementPosition.Outside)]
+ [InlineData(AxisElementPosition.Inside)]
+ public void DateTimeCategoryLabelsPosition_SetValue_ReturnsExpectedValue(AxisElementPosition labelPosition)
+ {
+ DateTimeCategoryAxis dateTimeCategoryXAxis = new DateTimeCategoryAxis
+ {
+ LabelsPosition = labelPosition
+ };
- Assert.NotNull(dateTimeAxis.PlotBands);
- Assert.Single(dateTimeAxis.PlotBands);
- Assert.Contains(dateTimeAxis.PlotBands, band => band.Start == new DateTime(2023, 1, 1) && band.End == new DateTime(2023, 12, 31));
- }
+ Assert.Equal(labelPosition, dateTimeCategoryXAxis.LabelsPosition);
+ }
- [Theory]
- [InlineData(DateTimeRangePadding.None)]
- [InlineData(DateTimeRangePadding.Round)]
- [InlineData(DateTimeRangePadding.Additional)]
- [InlineData(DateTimeRangePadding.Auto)]
- [InlineData(DateTimeRangePadding.RoundStart)]
- [InlineData(DateTimeRangePadding.RoundEnd)]
- [InlineData(DateTimeRangePadding.PrependInterval)]
- [InlineData(DateTimeRangePadding.AppendInterval)]
- public void DateTimeAxis_RangePadding_SetValue_ReturnsExpectedValue(DateTimeRangePadding rangePadding)
- {
- DateTimeAxis dateTimeAxis = new DateTimeAxis
+ [Fact]
+ public void PlotOffset_SetValue_ReturnsExpectedValue()
+ {
+ DateTimeCategoryAxis dateTimeCategory = new DateTimeCategoryAxis
{
- RangePadding = rangePadding
+ PlotOffsetStart = 10,
+ PlotOffsetEnd = 30
};
- Assert.Equal(rangePadding, dateTimeAxis.RangePadding);
+ Assert.Equal(10, dateTimeCategory.PlotOffsetStart);
+ Assert.Equal(30, dateTimeCategory.PlotOffsetEnd);
}
- }
+
+ [Fact]
+ public void MaximumLabels_IsDefault_ReturnsDefaultValue()
+ {
+ NumericalAxis axis = new();
+
+ Assert.Equal(3, axis.MaximumLabels);
+ }
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(5)]
+ [InlineData(10)]
+ public void MaximumLabels_SetValue_ReturnsExpectedValue(int maxLabels)
+ {
+ NumericalAxis axis = new()
+ {
+ MaximumLabels = maxLabels
+ };
+
+ Assert.Equal(maxLabels, axis.MaximumLabels);
+ }
+
+ [Fact]
+ public void MaximumLabels_IsZero_ReturnsExpectedValue()
+ {
+ NumericalAxis axis = new()
+ {
+ MaximumLabels = 0
+ };
+
+ Size availableSize = new(800, 400);
+
+ double intervalCount = axis.GetActualDesiredIntervalsCount(availableSize);
+
+ Assert.Equal(1.0, intervalCount);
+ }
+
+ [Fact]
+ public void MaximumLabels_IsDefault_ReturnsExpectedValue()
+ {
+ NumericalAxis axis = new();
+
+ Size availableSize = new(800, 400);
+
+ double intervalCount = axis.GetActualDesiredIntervalsCount(availableSize);
+
+ Assert.True(intervalCount > 1.0);
+ }
+ }
}
\ No newline at end of file
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/ChartSeriesUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/ChartSeriesUnitTests.cs
index a91259fa..faf0fdeb 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/ChartSeriesUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/ChartSeriesUnitTests.cs
@@ -371,7 +371,8 @@ public void DoughnutSeries_Constructor_InitializesDefaultsCorrectly()
var doughnutSeries = new DoughnutSeries();
Assert.Equal(0.4d, doughnutSeries.InnerRadius);
-
+ Assert.Equal(0d, doughnutSeries.GapRatio);
+ Assert.Equal(CapStyle.BothFlat, doughnutSeries.CapStyle);
Assert.Null(doughnutSeries.CenterView);
}
@@ -389,6 +390,36 @@ public void InnerRadius_SetAndGet_ReturnsExpectedValue(double expected)
Assert.Equal(expected, doughnutSeries.InnerRadius);
}
+ [Theory]
+ [InlineData(0.0)]
+ [InlineData(0.5)]
+ [InlineData(0.75)]
+ [InlineData(1.0)]
+ public void DoughnutSeries_Spacing_SetAndGet_ReturnsExpectedValue(double expected)
+ {
+ var doughnutSeries = new DoughnutSeries
+ {
+ GapRatio = expected
+ };
+
+ Assert.Equal(expected, doughnutSeries.GapRatio);
+ }
+
+ [Theory]
+ [InlineData(CapStyle.BothFlat)]
+ [InlineData(CapStyle.BothCurve)]
+ [InlineData(CapStyle.EndCurve)]
+ [InlineData(CapStyle.StartCurve)]
+ public void DoughnutSeries_CapStyle_SetAndGet_ReturnsExpectedValue(CapStyle expected)
+ {
+ var doughnutSeries = new DoughnutSeries
+ {
+ CapStyle = expected
+ };
+
+ Assert.Equal(expected, doughnutSeries.CapStyle);
+ }
+
[Fact]
public void CenterView_SetAndGet_ReturnsExpectedValue()
{
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/ChartAxisUnitTest.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/ChartAxisUnitTest.cs
index d544aba6..22de3e63 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/ChartAxisUnitTest.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/ChartAxisUnitTest.cs
@@ -8,7 +8,7 @@ public class ChartAxisUnitTest
[Fact]
public void CategoryAxis_InitializesBaseProperties()
{
- var axis = new CategoryAxis();
+ var axis = new CategoryAxis();
Assert.IsType(axis);
Assert.IsAssignableFrom(axis);
Assert.Equal(LabelPlacement.OnTicks, axis.LabelPlacement);
@@ -23,7 +23,7 @@ public void CategoryAxis_InitializesBaseProperties()
[Fact]
public void CategoryAxis_InitializesLineStyles()
{
- var axis = new CategoryAxis();
+ var axis = new CategoryAxis();
Assert.NotNull(axis.AxisLineStyle);
Assert.IsType(axis.AxisLineStyle);
Assert.NotNull(axis.MajorGridLineStyle);
@@ -37,7 +37,7 @@ public void CategoryAxis_InitializesLineStyles()
[Fact]
public void CategoryAxis_InitializesCrossingProperties()
{
- var axis = new CategoryAxis();
+ var axis = new CategoryAxis();
Assert.Equal(double.NaN, axis.CrossesAt);
Assert.True(axis.RenderNextToCrossingValue);
Assert.Null(axis.CrossAxisName);
@@ -80,7 +80,7 @@ public void CategoryAxis_InitializesTrackballProperties()
[Fact]
public void CategoryAxis_InitializesPositionProperties()
{
- var axis = new CategoryAxis();
+ var axis = new CategoryAxis();
Assert.Null(axis.AxisLabelsRenderer);
Assert.Null(axis.AxisElementRenderer);
Assert.Equal(string.Empty, axis.Name);
@@ -94,7 +94,7 @@ public void CategoryAxis_InitializesPositionProperties()
[Fact]
public void CategoryAxis_InitializesSeriesAndPlotBandProperties()
{
- var axis = new CategoryAxis();
+ var axis = new CategoryAxis();
Assert.True(double.IsNaN(axis.Interval));
Assert.NotNull(axis.RegisteredSeries);
Assert.Empty(axis.RegisteredSeries);
@@ -192,12 +192,196 @@ public void CategoryAxis_NullAndNumericProperties()
Assert.Equal(0d, axis.ActualPlotOffsetStart);
}
+ [Fact]
+ public void DateTimeCategoryAxis_InitializesBaseProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.IsType(axis);
+ Assert.IsAssignableFrom(axis);
+ Assert.True(axis.IsVisible);
+ Assert.Equal(0d, axis.AxisLineOffset);
+ Assert.Equal(0d, axis.LabelRotation);
+ Assert.NotNull(axis.LabelStyle);
+ Assert.IsType(axis.LabelStyle);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_InitializesLineStyles()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.NotNull(axis.AxisLineStyle);
+ Assert.IsType(axis.AxisLineStyle);
+ Assert.NotNull(axis.MajorGridLineStyle);
+ Assert.IsType(axis.MajorGridLineStyle);
+ Assert.NotNull(axis.MajorTickStyle);
+ Assert.IsType(axis.MajorTickStyle);
+ Assert.Equal(Color.FromArgb("#E7E0EC"), axis.MajorGridLineStroke.ToColor());
+ Assert.True(axis.ShowMajorGridLines);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_InitializesCrossingProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.Equal(double.NaN, axis.CrossesAt);
+ Assert.True(axis.RenderNextToCrossingValue);
+ Assert.Null(axis.CrossAxisName);
+ Assert.Equal(double.NaN, axis.ActualCrossingValue);
+ Assert.Null(axis.Title);
+ Assert.False(axis.IsInversed);
+ Assert.Equal(EdgeLabelsDrawingMode.Shift, axis.EdgeLabelsDrawingMode);
+ Assert.Equal(AxisLabelsIntersectAction.Hide, axis.LabelsIntersectAction);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_InitializesZoomProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.NotNull(axis.VisibleLabels);
+ Assert.Empty(axis.VisibleLabels);
+ Assert.Equal(0d, axis.LabelExtent);
+ Assert.Equal(AxisElementPosition.Outside, axis.LabelsPosition);
+ Assert.Equal(0d, axis.ZoomPosition);
+ Assert.Equal(1d, axis.ZoomFactor);
+ Assert.True(axis.EnableAutoIntervalOnZooming);
+ Assert.Equal(ChartAutoScrollingMode.End, axis.AutoScrollingMode);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_InitializesTrackballProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.False(axis.IsScrolling);
+ Assert.Null(axis.AxisRenderer);
+ Assert.Equal(0, axis.SideBySideSeriesCount);
+ Assert.Equal(double.NaN, axis.AutoScrollingDelta);
+ Assert.False(axis.ShowTrackballLabel);
+ Assert.NotNull(axis.TrackballLabelStyle);
+ Assert.IsType(axis.TrackballLabelStyle);
+ Assert.Equal(SolidColorBrush.Black, axis.TrackballAxisBackground);
+ Assert.Equal(12.0, axis.TrackballAxisFontSize);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_InitializesPositionProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.Null(axis.AxisLabelsRenderer);
+ Assert.Null(axis.AxisElementRenderer);
+ Assert.Equal(string.Empty, axis.Name);
+ Assert.Equal(AxisElementPosition.Outside, axis.TickPosition);
+ Assert.Equal(0, axis.PlotOffsetEnd);
+ Assert.Equal(0, axis.PlotOffsetStart);
+ Assert.NotNull(axis.TickPositions);
+ Assert.Empty(axis.TickPositions);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_InitializesSeriesAndPlotBandProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.True(double.IsNaN(axis.Interval));
+ Assert.NotNull(axis.RegisteredSeries);
+ Assert.Empty(axis.RegisteredSeries);
+ Assert.NotNull(axis.ActualPlotBands);
+ Assert.Empty(axis.ActualPlotBands);
+ Assert.Null(axis.RangeStyles);
+ Assert.Equal(3, axis.MaximumLabels);
+ Assert.Null(axis.PlotBands);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_InitializesLabelStyleProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ var labelStyle = axis.LabelStyle;
+ Assert.True(axis.VisibleRange.IsEmpty);
+ Assert.NotNull(labelStyle);
+ Assert.IsType(labelStyle);
+ Assert.Equal(12d, labelStyle.FontSize);
+ Assert.Equal(FontAttributes.None, labelStyle.FontAttributes);
+ Assert.Null(labelStyle.FontFamily);
+ Assert.Equal(Color.FromArgb("#49454F"), labelStyle.TextColor);
+ Assert.Equal(string.Empty, labelStyle.LabelFormat);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_InitializesMissingProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ var labelStyle = axis.LabelStyle;
+ Assert.True(double.IsNaN(axis.AxisInterval));
+ Assert.True(double.IsNaN(axis.AutoScrollingDelta));
+ Assert.Null(axis.CrossAxisName);
+ Assert.Equal(double.NaN, axis.VisibleMaximum);
+ Assert.Equal(double.NaN, axis.VisibleMinimum);
+ Assert.Null(axis.TrackballLabelTemplate);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_OffsetAndSizeProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.Equal(0f, axis.LeftOffset);
+ Assert.Equal(0f, axis.TopOffset);
+ Assert.Equal(0d, axis.ActualPlotOffset);
+ Assert.Equal(0d, axis.ActualPlotOffsetStart);
+ Assert.Equal(0d, axis.ActualPlotOffsetEnd);
+ Assert.Equal(Size.Zero, axis.ComputedDesiredSize);
+ Assert.Equal(0d, axis.InsidePadding);
+ Assert.Equal(Size.Zero, axis.AvailableSize);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_IntervalAndRangeProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.Equal(0d, axis.ActualInterval);
+ Assert.Equal(0d, axis.ActualRange.Start);
+ Assert.Equal(0d, axis.ActualRange.End);
+ Assert.Equal(0d, axis.ActualRange.Delta);
+ Assert.Equal(0d, axis.ActualRange.Median);
+ Assert.Equal(0d, axis.VisibleInterval);
+ Assert.False(axis.SmallTickRequired);
+ Assert.Equal(default(RectF), axis.RenderedRect);
+ Assert.True(double.IsNaN(axis.AxisInterval));
+ Assert.Equal(new int[] { 10, 5, 2, 1 }, ChartAxis.IntervalDivs);
+ Assert.Equal(Rect.Zero, axis.ArrangeRect);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_BooleanProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.False(axis.IsPolarArea);
+ Assert.True(double.IsNaN(axis.ActualAutoScrollDelta));
+ Assert.False(axis.CanAutoScroll);
+ Assert.False(axis.IsVertical);
+ Assert.False(axis._isOverriddenOnCreateLabelsMethod);
+ Assert.NotNull(axis.AssociatedAxes);
+ Assert.Empty(axis.AssociatedAxes);
+ }
+
+ [Fact]
+ public void DateTimeCategoryAxis_NullAndNumericProperties()
+ {
+ var axis = new DateTimeCategoryAxis();
+ Assert.Null(axis.Area);
+ Assert.Null(axis.CartesianArea);
+ Assert.Null(axis.PolarArea);
+ Assert.Equal(270, axis.PolarStartAngle);
+ Assert.Equal(0f, axis.LeftOffset);
+ Assert.Equal(0f, axis.TopOffset);
+ Assert.Equal(0d, axis.ActualPlotOffset);
+ Assert.Equal(0d, axis.ActualPlotOffsetStart);
+ }
+
[Fact]
public void DateTimeAxis_DefaultDateTimeProperties()
{
var axis = new DateTimeAxis();
Assert.Equal(DateTimeIntervalType.Auto, axis.IntervalType);
- Assert.Null(axis.Minimum);
+ Assert.Null(axis.Minimum);
Assert.IsType(axis);
Assert.IsAssignableFrom(axis);
Assert.Null(axis.Maximum);
@@ -243,7 +427,7 @@ public void DateTimeAxis_DefaultProperties()
{
var axis = new DateTimeAxis();
Assert.Null(axis.DefaultMinimum);
- Assert.Null(axis.DefaultMaximum);
+ Assert.Null(axis.DefaultMaximum);
Assert.Equal(ChartAutoScrollingMode.End, axis.AutoScrollingMode);
Assert.Equal(double.NaN, axis.AutoScrollingDelta);
Assert.Equal(EdgeLabelsDrawingMode.Shift, axis.EdgeLabelsDrawingMode);
@@ -255,7 +439,7 @@ public void DateTimeAxis_DefaultProperties()
[Fact]
public void DateTimeAxis_DefaultZoomAndScrollProperties()
{
- var axis = new DateTimeAxis();
+ var axis = new DateTimeAxis();
Assert.Equal(AxisElementPosition.Outside, axis.TickPosition);
Assert.Equal(AxisElementPosition.Outside, axis.LabelsPosition);
Assert.IsType(axis.TrackballLabelStyle);
@@ -265,12 +449,12 @@ public void DateTimeAxis_DefaultZoomAndScrollProperties()
Assert.Equal(1d, axis.ZoomFactor);
Assert.True(axis.EnableAutoIntervalOnZooming);
}
-
+
[Fact]
public void DateTimeAxis_DefaultLabelStyleProperties()
{
var axis = new DateTimeAxis();
- var labelStyle = axis.LabelStyle;
+ var labelStyle = axis.LabelStyle;
Assert.True(axis.VisibleRange.IsEmpty);
Assert.Equal(default(DateTime), axis.ActualMinimum);
Assert.Equal(default(DateTime), axis.ActualMaximum);
@@ -319,7 +503,7 @@ public void DateTimeAxis_IntervalAndRangeProperties()
Assert.Equal(0d, axis.ActualRange.End);
Assert.Equal(0d, axis.ActualRange.Delta);
Assert.Equal(0d, axis.ActualRange.Median);
- Assert.Equal(0d, axis.ActualInterval);
+ Assert.Equal(0d, axis.ActualInterval);
Assert.Equal(0d, axis.VisibleInterval);
Assert.False(axis.SmallTickRequired);
Assert.Equal(default(RectF), axis.RenderedRect);
@@ -339,8 +523,8 @@ public void DateTimeAxis_BooleanProperties()
Assert.Equal(Rect.Zero, axis.ArrangeRect);
Assert.False(axis.IsScrolling);
Assert.False(axis._isOverriddenOnCreateLabelsMethod);
- Assert.Equal(0d, axis.PlotOffsetEnd);
- Assert.Equal(270, axis.PolarStartAngle);
+ Assert.Equal(0d, axis.PlotOffsetEnd);
+ Assert.Equal(270, axis.PolarStartAngle);
Assert.Equal(EdgeLabelsVisibilityMode.Default, axis.EdgeLabelsVisibilityMode);
Assert.Equal(0, axis.MinorTicksPerInterval);
}
@@ -356,15 +540,15 @@ public void DateTimeAxis_NullAndCollectionProperties()
Assert.Null(axis.AxisRenderer);
Assert.Null(axis.RangeStyles);
Assert.Equal(0, axis.SideBySideSeriesCount);
- Assert.Equal(3, axis.MaximumLabels);
+ Assert.Equal(3, axis.MaximumLabels);
Assert.NotNull(axis.MinorGridLineStyle);
Assert.IsType(axis.MinorGridLineStyle);
- }
+ }
[Fact]
public void DateTimeAxis_RangeAxisBaseProperties()
{
- var axis = new DateTimeAxis();
+ var axis = new DateTimeAxis();
Assert.NotNull(axis.MinorTickStyle);
Assert.IsType(axis.MinorTickStyle);
Assert.True(axis.ShowMinorGridLines);
@@ -374,13 +558,13 @@ public void DateTimeAxis_RangeAxisBaseProperties()
Assert.IsType(axis.MinorGridLineStroke);
Assert.Equal(Color.FromArgb("#EDEFF1"), (axis.MinorGridLineStroke as SolidColorBrush)!.Color);
Assert.Equal(0.5f, axis.MinorGridLineStyle.StrokeWidth);
- Assert.Equal(4d, axis.MinorTickStyle.TickSize);
+ Assert.Equal(4d, axis.MinorTickStyle.TickSize);
}
[Fact]
public void NumericalAxis_DefaultNumericProperties()
{
- var axis = new NumericalAxis();
+ var axis = new NumericalAxis();
Assert.IsType(axis);
Assert.IsAssignableFrom(axis);
Assert.Equal(NumericalPadding.Auto, axis.RangePadding);
@@ -424,7 +608,7 @@ public void NumericalAxis_DefaultStyleProperties()
[Fact]
public void NumericalAxis_DefaultZoomAndScrollProperties()
{
- var axis = new NumericalAxis();
+ var axis = new NumericalAxis();
Assert.True(axis.ShowMajorGridLines);
Assert.False(axis.ShowTrackballLabel);
Assert.IsType(axis.TrackballLabelStyle);
@@ -435,13 +619,13 @@ public void NumericalAxis_DefaultZoomAndScrollProperties()
Assert.Equal(double.NaN, axis.AutoScrollingDelta);
}
-
+
[Fact]
public void NumericalAxis_DefaultLabelStyleProperties()
{
var axis = new NumericalAxis();
- var labelStyle = axis.LabelStyle;
+ var labelStyle = axis.LabelStyle;
Assert.True(axis.VisibleRange.IsEmpty);
Assert.Equal(0, axis.ActualMinimum);
Assert.Equal(0, axis.ActualMaximum);
@@ -455,7 +639,7 @@ public void NumericalAxis_DefaultLabelStyleProperties()
[Fact]
public void NumericalAxis_DefaultCollectionProperties()
{
- var axis = new NumericalAxis();
+ var axis = new NumericalAxis();
Assert.Null(axis.PlotBands);
Assert.NotNull(axis.VisibleLabels);
Assert.Empty(axis.VisibleLabels);
@@ -533,7 +717,7 @@ public void NumericalAxis_RemainingProperties()
Assert.Equal(0d, axis.PlotOffsetEnd);
Assert.Equal(0, axis.SideBySideSeriesCount);
Assert.Equal(3, axis.MaximumLabels);
- Assert.Equal(270, axis.PolarStartAngle);
+ Assert.Equal(270, axis.PolarStartAngle);
Assert.Equal(EdgeLabelsVisibilityMode.Default, axis.EdgeLabelsVisibilityMode);
Assert.Equal(0, axis.MinorTicksPerInterval);
Assert.NotNull(axis.MinorGridLineStyle);
@@ -547,12 +731,12 @@ public void NumericalAxis_RangeAxisBaseProperties()
Assert.NotNull(axis.MinorTickStyle);
Assert.IsType(axis.MinorTickStyle);
- Assert.True(axis.ShowMinorGridLines);
+ Assert.True(axis.ShowMinorGridLines);
Assert.NotNull(axis.SmallTickPoints);
Assert.Empty(axis.SmallTickPoints);
Assert.NotNull(axis.MinorGridLineStroke);
Assert.IsType(axis.MinorGridLineStroke);
- Assert.Equal(Color.FromArgb("#EDEFF1"), (axis.MinorGridLineStroke as SolidColorBrush)!.Color);
+ Assert.Equal(Color.FromArgb("#EDEFF1"), (axis.MinorGridLineStroke as SolidColorBrush)!.Color);
Assert.Equal(0.5f, axis.MinorGridLineStyle.StrokeWidth);
Assert.Equal(4d, axis.MinorTickStyle.TickSize);
@@ -561,7 +745,7 @@ public void NumericalAxis_RangeAxisBaseProperties()
[Fact]
public void LogarithmicAxis_DefaultNumericProperties()
{
- var axis = new LogarithmicAxis();
+ var axis = new LogarithmicAxis();
Assert.IsType(axis);
Assert.IsAssignableFrom(axis);
Assert.Null(axis.Minimum);
@@ -581,7 +765,7 @@ public void LogarithmicAxis_DefaultVisibleRange()
Assert.Equal(expectedValue.End, axis.VisibleRange.End);
Assert.Equal(expectedValue.Delta, axis.VisibleRange.Delta);
Assert.Equal(expectedValue.Median, axis.VisibleRange.Median);
- Assert.Equal(expectedValue.IsEmpty, axis.VisibleRange.IsEmpty);
+ Assert.Equal(expectedValue.IsEmpty, axis.VisibleRange.IsEmpty);
Assert.Equal(0d, axis.ZoomPosition);
Assert.Equal(1d, axis.ZoomFactor);
Assert.Equal(EdgeLabelsDrawingMode.Shift, axis.EdgeLabelsDrawingMode);
@@ -604,7 +788,7 @@ public void LogarithmicAxis_DefaultAxisProperties()
[Fact]
public void LogarithmicAxis_DefaultStyleProperties()
{
- var axis = new LogarithmicAxis();
+ var axis = new LogarithmicAxis();
Assert.Equal(double.NaN, axis.AutoScrollingDelta);
Assert.NotNull(axis.LabelStyle);
Assert.IsType(axis.LabelStyle);
@@ -614,7 +798,7 @@ public void LogarithmicAxis_DefaultStyleProperties()
Assert.IsType(axis.MajorGridLineStyle);
Assert.NotNull(axis.MajorTickStyle);
Assert.IsType(axis.MajorTickStyle);
- }
+ }
[Fact]
public void LogarithmicAxis_DefaultLabelStyleProperties()
@@ -625,7 +809,7 @@ public void LogarithmicAxis_DefaultLabelStyleProperties()
Assert.Equal(0, axis.ActualMaximum);
Assert.Equal(12d, labelStyle.FontSize);
Assert.Equal(FontAttributes.None, labelStyle.FontAttributes);
- Assert.Null(labelStyle.FontFamily);
+ Assert.Null(labelStyle.FontFamily);
Assert.Null(axis.PlotBands);
Assert.Equal(Color.FromArgb("#49454F"), labelStyle.TextColor);
Assert.Equal(string.Empty, labelStyle.LabelFormat);
@@ -642,7 +826,7 @@ public void LogarithmicAxis_DefaultCollectionProperties()
Assert.NotNull(axis.RegisteredSeries);
Assert.Empty(axis.RegisteredSeries);
Assert.NotNull(axis.ActualPlotBands);
- Assert.Empty(axis.ActualPlotBands);
+ Assert.Empty(axis.ActualPlotBands);
Assert.True(axis.EnableAutoIntervalOnZooming);
Assert.Equal(ChartAutoScrollingMode.End, axis.AutoScrollingMode);
}
@@ -657,11 +841,11 @@ public void LogarithmicAxis_DefaultOffsetProperties()
Assert.Equal(0d, axis.ActualPlotOffsetStart);
Assert.Equal(0d, axis.ActualPlotOffsetEnd);
Assert.Equal(0d, axis.PlotOffsetStart);
- Assert.Equal(0d, axis.PlotOffsetEnd);
+ Assert.Equal(0d, axis.PlotOffsetEnd);
Assert.Equal(Size.Zero, axis.ComputedDesiredSize);
Assert.Equal(0d, axis.InsidePadding);
}
-
+
[Fact]
public void LogarithmicAxis_DefaultIntervalProperties()
@@ -674,9 +858,9 @@ public void LogarithmicAxis_DefaultIntervalProperties()
Assert.Equal(0d, axis.ActualRange.Median);
Assert.Equal(0d, axis.VisibleInterval);
Assert.Equal(new int[] { 10, 5, 2, 1 }, ChartAxis.IntervalDivs);
- Assert.Equal(0, axis.MinorTicksPerInterval);
+ Assert.Equal(0, axis.MinorTicksPerInterval);
Assert.False(axis.SmallTickRequired);
- Assert.False(axis.IsPolarArea);
+ Assert.False(axis.IsPolarArea);
Assert.False(axis.IsVertical);
Assert.False(axis.CanAutoScroll);
}
@@ -687,16 +871,16 @@ public void LogarithmicAxis_DefaultBooleanProperties()
var axis = new LogarithmicAxis();
Assert.False(axis.IsScrolling);
Assert.False(axis._isOverriddenOnCreateLabelsMethod);
- Assert.False(axis.ShowTrackballLabel);
+ Assert.False(axis.ShowTrackballLabel);
Assert.NotNull(axis.AssociatedAxes);
Assert.Empty(axis.AssociatedAxes);
- Assert.Null(axis.RangeStyles);
+ Assert.Null(axis.RangeStyles);
Assert.Equal(Size.Zero, axis.AvailableSize);
Assert.Equal(default(RectF), axis.RenderedRect);
Assert.Equal(Rect.Zero, axis.ArrangeRect);
}
-
+
[Fact]
public void LogarithmicAxis_DefaultNullProperties()
@@ -705,12 +889,12 @@ public void LogarithmicAxis_DefaultNullProperties()
Assert.Null(axis.Area);
Assert.Null(axis.CartesianArea);
Assert.Null(axis.AxisRenderer);
- Assert.Null(axis.TrackballLabelTemplate);
+ Assert.Null(axis.TrackballLabelTemplate);
Assert.True(double.IsNaN(axis.ActualAutoScrollDelta));
Assert.Equal(0, axis.SideBySideSeriesCount);
Assert.Equal(3, axis.MaximumLabels);
Assert.Equal(270, axis.PolarStartAngle);
- }
+ }
[Fact]
public void LogarithmicAxis_DefaultEnumProperties()
{
@@ -730,16 +914,16 @@ public void LogarithmicAxis_DefaultEnumProperties()
public void LogarithmicAxis_RangeAxisBaseProperties()
{
var axis = new LogarithmicAxis();
-
- Assert.True(axis.ShowMinorGridLines);
+
+ Assert.True(axis.ShowMinorGridLines);
Assert.NotNull(axis.SmallTickPoints);
Assert.Empty(axis.SmallTickPoints);
Assert.NotNull(axis.MinorGridLineStroke);
Assert.IsType(axis.MinorGridLineStroke);
- Assert.Equal(Color.FromArgb("#EDEFF1"), (axis.MinorGridLineStroke as SolidColorBrush)!.Color);
+ Assert.Equal(Color.FromArgb("#EDEFF1"), (axis.MinorGridLineStroke as SolidColorBrush)!.Color);
Assert.Equal(0.5f, axis.MinorGridLineStyle.StrokeWidth);
Assert.Equal(4d, axis.MinorTickStyle.TickSize);
-
+
}
}
}
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/SeriesDefaultTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/SeriesDefaultTests.cs
index b4741a87..d868ae0d 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/SeriesDefaultTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/SeriesDefaultTests.cs
@@ -153,21 +153,17 @@ public void AreaSeries_InitializeInternalSettingsCorrectly()
public void AreaSeries_InitializeInternalsSettingsCorrectly()
{
var series = new AreaSeries();
+ series.ActualXAxis = new CategoryAxis();
Assert.False(series.IsSideBySide);
Assert.True(series.YValues.Count == 0);
Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
Assert.Null(series.ChartArea);
Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis categoryAxis, series.IsGrouped);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
- else
- {
- Assert.False(series.IsGrouped);
- }
-
+
Assert.Equal(0, series.SideBySideIndex);
Assert.False(series.IsSbsValueCalculated);
}
@@ -318,19 +314,16 @@ public void BoxAndWhiskerSeries_InitializeInternalSettingsCorrectly()
public void BoxAndWhiskerSeries_InitializeInternalsSettingsCorrectly()
{
var series = new BoxAndWhiskerSeries();
+ series.ActualXAxis = new CategoryAxis();
Assert.True(series.IsSideBySide);
Assert.True(series.YValues.Count == 0);
Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
Assert.Null(series.ChartArea);
Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis categoryAxis, series.IsGrouped);
+
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -469,20 +462,17 @@ public void BubbleSeries_InitializeInternalSettingsCorrectly()
public void BubbleSeries_InitializeInternalsSettingsCorrectly()
{
var series = new BubbleSeries();
+ series.ActualXAxis = new CategoryAxis();
Assert.False(series.IsSideBySide);
Assert.False(series.IsStacking);
Assert.True(series.YValues.Count == 0);
Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
Assert.Null(series.ChartArea);
Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis categoryAxis, series.IsGrouped);
+
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -635,7 +625,6 @@ public void CandleSeriesDefaultTests_Part13()
public void CandleSeriesDefaultTests_Part17()
{
var series = new CandleSeries();
- Assert.False(series.IsGrouped);
Assert.NotNull(series.HighValues);
Assert.NotNull(series.LowValues);
Assert.NotNull(series.OpenValues);
@@ -723,19 +712,15 @@ public void WaterfallSeriesDefaultTests_Part4()
public void WaterfallSeriesDefaultTests_Part5()
{
var series = new WaterfallSeries();
- Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
+ series.ActualXAxis = new CategoryAxis();
+ Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
Assert.True(series.SbsInfo.IsEmpty);
Assert.Null(series.ChartArea);
Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -891,19 +876,15 @@ public void StepLineSeriesDefaultTests_Part4()
public void StepLineSeriesDefaultTests_Part5()
{
var series = new StepLineSeries();
- Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
+ series.ActualXAxis = new CategoryAxis();
+ Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
Assert.True(series.SbsInfo.IsEmpty);
Assert.Null(series.ChartArea);
Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
}
@@ -1059,19 +1040,15 @@ public void StepAreaSeriesDefaultTests_Part4()
public void StepAreaSeriesDefaultTests_Part5()
{
var series = new StepAreaSeries();
- Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
+ series.ActualXAxis = new CategoryAxis();
+ Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
Assert.True(series.SbsInfo.IsEmpty);
Assert.Null(series.ChartArea);
Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
}
@@ -1240,16 +1217,12 @@ public void StackingLineSeriesDefaultTests_Part5()
public void StackingLineSeriesDefaultTests_Part6()
{
var series = new StackingLineSeries();
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
+ series.ActualXAxis = new CategoryAxis();
+ Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -1419,16 +1392,12 @@ public void StackingLine100SeriesDefaultTests_Part5()
public void StackingLine100SeriesDefaultTests_Part6()
{
var series = new StackingLine100Series();
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
+ series.ActualXAxis = new CategoryAxis();
+ Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -1598,16 +1567,12 @@ public void StackingColumnSeriesDefaultTests_Part5()
public void StackingColumnSeriesDefaultTests_Part6()
{
var series = new StackingColumnSeries();
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
+ series.ActualXAxis = new CategoryAxis();
+ Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -1777,16 +1742,12 @@ public void StackingColumn100SeriesDefaultTests_Part5()
public void StackingColumn100SeriesDefaultTests_Part6()
{
var series = new StackingColumn100Series();
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
+ series.ActualXAxis = new CategoryAxis();
+ Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -1956,16 +1917,12 @@ public void StackingAreaSeriesDefaultTests_Part5()
public void StackingAreaSeriesDefaultTests_Part6()
{
var series = new StackingAreaSeries();
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
+ series.ActualXAxis = new CategoryAxis();
+ Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -2135,16 +2092,12 @@ public void StackingArea100SeriesDefaultTests_Part5()
public void StackingArea100SeriesDefaultTests_Part6()
{
var series = new StackingArea100Series();
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
+ series.ActualXAxis = new CategoryAxis();
+ Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -2310,20 +2263,15 @@ public void SplineSeriesDefaultTests_Part4()
public void SplineSeriesDefaultTests_Part5()
{
var series = new SplineSeries();
-
+ series.ActualXAxis = new CategoryAxis();
Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
Assert.True(series.SbsInfo.IsEmpty);
Assert.Null(series.ChartArea);
Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -2496,20 +2444,15 @@ public void SplineRangeAreaSeriesDefaultTests_Part4()
public void SplineRangeAreaSeriesDefaultTests_Part5()
{
var series = new SplineRangeAreaSeries();
-
+ series.ActualXAxis = new CategoryAxis();
Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
Assert.True(series.SbsInfo.IsEmpty);
Assert.Null(series.ChartArea);
Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
@@ -2682,20 +2625,15 @@ public void SplineAreaSeriesDefaultTests_Part4()
public void SplineAreaSeriesDefaultTests_Part5()
{
var series = new SplineAreaSeries();
-
+ series.ActualXAxis = new CategoryAxis();
Assert.Equal(series.DataLabelSettings, series.ChartDataLabelSettings);
Assert.True(series.SbsInfo.IsEmpty);
Assert.Null(series.ChartArea);
Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsIndexed);
- Assert.Equal(series.ActualXAxis is CategoryAxis, series.IsGrouped);
if (series.ActualXAxis is CategoryAxis category)
{
- Assert.Equal(category.ArrangeByIndex, series.IsGrouped);
- }
- else
- {
- Assert.False(series.IsGrouped);
+ Assert.Equal(category.ArrangeByIndex, series.IsIndexed);
}
Assert.Equal(0, series.SideBySideIndex);
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/SeriesDefaultTestsPartial.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/SeriesDefaultTestsPartial.cs
index 45f05750..eddc1a93 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/SeriesDefaultTestsPartial.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/DefaultTests/SeriesDefaultTestsPartial.cs
@@ -636,7 +636,6 @@ public void HistogramSeriesDefaultTests_Part11()
{
var series = new HistogramSeries();
Assert.False(series.IsIndexed);
- Assert.False(series.IsGrouped);
Assert.True(series.SbsInfo.IsEmpty);
Assert.Equal(0, series.SideBySideIndex);
Assert.False(series.IsSbsValueCalculated);
@@ -1457,7 +1456,6 @@ public void ColumnSeriesDefaultTests_Part11()
Assert.False(series.IsSbsValueCalculated);
Assert.Null(series.ChartArea);
Assert.False(series.IsIndexed);
- Assert.False(series.IsGrouped);
}
[Fact]
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/Features/SegmentUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/Features/SegmentUnitTests.cs
index ded4b1a2..9f12fd2c 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/Features/SegmentUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Chart/Features/SegmentUnitTests.cs
@@ -582,8 +582,7 @@ public void UpdatePosition_ShouldSetActualBoundsAndRadius()
doughnutSegment.Series = doughnutSeries;
sfCircularChart.Series.Add(doughnutSeries);
SetPrivateField(doughnutSegment, "_currentBounds", new RectF(10, 20, 100, 100));
- var store = Array.Empty();
- InvokePrivateMethod(doughnutSegment, "UpdatePosition", store);
+ InvokePrivateMethod(doughnutSegment, "UpdatePosition", doughnutSeries);
var actualBounds = GetPrivateField(doughnutSegment, "_actualBounds");
var segmentRadius = GetPrivateField(doughnutSegment, "_segmentRadius");
Assert.NotEqual(RectF.Zero, actualBounds);
@@ -1073,5 +1072,38 @@ public void FastScatter_OnLayout_ShouldUpdateSegmentPointsCorrectly()
Assert.NotEmpty(plottingPoints);
Assert.Equal(fastScatterSeries.PointsCount, plottingPoints.Count);
}
+
+
+ [Fact]
+ public void CalculateCurvePoints_ShouldCalculateCorrectPoints()
+ {
+ float segmentAngle = 24.5553341f;
+ float arcAngle = 0f;
+ float innerRadius = 79.488f;
+ float outerRadius = 198.72f;
+ float midRadius = 139.104f;
+
+ DoughnutSeries series = new DoughnutSeries();
+ series.Center = new PointF(447.2f, 248.4f);
+ DoughnutSegment segment = new DoughnutSegment();
+
+ PointF[]? result = (PointF[]?)InvokePrivateMethod(segment, "CalculateCurvePoints", [series, segmentAngle, arcAngle, innerRadius, outerRadius, midRadius]);
+
+ if (result != null)
+ {
+ PointF innerPoint = result[0];
+ PointF innerMidPoint = result[1];
+ PointF centerPoint = result[2];
+ PointF outerMidPoint = result[3];
+ PointF outerPoint = result[4];
+ Assert.Equal(new PointF(519.499146f, 281.432983f), innerPoint);
+ Assert.Equal(new PointF(627.9479f, 330.982422f), outerPoint);
+ Assert.Equal(new PointF(586.304f, 248.4f), centerPoint);
+ Assert.Equal(new PointF(526.688f, 248.4f), innerMidPoint);
+ Assert.Equal(new PointF(645.920044f, 248.4f), outerMidPoint);
+ }
+
+
+ }
}
}
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Editors/SfOtpInputUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Editors/SfOtpInputUnitTests.cs
index 6b1fe3ed..0c17db14 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Editors/SfOtpInputUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Editors/SfOtpInputUnitTests.cs
@@ -120,7 +120,7 @@ public void GetVisualState_Filled(bool enabled, bool hovered, string stroke, str
[InlineData(OtpInputStyle.Outlined, 13, false, OtpInputState.Error, "#008000", "#FF0000", "#1C1B1F", "#611c1b1f")]
public void UpdateParameters(OtpInputStyle otpInputStyle, double cornerRadius, bool isEnabled, OtpInputState otpInputState, string stroke, string background, string textColor, string disabledTextColor)
{
- OTPEntry otpEntry = new OTPEntry();
+ OTPEntry otpEntry = new OTPEntry();
otpEntry.UpdateParameters(otpInputStyle, cornerRadius, new PointF(10, 10), new PointF(30, 30), new SfOtpInput(),isEnabled, otpInputState, Color.FromArgb(stroke), Color.FromArgb(background), Color.FromArgb(textColor), Color.FromArgb(disabledTextColor));
OtpInputStyle? resultInputStyle = (OtpInputStyle?)GetPrivateField(otpEntry, "_styleMode");
double? resultCornerRadius = (double?)GetPrivateField(otpEntry, "_cornerRadius");
@@ -292,9 +292,10 @@ public void InputBackground(Color input, Color expected)
Assert.Equal(expected, actual);
}
-
-
[Theory]
+ [InlineData(0)]
+ [InlineData(-5)]
+ [InlineData(-30)]
[InlineData(40)]
[InlineData(60)]
[InlineData(100)]
@@ -307,15 +308,24 @@ public void BoxWidth_UpdatesEntryDimensions(double newWidth)
{
foreach (var entry in _otpEntries)
{
- Assert.Equal(newWidth, entry.MinimumWidthRequest);
- Assert.Equal(newWidth, entry.WidthRequest);
+ if(newWidth > 0)
+ {
+ Assert.Equal(newWidth, entry.MinimumWidthRequest);
+ Assert.Equal(newWidth, entry.WidthRequest);
+ }
+ else
+ {
+ Assert.NotEqual(newWidth, entry.MinimumWidthRequest);
+ Assert.NotEqual(newWidth, entry.WidthRequest);
+ }
}
}
}
-
-
[Theory]
+ [InlineData(0)]
+ [InlineData(-5)]
+ [InlineData(-45)]
[InlineData(40)]
[InlineData(55)]
[InlineData(80)]
@@ -328,8 +338,16 @@ public void BoxHeight_UpdatesEntryDimensions(double newHeight)
{
foreach (var entry in _otpEntries)
{
- Assert.Equal(newHeight, entry.MinimumHeightRequest);
- Assert.Equal(newHeight, entry.HeightRequest);
+ if(newHeight > 0)
+ {
+ Assert.Equal(newHeight, entry.MinimumHeightRequest);
+ Assert.Equal(newHeight, entry.HeightRequest);
+ }
+ else
+ {
+ Assert.NotEqual(newHeight, entry.MinimumHeightRequest);
+ Assert.NotEqual(newHeight, entry.HeightRequest);
+ }
}
}
}
@@ -590,11 +608,11 @@ public void FocusAsync()
var otpInput = new SfOtpInput();
OTPEntry[]? otpEntries = (OTPEntry[]?)GetPrivateField(otpInput, "_otpEntries");
SetPrivateField(otpInput, "_otpEntries", otpEntries);
- var sender = otpEntries?[2];
+ var sender = otpEntries?[2];
var focusEventArgs = new FocusEventArgs(otpInput,true);
InvokePrivateMethod(otpInput, "FocusAsync", sender, focusEventArgs);
var focusedIndex = (int?)GetPrivateField(otpInput, "_focusedIndex");
- Assert.Equal(2, focusedIndex);
+ Assert.Equal(2, focusedIndex);
}
@@ -609,14 +627,14 @@ public void FocusOutAsync()
}
[Theory]
- [InlineData(0, true, 0)]
+ [InlineData(0, true, 0)]
[InlineData(1, false, 1)]
public void FocusEntry(int index, bool setCursorToStart, int expectedCursorPosition)
{
var otpInput = new SfOtpInput();
OTPEntry[]? otpEntries = (OTPEntry[]?)GetPrivateField(otpInput, "_otpEntries");
SetPrivateField(otpInput, "_otpEntries", otpEntries);
- SetPrivateField(otpInput, "_focusedIndex", 0);
+ SetPrivateField(otpInput, "_focusedIndex", 0);
otpEntries?[0].Focus();
InvokePrivateMethod(otpInput, "FocusEntry", index, setCursorToStart);
int? resultFocusedIndex = (int?)GetPrivateField(otpInput, "_focusedIndex");
@@ -626,9 +644,9 @@ public void FocusEntry(int index, bool setCursorToStart, int expectedCursorPosit
}
[Theory]
- [InlineData(3, true, 3)]
+ [InlineData(3, true, 3)]
[InlineData(0, false, 0)]
- [InlineData(3, false, 3)]
+ [InlineData(3, false, 3)]
public void HandleFocus(int index, bool hasText, int expectedFocusIndex)
{
var otpInput = new SfOtpInput ();
@@ -638,26 +656,26 @@ public void HandleFocus(int index, bool hasText, int expectedFocusIndex)
SetPrivateField(otpInput, "_focusedIndex", index);
InvokePrivateMethod(otpInput,"HandleFocus", index, hasText);
focusedIndex = GetPrivateField(otpInput, "_focusedIndex");
- Assert.Equal(expectedFocusIndex, focusedIndex);
+ Assert.Equal(expectedFocusIndex, focusedIndex);
}
[Theory]
- [InlineData(0, 1)]
+ [InlineData(0, 1)]
[InlineData(1, 1)]
- [InlineData(2,1)]
- [InlineData(3, 1)]
+ [InlineData(2,1)]
+ [InlineData(3, 1)]
public void GetStrokeThickness(int index, float expectedStrokeThickness)
{
var otpInput = new SfOtpInput();
OTPEntry[]? otpEntries = (OTPEntry[]?)GetPrivateField(otpInput, "_otpEntries");
SetPrivateField(otpInput,"_otpEntries", otpEntries);
float? strokeThickness =(float?) InvokePrivateMethod(otpInput, "GetStrokeThickness", index);
- Assert.Equal(expectedStrokeThickness, strokeThickness);
+ Assert.Equal(expectedStrokeThickness, strokeThickness);
}
[Theory]
- [InlineData(0, 30f, 40f, 5f, 3f, false, 300f)]
+ [InlineData(0, 30f, 40f, 5f, 3f, false, 300f)]
[InlineData(1, 35f, 45f, 10f, 5f, false, 300f)]
public void UpdateDrawingParameters(int index, float entryWidth, float entryHeight, float spacing, float extraSpacing, bool isRTL, float controlWidth)
{
@@ -708,14 +726,14 @@ public void UpdateValue()
var otpInput = new SfOtpInput();
OTPEntry[]? _otpEntries = (OTPEntry[]?)GetPrivateField(otpInput, "_otpEntries");
- string invalidInput = "12A4";
- InvokePrivateMethod(otpInput, "UpdateValue", otpInput, invalidInput);
+ string invalidInput = "12A4";
+ InvokePrivateMethod(otpInput, "UpdateValue", otpInput, invalidInput);
Assert.Equal("1", _otpEntries?[0].Text);
Assert.Equal("2", _otpEntries?[1].Text);
- Assert.Equal("", _otpEntries?[2].Text);
+ Assert.Equal("", _otpEntries?[2].Text);
Assert.Equal("4", _otpEntries?[3].Text);
- otpInput.Type = OtpInputType.Password;
+ otpInput.Type = OtpInputType.Password;
string passwordInput = "1234";
InvokePrivateMethod(otpInput, "UpdateValue", otpInput, passwordInput);
if (_otpEntries != null)
@@ -737,7 +755,7 @@ public void UpdateEntriesLength(int initialLength, int newLength)
InvokePrivateMethod(otpInput, "UpdateEntriesLength", initialLength, newLength);
OTPEntry[]? _otpEntries = (OTPEntry[]?)GetPrivateField(otpInput, "_otpEntries");
SetPrivateField(otpInput, "_otpEntries", _otpEntries);
- Assert.Equal(newLength, _otpEntries?.Length);
+ Assert.Equal(newLength, _otpEntries?.Length);
}
[Theory]
@@ -794,14 +812,14 @@ public void AddEntry(int initialLength,int newLength)
int expectedSeparatorCount = newLength - 1;
int actualSeparatorCount = layout.Children.OfType().Count();
Assert.Equal(expectedSeparatorCount, actualSeparatorCount);
- int expectedTotalChildren = newLength + expectedSeparatorCount;
+ int expectedTotalChildren = newLength + expectedSeparatorCount;
Assert.Equal(expectedTotalChildren, layout.Children.Count);
}
[Theory]
- [InlineData(0, 50f, 30f, 5f, 5f)]
- [InlineData(0, 60f, 40f, 10f, 10f)]
+ [InlineData(0, 50f, 30f, 5f, 5f)]
+ [InlineData(0, 60f, 40f, 10f, 10f)]
public void SetInputFieldPosition(int index, float entryWidth, float entryHeight, float spacing, float extraSpacing)
{
@@ -818,22 +836,22 @@ public void SetInputFieldPosition(int index, float entryWidth, float entryHeight
InvokePrivateMethod(otpInput, "SetInputFieldPosition", index, otpEntry);
RectF[]? entryBounds = (RectF[]?)GetPrivateField(otpInput, "_entryBounds");
- Assert.Equal(index + 1, entryBounds?.Length);
+ Assert.Equal(index + 1, entryBounds?.Length);
var layoutBounds = AbsoluteLayout.GetLayoutBounds(otpEntry);
float expectedX = (entryWidth + spacing) * index + extraSpacing;
float expectedY = extraSpacing;
- Assert.Equal(expectedX, layoutBounds.X);
+ Assert.Equal(expectedX, layoutBounds.X);
Assert.Equal(expectedY, layoutBounds.Y);
Assert.Equal(entryWidth, layoutBounds.Width);
- Assert.Equal(entryHeight, layoutBounds.Height);
+ Assert.Equal(entryHeight, layoutBounds.Height);
}
[Theory]
- [InlineData(0, 50f, 30f, 5f, 5f)]
- [InlineData(1, 60f, 40f, 10f, 10f)]
+ [InlineData(0, 50f, 30f, 5f, 5f)]
+ [InlineData(1, 60f, 40f, 10f, 10f)]
public void SetSeparatorPosition(int index, float entryWidth, float entryHeight, float spacing, float separatorWidth)
{
var otpInput = new SfOtpInput();
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Layout/SfTextInputLayoutUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Layout/SfTextInputLayoutUnitTests.cs
index eadacb7e..501fd802 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Layout/SfTextInputLayoutUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Layout/SfTextInputLayoutUnitTests.cs
@@ -715,12 +715,12 @@ public void TestUpdateIconRectFMethod()
TrailingViewPosition = ViewPosition.Outside
};
InvokePrivateMethod(inputLayout, "UpdateIconRectF");
- var outRectExpected = new RectF() { X = 21, Y = 2, Width = 1, Height = -24 };
+ var outRectExpected = new RectF() { X = 21, Y = 2, Width = -43, Height = -24 };
var resultOutRect = GetPrivateField(inputLayout, "_outlineRectF");
- Assert.Equal(outRectExpected, resultOutRect);
- var backgroundRectExpected = new RectF() { X = 19, Y = 0, Width = 1, Height = -22 };
+ Assert.Equal(resultOutRect, outRectExpected);
+ var backgroundRectExpected = new RectF() { X = 19, Y = 0, Width = -39, Height = -22 };
var resultBackgroundRect = GetPrivateField(inputLayout, "_backgroundRectF");
- Assert.Equal(backgroundRectExpected, resultBackgroundRect);
+ Assert.Equal(resultBackgroundRect, backgroundRectExpected);
var _passwordToggleIconRectF = new RectF() { X = -83, Y = -27, Width = 32, Height = 32 };
var resultPassToggleRect = GetPrivateField(inputLayout, "_passwordToggleIconRectF");
Assert.Equal(resultPassToggleRect, _passwordToggleIconRectF);
@@ -816,7 +816,7 @@ public void TestUpdateHelperTextPositionMethod()
TrailingView = new Entry(),
TrailingViewPosition = ViewPosition.Outside
};
- var expected = new RectF() { X = 60, Y = -18, Width = 1, Height = 16 };
+ var expected = new RectF() { X = 60, Y = -18, Width = -96, Height = 16 };
InvokePrivateMethod(inputLayout, "UpdateHelperTextPosition");
var result = GetPrivateField(inputLayout, "_helperTextRect");
Assert.Equal(expected, result);
@@ -873,7 +873,7 @@ public void TestUpdateErrorTextPositionMethod()
{
var inputLayout = new SfTextInputLayout();
InvokePrivateMethod(inputLayout, "UpdateErrorTextPosition");
- RectF expectedRect = new RectF() { X = 16, Y = -18, Width = 1, Height = 16 };
+ RectF expectedRect = new RectF() { X = 16, Y = -18, Width = -33, Height = 16 };
var result = GetPrivateField(inputLayout, "_errorTextRect");
Assert.Equal(expectedRect, result);
}
@@ -1191,72 +1191,6 @@ public void Content_TimePicker(ContainerType container)
Assert.Equal(new TimeSpan(10, 30, 0), timePicker.Time);
}
- [Fact]
- public void VerySmallContainerWidth_ShouldNotCrash()
- {
- // Test for the fix of issue where very small container widths cause Android crash
- // with Java.Lang.IllegalArgumentException: 'Layout: -46 < 0'
- var inputLayout = new SfTextInputLayout
- {
- Content = new Entry { Text = "Test" },
- Hint = "Name",
- WidthRequest = 10, // Very small width that could cause negative layout bounds
- HeightRequest = 50
- };
-
- // The control should handle very small dimensions gracefully without throwing exceptions
- Assert.NotNull(inputLayout);
- Assert.NotNull(inputLayout.Content);
- Assert.Equal("Test", ((Entry)inputLayout.Content).Text);
- Assert.Equal("Name", inputLayout.Hint);
- }
-
- [Fact]
- public void VerySmallContainerWidthWithLeadingAndTrailingViews_ShouldNotCrash()
- {
- // Test for the fix where very small container widths with leading/trailing views cause crashes
- var inputLayout = new SfTextInputLayout
- {
- Content = new Entry { Text = "Test" },
- Hint = "Name",
- LeadingView = new Label { Text = "L" },
- TrailingView = new Label { Text = "T" },
- ShowLeadingView = true,
- ShowTrailingView = true,
- WidthRequest = 10, // Very small width that could cause negative layout bounds
- HeightRequest = 50
- };
-
- // The control should handle very small dimensions gracefully without throwing exceptions
- Assert.NotNull(inputLayout);
- Assert.NotNull(inputLayout.Content);
- Assert.NotNull(inputLayout.LeadingView);
- Assert.NotNull(inputLayout.TrailingView);
- Assert.True(inputLayout.ShowLeadingView);
- Assert.True(inputLayout.ShowTrailingView);
- }
-
- [Fact]
- public void VerySmallContainerWidthWithErrorAndHelperText_ShouldNotCrash()
- {
- // Test for the fix where very small container widths with error/helper text cause crashes
- var inputLayout = new SfTextInputLayout
- {
- Content = new Entry { Text = "Test" },
- Hint = "Name",
- ErrorText = "This is an error message",
- HelperText = "This is a helper message",
- WidthRequest = 10, // Very small width that could cause negative text layout bounds
- HeightRequest = 50
- };
-
- // The control should handle very small dimensions gracefully without throwing exceptions
- Assert.NotNull(inputLayout);
- Assert.NotNull(inputLayout.Content);
- Assert.Equal("This is an error message", inputLayout.ErrorText);
- Assert.Equal("This is a helper message", inputLayout.HelperText);
- }
-
#endregion
}
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Miscellaneous/SfEffectsViewUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Miscellaneous/SfEffectsViewUnitTests.cs
index 365bce31..fc361fd1 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Miscellaneous/SfEffectsViewUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Miscellaneous/SfEffectsViewUnitTests.cs
@@ -1,9 +1,12 @@
-using Syncfusion.Maui.Toolkit.EffectsView;
+using Microsoft.Maui.Layouts;
+using Syncfusion.Maui.Toolkit.EffectsView;
+using Syncfusion.Maui.Toolkit.Internals;
namespace Syncfusion.Maui.Toolkit.UnitTest
{
public class SfEffectsViewUnitTests : BaseUnitTest
{
+
#region Contructor
[Fact]
public void Constructor_InitializesDefaultsCorrectly()
@@ -38,6 +41,83 @@ public void Constructor_InitializesDefaultsCorrectly()
#endregion
#region Public Properties
+ [Theory]
+ [InlineData(1.0)]
+ [InlineData(0.0)]
+ [InlineData(-2.0)]
+ public void TestScale(double scaleValue)
+ {
+ var effectsView = new SfEffectsView();
+ effectsView.Scale = scaleValue;
+ Assert.Equal(scaleValue, effectsView.Scale);
+ }
+
+ [Theory]
+ [InlineData(1.0)]
+ [InlineData(0.0)]
+ [InlineData(-1.0)]
+ public void TestOpacity(double opacityValue)
+ {
+ var effectsView = new SfEffectsView();
+ effectsView.Opacity = opacityValue;
+ if (opacityValue < 0)
+ {
+ Assert.Equal(0.0, effectsView.Opacity);
+ }
+ else
+ {
+ Assert.Equal(opacityValue, effectsView.Opacity);
+ }
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void TestIsEnabled(bool isEnabled)
+ {
+ var effectsView = new SfEffectsView();
+ effectsView.IsEnabled = isEnabled;
+ Assert.Equal(isEnabled, effectsView.IsEnabled);
+ }
+
+ [Theory]
+ [InlineData(200.0)]
+ [InlineData(0.0)]
+ [InlineData(-300.0)]
+ public void TestTranslationX(double translation)
+ {
+ var effectsView = new SfEffectsView();
+ effectsView.TranslationX = translation;
+ Assert.Equal(translation, effectsView.TranslationX);
+ }
+
+ [Theory]
+ [InlineData(200.0)]
+ [InlineData(0.0)]
+ [InlineData(-300.0)]
+ public void TestTranslationY(double translation)
+ {
+ var effectsView = new SfEffectsView();
+ effectsView.TranslationX = translation;
+ Assert.Equal(0.0, effectsView.TranslationY);
+ }
+
+ [Theory]
+ [InlineData("Red")]
+ [InlineData("Green")]
+ public void TestEffectColor(string colorName)
+ {
+ var effectsView = new SfEffectsView();
+ var expected = colorName switch
+ {
+ "Red" => Colors.Red,
+ "Green" => Colors.Green,
+ _ => throw new ArgumentOutOfRangeException()
+ };
+ effectsView.BackgroundColor = expected;
+ Assert.Equal(expected, effectsView.BackgroundColor);
+ }
+
[Theory]
[InlineData(120)]
[InlineData(-180)]
@@ -246,33 +326,23 @@ public void TouchUpEffects_SetValue_ReturnsExpectedValue(SfEffects touchUpEffect
}
[Fact]
- public void TouchUpCommandParameter_SetValue_ReturnsExpectedValue()
- {
- var effectsView = new SfEffectsView();
- var touchUpCommandParameter = new object();
- effectsView.TouchUpCommandParameter = touchUpCommandParameter;
-
- Assert.Equal(touchUpCommandParameter, effectsView.TouchUpCommandParameter);
- }
-
- [Fact]
- public void LongPressedCommandParameter_SetValue_ReturnsExpectedValue()
+ public void TestState_Initialization()
{
var effectsView = new SfEffectsView();
- var longPressedCommandParameter = new object();
- effectsView.LongPressedCommandParameter = longPressedCommandParameter;
-
- Assert.Equal(longPressedCommandParameter, effectsView.LongPressedCommandParameter);
+ Assert.Equal(SfEffects.None, effectsView.TouchUpEffects);
+ Assert.Equal(AutoResetEffects.None, effectsView.AutoResetEffects);
+ Assert.Equal(1.0, effectsView.Scale);
}
[Fact]
- public void TouchDownCommandParameter_SetValue_ReturnsExpectedValue()
+ public void TestUpdateSelectionBackground()
{
- var effectsView = new SfEffectsView();
- var touchDownCommandParameter = new object();
- effectsView.TouchDownCommandParameter = touchDownCommandParameter;
-
- Assert.Equal(touchDownCommandParameter, effectsView.TouchDownCommandParameter);
+ var view = new SfEffectsView();
+ view.SelectionBackground = new SolidColorBrush(Colors.Red);
+ var brush = new SolidColorBrush(Colors.Red);
+ view.IsSelected = true;
+ InvokePrivateMethod(view, "UpdateSelectionBackground", brush);
+ Assert.Equal(brush.Color, view.SelectionBackground);
}
#endregion
@@ -333,10 +403,84 @@ public void TouchDownCommand_SetBinding_ExecutesCommandAndReturnsExpectedValue()
};
effectsView.SetBinding(SfEffectsView.TouchDownCommandProperty, new Binding("TouchDownCommand"));
effectsView.TouchDownCommand?.Execute(null);
-
Assert.True(commandExecuted);
Assert.Same(mockCommand, effectsView.TouchDownCommand);
}
+
+ [Fact]
+ public void TouchUpCommandParameter_SetValue_ReturnsExpectedValue()
+ {
+ var effectsView = new SfEffectsView();
+ var touchUpCommandParameter = new object();
+ effectsView.TouchUpCommandParameter = touchUpCommandParameter;
+ Assert.Equal(touchUpCommandParameter, effectsView.TouchUpCommandParameter);
+ }
+
+ [Fact]
+ public void LongPressedCommandParameter_SetValue_ReturnsExpectedValue()
+ {
+ var effectsView = new SfEffectsView();
+ var longPressedCommandParameter = new object();
+ effectsView.LongPressedCommandParameter = longPressedCommandParameter;
+ Assert.Equal(longPressedCommandParameter, effectsView.LongPressedCommandParameter);
+ }
+
+ [Fact]
+ public void TouchDownCommandParameter_SetValue_ReturnsExpectedValue()
+ {
+ var effectsView = new SfEffectsView();
+ var touchDownCommandParameter = new object();
+ effectsView.TouchDownCommandParameter = touchDownCommandParameter;
+ Assert.Equal(touchDownCommandParameter, effectsView.TouchDownCommandParameter);
+ }
+
+ [Fact]
+ public void TestLongPressHandled_ShouldSetAndGetProperly()
+ {
+ var effectsView = new SfEffectsView();
+ effectsView.LongPressHandled = true;
+ Assert.True(effectsView.LongPressHandled);
+ }
+
+ [Fact]
+ public void TestInvokeTouchUpEventAndCommand()
+ {
+ var view = new SfEffectsView();
+ bool eventTriggered = false;
+ view.TouchUp += (sender, args) => eventTriggered = true;
+ view.InvokeTouchUpEventAndCommand();
+ Assert.True(eventTriggered);
+ }
+
+ [Fact]
+ public void TestInvokeTouchDownEventAndCommand()
+ {
+ var view = new SfEffectsView();
+ bool eventTriggered = false;
+ view.TouchDown += (sender, args) => eventTriggered = true;
+ view.InvokeTouchDownEventAndCommand();
+ Assert.True(eventTriggered);
+ }
+
+ [Fact]
+ public void TestInvokeLongPressEventAndCommand()
+ {
+ var view = new SfEffectsView();
+ bool eventTriggered = false;
+ view.LongPressed += (sender, args) => eventTriggered = true;
+ view.InvokeLongPressedEventAndCommand();
+ Assert.True(eventTriggered);
+ }
+
+ [Fact]
+ public void TestInvokeTouchUpEventAndCommand_ShouldExecuteCommand()
+ {
+ var view = new SfEffectsView();
+ var commandExecuted = false;
+ view.TouchUpCommand = new Command(() => commandExecuted = true);
+ view.InvokeTouchUpEventAndCommand();
+ Assert.True(commandExecuted);
+ }
#endregion
#region Internal Properties
@@ -367,5 +511,314 @@ public void LongPressHandled_Setvalue_ReturnsExpectedValue(bool longPressHandled
}
#endregion
+
+ #region Events
+
+ [Fact]
+ public void TestAnimationCompleted_Event()
+ {
+ var sfEffectsView = new SfEffectsView();
+ bool eventTriggered = false;
+ sfEffectsView.AnimationCompleted += (sender, args) => eventTriggered = true;
+ sfEffectsView.RaiseAnimationCompletedEvent(EventArgs.Empty);
+ Assert.True(eventTriggered, "AnimationCompleted event was not triggered correctly.");
+ }
+
+ [Fact]
+ public void TestTouchEventsSequence()
+ {
+ var effectsView = new SfEffectsView();
+ bool touchDownFired = false, touchUpFired = false;
+
+ effectsView.TouchDown += (s, e) => touchDownFired = true;
+ effectsView.TouchUp += (s, e) => touchUpFired = true;
+
+ effectsView.InvokeTouchDownEventAndCommand();
+ effectsView.InvokeTouchUpEventAndCommand();
+
+ Assert.True(touchDownFired && touchUpFired, "Both touch events should fire in sequence.");
+ }
+
+ #endregion
+
+ #region Test Layouts
+
+ [Fact]
+ public void Test_InStackLayout()
+ {
+ var stackLayout = new StackLayout();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in StackLayout" } };
+ stackLayout.Children.Add(effectsView);
+ Assert.Contains(effectsView, stackLayout.Children);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void Test_InGrid()
+ {
+ var grid = new Grid();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in Grid" } };
+ grid.Children.Add(effectsView);
+ Assert.Contains(effectsView, grid.Children);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void Test_InAbsoluteLayout_()
+ {
+ var absoluteLayout = new AbsoluteLayout();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in AbsoluteLayout" } };
+ AbsoluteLayout.SetLayoutBounds(effectsView, new Rect(0, 0, 100, 50));
+ AbsoluteLayout.SetLayoutFlags(effectsView, AbsoluteLayoutFlags.None);
+ absoluteLayout.Children.Add(effectsView);
+ Assert.Contains(effectsView, absoluteLayout.Children);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void Test_InFlexLayout()
+ {
+ var flexLayout = new FlexLayout();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in FlexLayout" } };
+ flexLayout.Children.Add(effectsView);
+ Assert.Contains(effectsView, flexLayout.Children);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void Test_InHorizontalStackLayout()
+ {
+ var horizontalStackLayout = new HorizontalStackLayout();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in HorizontalStackLayout" } };
+ horizontalStackLayout.Children.Add(effectsView);
+ Assert.Contains(effectsView, horizontalStackLayout.Children);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void Test_InVerticalStackLayout()
+ {
+ var verticalStackLayout = new VerticalStackLayout();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in VerticalStackLayout" } };
+ verticalStackLayout.Children.Add(effectsView);
+ Assert.Contains(effectsView, verticalStackLayout.Children);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void Test_InScrollView()
+ {
+ var scrollView = new ScrollView();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in ScrollView" } };
+ scrollView.Content = effectsView;
+ Assert.Equal(effectsView, scrollView.Content);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void Test_InContentView()
+ {
+ var contentView = new ContentView();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in ContentView" } };
+ contentView.Content = effectsView;
+ Assert.Equal(effectsView, contentView.Content);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void Test_InCustomLayout()
+ {
+ var customLayout = new AbsoluteLayout();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in Custom Layout" } };
+ AbsoluteLayout.SetLayoutBounds(effectsView, new Rect(10, 20, 150, 50));
+ AbsoluteLayout.SetLayoutFlags(effectsView, AbsoluteLayoutFlags.PositionProportional);
+ customLayout.Children.Add(effectsView);
+ Assert.Contains(effectsView, customLayout.Children);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void TestMultipleVisualStateGroups()
+ {
+ var effectsView = new SfEffectsView();
+ var group1 = new VisualStateGroup { Name = "Group1", States = { new VisualState { Name = "State1" } } };
+ var group2 = new VisualStateGroup { Name = "Group2", States = { new VisualState { Name = "State2" } } };
+ VisualStateManager.SetVisualStateGroups(effectsView, new VisualStateGroupList { group1, group2 });
+ Assert.Equal(2, VisualStateManager.GetVisualStateGroups(effectsView).Count);
+ }
+
+ [Fact]
+ public void TestAddVSMStates()
+ {
+ var effectsView = new SfEffectsView();
+ var group = new VisualStateGroup
+ {
+ Name = "CommonStates",
+ States =
+ {
+ new VisualState { Name = "Normal" },
+ new VisualState { Name = "Disabled" }
+ }
+ };
+
+ VisualStateManager.SetVisualStateGroups(effectsView, new VisualStateGroupList { group });
+ var visualStateGroups = VisualStateManager.GetVisualStateGroups(effectsView);
+ Assert.Single(visualStateGroups);
+ Assert.Equal(2, visualStateGroups[0].States.Count);
+ }
+
+ [Fact]
+ public void TestIsEnabledChange()
+ {
+ var effectsView = new SfEffectsView();
+ var group = new VisualStateGroup
+ {
+ Name = "CommonStates",
+ States =
+ {
+ new VisualState { Name = "Enabled" },
+ new VisualState { Name = "Disabled" }
+ }
+ };
+
+ VisualStateManager.SetVisualStateGroups(effectsView, new VisualStateGroupList { group });
+ effectsView.IsEnabled = false;
+ VisualStateManager.GoToState(effectsView, "Disabled");
+ var currentState = VisualStateManager.GetVisualStateGroups(effectsView)[0].CurrentState;
+ Assert.Equal("Disabled", currentState.Name);
+ }
+
+ [Fact]
+ public void TestRTLDirection()
+ {
+ var effectsView = new SfEffectsView { FlowDirection = FlowDirection.RightToLeft };
+ Assert.Equal(FlowDirection.RightToLeft, effectsView.FlowDirection);
+ }
+
+ [Fact]
+ public void Test_DefaultVSMStates()
+ {
+ var effectsView = new SfEffectsView();
+ var visualStateGroups = VisualStateManager.GetVisualStateGroups(effectsView);
+ Assert.Empty(visualStateGroups);
+ }
+
+ [Fact]
+ public void Test_InBorder()
+ {
+ var border = new Border();
+ var effectsView = new SfEffectsView { Content = new Label { Text = "Test in Frame" } };
+ border.Content = effectsView;
+ Assert.Equal(effectsView, border.Content);
+ Assert.IsType(effectsView.Parent);
+ }
+
+ [Fact]
+ public void TestRTLContent()
+ {
+ var label = new Label { Text = "Test Content", FlowDirection = FlowDirection.RightToLeft };
+ var effectsView = new SfEffectsView { Content = label };
+ Assert.Equal(FlowDirection.RightToLeft, ((Label)effectsView.Content).FlowDirection);
+ }
+
+ #endregion
+
+ #region Methods
+
+ [Fact]
+ public void TestRapid_Reset_Calls()
+ {
+ var effectsView = new SfEffectsView();
+
+ for (int i = 0; i < 1000; i++)
+ {
+ effectsView.Reset();
+ }
+
+ Assert.True(true);
+ }
+
+ [Theory]
+ [InlineData(PointerActions.Released)]
+ [InlineData(PointerActions.Moved)]
+ [InlineData(PointerActions.Exited)]
+ [InlineData(PointerActions.Cancelled)]
+ public void Test_EffectsView_OnTouch(PointerActions action)
+ {
+ var effectsView = new SfEffectsView();
+ effectsView.AutoResetEffects = AutoResetEffects.None;
+ var eventArgs = new Syncfusion.Maui.Toolkit.Internals.PointerEventArgs(1, action, new Point(30, 30));
+ var exception = Record.Exception(() => ((ITouchListener)effectsView).OnTouch(eventArgs));
+ Assert.Null(exception);
+ }
+
+ [Fact]
+ public void Test_ArrangeContent()
+ {
+ var effectsView = new SfEffectsView();
+ InvokePrivateMethod(effectsView, "AddResetEffects", AutoResetEffects.None, new Point(30, 30));
+ Assert.Equal(AutoResetEffects.None, effectsView.AutoResetEffects);
+ }
+
+ [Fact]
+ public void Test_ApplyEffects()
+ {
+ var effectsView = new SfEffectsView
+ {
+ IsSelected = true,
+ Content = new Label { Text = "Test", WidthRequest = 200, HeightRequest = 200 }
+ };
+ var result = InvokePrivateMethod(effectsView, "ArrangeContent", new Rect(10, 10, 100, 100));
+ Assert.NotNull(result);
+ var contentSize = (Size)result;
+ Assert.Equal(100, contentSize.Width);
+ }
+
+ [Fact]
+ public void Test_OnAnimationUpdate()
+ {
+ var effectsView = new SfEffectsView
+ {
+ Content = new Label { Text = "Test", WidthRequest = 200, HeightRequest = 200 }
+ };
+ InvokePrivateMethod(effectsView, "OnAnimationUpdate", 30);
+ InvokePrivateMethod(effectsView, "OnScaleAnimationUpdate", 50);
+ Assert.Equal(30, effectsView.Content.Rotation);
+ Assert.Equal(50, effectsView.Content.Scale);
+ }
+
+ [Fact]
+ public void Test_UpdateToGradient()
+ {
+ var effectsView = new SfEffectsView();
+ var rippleEffectLayer = GetPrivateField(effectsView, "_rippleEffectLayer") as RippleEffectLayer;
+ var radialGradientBrush = (RadialGradientBrush?)InvokePrivateMethod(rippleEffectLayer, "UpdateToGradient", new SolidColorBrush(Color.FromArgb("#1C1B1F")));
+ if(radialGradientBrush!=null)
+ {
+ Assert.Equal(0.5, radialGradientBrush.Center.X);
+ }
+ }
+
+ [Fact]
+ public void Test_OnFadeAnimationUpdate()
+ {
+ var effectsView = new SfEffectsView();
+ var rippleEffectLayer = GetPrivateField(effectsView, "_rippleEffectLayer") as RippleEffectLayer;
+ InvokePrivateMethod(rippleEffectLayer, "OnFadeAnimationUpdate", 30);
+ var alphaValue = GetPrivateField(rippleEffectLayer, "_alphaValue");
+ Assert.Equal((float)30, alphaValue);
+ }
+
+ [Fact]
+ public void Test_OnRippleAnimationUpdate()
+ {
+ var effectsView = new SfEffectsView();
+ var rippleEffectLayer = GetPrivateField(effectsView, "_rippleEffectLayer") as RippleEffectLayer;
+ InvokePrivateMethod(rippleEffectLayer, "OnRippleAnimationUpdate", 20);
+ var rippleDiameter = GetPrivateField(rippleEffectLayer, "_rippleDiameter");
+ Assert.Equal((float)20, rippleDiameter);
+ }
+
+ #endregion
}
}
\ No newline at end of file
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Navigation/SfNavigationDrawerUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Navigation/SfNavigationDrawerUnitTests.cs
index 4bf7ee37..2cd9bd02 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Navigation/SfNavigationDrawerUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Navigation/SfNavigationDrawerUnitTests.cs
@@ -1,4 +1,6 @@
+using Microsoft.Maui.Controls;
using Syncfusion.Maui.Toolkit.Helper;
+using Syncfusion.Maui.Toolkit.Internals;
using Syncfusion.Maui.Toolkit.NavigationDrawer;
using System.Reflection;
@@ -12,7 +14,6 @@ public class SfNavigationDrawerUnitTests : BaseUnitTest
public void Constructor_InitializesDefaultsCorrectly()
{
SfNavigationDrawer navigationDrawer = [];
-
Assert.False(navigationDrawer.IsOpen);
Assert.Null(navigationDrawer.ContentView);
Assert.NotNull(navigationDrawer.DrawerSettings);
@@ -790,12 +791,15 @@ public void TestValidateRemainDrawerHeightNegative()
Assert.Equal(-20, expectedValue);
}
- [Fact]
- public void TestValidateCurrentDuration()
+ [Theory]
+ [InlineData(-20d)]
+ [InlineData(0d)]
+ [InlineData(500d)]
+ public void TestValidateCurrentDuration(double expectedValue)
{
SfNavigationDrawer navigationDrawer = [];
- double expectedValue = 20;
double actualValue = Convert.ToDouble(InvokePrivateStaticMethod(navigationDrawer, "ValidateCurrentDuration", expectedValue));
+ expectedValue = expectedValue <= 0 ? 1 : expectedValue;
Assert.Equal(expectedValue, actualValue);
}
@@ -1557,6 +1561,467 @@ public void TestIsTouchOutsideDrawerBoundsOnBottom()
Assert.True(actualValue);
}
+ [Fact]
+ public void TestUpdateGridOverlayTranslate()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ navigationDrawer.DrawerSettings.Transition = Transition.Reveal;
+ var greyOverlayGrid = new SfGrid();
+ SetPrivateField(navigationDrawer, "_greyOverlayGrid", greyOverlayGrid);
+ InvokePrivateMethod(navigationDrawer, "UpdateGridOverlayTranslate");
+ var actualGreyOverlay = GetPrivateField(navigationDrawer, "_greyOverlayGrid") as Grid;
+ Assert.NotNull(actualGreyOverlay);
+ Assert.Equal(0, actualGreyOverlay.TranslationX);
+ }
+
+ [Fact]
+ public void TestHandleLeftSwipeIn()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer();
+ navigationDrawer.DrawerSettings = new DrawerSettings { Transition = Transition.Reveal, DrawerWidth = 200 };
+ var greyOverlayGrid = new SfGrid();
+ var drawerLayout = new SfGrid();
+ var mainContentGrid = new SfGrid() { TranslationX = navigationDrawer.DrawerSettings.DrawerWidth };
+ SetPrivateField(navigationDrawer, "_mainContentGrid", mainContentGrid);
+ SetPrivateField(navigationDrawer, "_greyOverlayGrid", greyOverlayGrid);
+ SetPrivateField(navigationDrawer, "_drawerLayout", drawerLayout);
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", false);
+ InvokePrivateMethod(navigationDrawer, "HandleLeftSwipeIn");
+ bool isDrawerOpen;
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.True(isDrawerOpen);
+ navigationDrawer.DrawerSettings.Transition = Transition.Push;
+ var actualDrawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as Grid;
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", false);
+ Assert.NotNull(actualDrawerLayout);
+ actualDrawerLayout.TranslationX = 0;
+ InvokePrivateMethod(navigationDrawer, "HandleLeftSwipeIn");
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.True(isDrawerOpen);
+ }
+
+ [Fact]
+ public void TestHandleLeftSwipeOut()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer();
+ navigationDrawer.DrawerSettings = new DrawerSettings { Transition = Transition.Reveal, DrawerWidth = 200 };
+ var greyOverlayGrid = new SfGrid();
+ var drawerLayout = new SfGrid();
+ var mainContentGrid = new SfGrid();
+ mainContentGrid.TranslationX = 0;
+ SetPrivateField(navigationDrawer, "_mainContentGrid", mainContentGrid);
+ SetPrivateField(navigationDrawer, "_greyOverlayGrid", greyOverlayGrid);
+ SetPrivateField(navigationDrawer, "_drawerLayout", drawerLayout);
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", true);
+ SetPrivateField(navigationDrawer, "_isTransitionDifference", true);
+ InvokePrivateMethod(navigationDrawer, "HandleLeftSwipeOut");
+ bool isDrawerOpen;
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.False(isDrawerOpen);
+ navigationDrawer.DrawerSettings.Transition = Transition.Push;
+ var actualDrawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as Grid;
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", true);
+ SetPrivateField(navigationDrawer, "_isTransitionDifference", true);
+ Assert.NotNull(actualDrawerLayout);
+ actualDrawerLayout.TranslationX = -navigationDrawer.DrawerSettings.DrawerWidth;
+ InvokePrivateMethod(navigationDrawer, "HandleLeftSwipeOut");
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.False(isDrawerOpen);
+ }
+
+ [Fact]
+ public void TestHandleLeftSwipeByPosition()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ var greyOverlayGrid = new SfGrid();
+ var drawerLayout = new SfGrid();
+ bool isDrawerOpen;
+ SetPrivateField(navigationDrawer, "_greyOverlayGrid", greyOverlayGrid);
+ SetPrivateField(navigationDrawer, "_drawerLayout", drawerLayout);
+ navigationDrawer.DrawerSettings.Transition = Transition.Push;
+ var actualDrawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as Grid;
+ Assert.NotNull(actualDrawerLayout);
+ actualDrawerLayout.TranslationX = 0;
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", false);
+ InvokePrivateMethod(navigationDrawer, "HandleLeftSwipeByPosition");
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.True(isDrawerOpen);
+ navigationDrawer.DrawerSettings.Transition = Transition.Reveal;
+ navigationDrawer.ContentView.TranslationX = navigationDrawer.DrawerSettings.DrawerWidth;
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", false);
+ InvokePrivateMethod(navigationDrawer, "HandleLeftSwipeByPosition");
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.True(isDrawerOpen);
+ }
+
+ [Fact]
+ public void TestHandleRightSwipeIn()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer();
+ navigationDrawer.DrawerSettings = new DrawerSettings { Transition = Transition.Reveal, DrawerWidth = 200 };
+ var greyOverlayGrid = new SfGrid();
+ var drawerLayout = new SfGrid();
+ var mainGrid = new SfGrid { TranslationX = -navigationDrawer.DrawerSettings.DrawerWidth };
+ SetPrivateField(navigationDrawer, "_mainContentGrid", mainGrid);
+ SetPrivateField(navigationDrawer, "_greyOverlayGrid", greyOverlayGrid);
+ SetPrivateField(navigationDrawer, "_drawerLayout", drawerLayout);
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", false);
+ InvokePrivateMethod(navigationDrawer, "HandleRightSwipeIn");
+ bool isDrawerOpen;
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.True(isDrawerOpen);
+ navigationDrawer.DrawerSettings.Transition = Transition.Push;
+ var actualDrawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as Grid;
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", false);
+ Assert.NotNull(actualDrawerLayout);
+ actualDrawerLayout.TranslationX = (navigationDrawer.ScreenWidth - navigationDrawer.DrawerSettings.DrawerWidth);
+ InvokePrivateMethod(navigationDrawer, "HandleRightSwipeIn");
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.True(isDrawerOpen);
+
+ }
+
+ [Fact]
+ public void HandleRightSwipeOut_Reveal_Transition_MainAtZero_TogglesDrawerOut()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings
+ {
+ Transition = Transition.Reveal,
+ DrawerHeight = 300
+ };
+ nav.ScreenWidth = 1200;
+
+ var mainContentGrid = new SfGrid { TranslationX = 0 };
+ SetPrivateField(nav, "_mainContentGrid", mainContentGrid);
+ SetPrivateField(nav, "_drawerLayout", new SfGrid());
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid { TranslationX = 0 });
+
+ SetPrivateField(nav, "_isDrawerOpen", true);
+ SetPrivateField(nav, "_isTransitionDifference", true);
+
+ InvokePrivateMethod(nav, "HandleRightSwipeOut");
+
+ // Assert that the drawer has toggled (example: isDrawerOpen = false after toggling out)
+ Assert.False((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
+ [Fact]
+ public void HandleRightSwipeOut_NonReveal_DrawerAtScreenWidth_TogglesDrawerOut()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings
+ {
+ Transition = Transition.Push,
+ DrawerHeight = 300
+ };
+ nav.ScreenWidth = 1200;
+
+ var drawerLayout = new SfGrid { TranslationX = 1200 };
+ SetPrivateField(nav, "_mainContentGrid", new SfGrid());
+ SetPrivateField(nav, "_drawerLayout", drawerLayout);
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid { TranslationX = 0 });
+
+ SetPrivateField(nav, "_isDrawerOpen", true);
+ SetPrivateField(nav, "_isTransitionDifference", true);
+
+ InvokePrivateMethod(nav, "HandleRightSwipeOut");
+
+ Assert.False((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
+ [Fact]
+ public void HandleRightSwipeByPosition_PushTransition_AtRightEdge_TogglesDrawer()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings { Transition = Transition.Push, DrawerWidth = 200 };
+ nav.ScreenWidth = 1080;
+
+ SetPrivateField(nav, "_drawerLayout", new SfGrid { TranslationX = 1080 - 200 });
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid());
+
+ SetPrivateField(nav, "_remainDrawerWidth", -(200 / 2.0)); // = –100, meets first clause
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleRightSwipeByPosition");
+
+ Assert.True((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
+ [Fact]
+ public void HandleRightSwipeByPosition_RevealTransition_MainGridAtFullLeft_TogglesDrawer()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings { Transition = Transition.Reveal, DrawerWidth = 200 };
+ nav.ScreenWidth = 1080;
+
+ SetPrivateField(nav, "_mainContentGrid", new SfGrid { TranslationX = -200 });
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid { TranslationX = 100 });
+
+ SetPrivateField(nav, "_drawerLayout", new SfGrid());
+ SetPrivateField(nav, "_remainDrawerWidth", -150); // <= -100, meets second clause
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleRightSwipeByPosition");
+
+ Assert.True((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
+ [Fact]
+ public void HandleBottomSwipeIn_WhenRevealAndContentAtNegativeHeight_TogglesDrawer()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings
+ {
+ Transition = Transition.Reveal,
+ DrawerHeight = 300
+ };
+
+ var drawerLayout = new SfGrid();
+ var greyOverlay = new SfGrid();
+ var mainGrid = new SfGrid { TranslationY = -300 };
+
+ SetPrivateField(nav, "_drawerLayout", drawerLayout);
+ SetPrivateField(nav, "_greyOverlayGrid", greyOverlay);
+ SetPrivateField(nav, "_mainContentGrid", mainGrid);
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleBottomSwipeIn");
+
+ bool? isOpen = (bool?)GetPrivateField(nav, "_isDrawerOpen");
+ Assert.True(isOpen);
+ }
+
+ [Fact]
+ public void HandleBottomSwipeIn_WhenNonRevealAndLayoutAtEdge_TogglesDrawer()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings
+ {
+ Transition = Transition.Push,
+ DrawerHeight = 300
+ };
+
+ nav.ScreenHeight = 1200;
+ double expectedY = (1200 / 2.0) - (300 / 2.0);
+
+ var drawerLayout = new SfGrid { TranslationY = expectedY };
+ var greyOverlay = new SfGrid();
+ var mainGrid = new SfGrid();
+
+ SetPrivateField(nav, "_drawerLayout", drawerLayout);
+ SetPrivateField(nav, "_greyOverlayGrid", greyOverlay);
+ SetPrivateField(nav, "_mainContentGrid", mainGrid);
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleBottomSwipeIn");
+
+ bool? isOpen = (bool?)GetPrivateField(nav, "_isDrawerOpen");
+ Assert.True(isOpen);
+ }
+
+ [Fact]
+ public void HandleTopSwipeIn_Reveal_MainContentAtDrawerHeight_TogglesIn()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings
+ {
+ Transition = Transition.Reveal,
+ DrawerHeight = 300
+ };
+
+ var mainContent = new SfGrid { TranslationY = 300 };
+ SetPrivateField(nav, "_mainContentGrid", mainContent);
+ SetPrivateField(nav, "_drawerLayout", new SfGrid());
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid());
+
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleTopSwipeIn");
+
+ Assert.True((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
+ [Fact]
+ public void HandleTopSwipeIn_NonReveal_DrawerAtExpectedPosition_TogglesIn()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings
+ {
+ Transition = Transition.Push,
+ DrawerHeight = 300
+ };
+ nav.ScreenHeight = 1200;
+
+ double expectedTranslationY = -((1200 / 2.0) - (300 / 2.0)) - 0; // Assuming _drawerMoveTop = 0
+ var drawerLayout = new SfGrid { TranslationY = expectedTranslationY };
+
+ SetPrivateField(nav, "_mainContentGrid", new SfGrid());
+ SetPrivateField(nav, "_drawerLayout", drawerLayout);
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid());
+
+ SetPrivateField(nav, "_drawerMoveTop", 0.0);
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleTopSwipeIn");
+
+ Assert.True((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
+ [Fact]
+ public void TestHandleTopSwipeOut()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ var greyOverlayGrid = new SfGrid();
+ var drawerLayout = new SfGrid();
+ SetPrivateField(navigationDrawer, "_greyOverlayGrid", greyOverlayGrid);
+ SetPrivateField(navigationDrawer, "_drawerLayout", drawerLayout);
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", true);
+ SetPrivateField(navigationDrawer, "_isTransitionDifference", true);
+ navigationDrawer.DrawerSettings.Transition = Transition.Reveal;
+ navigationDrawer.ContentView.TranslationY = 0;
+ InvokePrivateMethod(navigationDrawer, "HandleTopSwipeOut");
+ bool isDrawerOpen;
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.False(isDrawerOpen);
+ navigationDrawer.DrawerSettings.Transition = Transition.Push;
+ var actualDrawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as Grid;
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", true);
+ SetPrivateField(navigationDrawer, "_isTransitionDifference", true);
+ Assert.NotNull(actualDrawerLayout);
+ actualDrawerLayout.TranslationY = -((navigationDrawer.ScreenHeight / 2) - (navigationDrawer.DrawerSettings.DrawerHeight / 2)) - Convert.ToDouble(GetPrivateField(navigationDrawer, "_drawerMoveTop"));
+ InvokePrivateMethod(navigationDrawer, "HandleTopSwipeOut");
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.False(isDrawerOpen);
+ }
+
+ [Fact]
+ public void HandleTopSwipeByPosition_NonReveal_TopEdge_TogglesDrawer()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings { Transition = Transition.Push, DrawerHeight = 300 };
+ nav.ScreenHeight = 1200;
+ SetPrivateField(nav, "_drawerMoveTop", 0.0);
+
+ var drawerLayout = new SfGrid
+ {
+ TranslationY = -((1200 / 2.0) - (300 / 2.0)) // -450
+ };
+ SetPrivateField(nav, "_drawerLayout", drawerLayout);
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid());
+ SetPrivateField(nav, "_mainContentGrid", new SfGrid());
+
+ SetPrivateField(nav, "_remainDrawerHeight", -(300 / 2.0)); // -150
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleTopSwipeByPosition");
+
+ Assert.True((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
+ [Fact]
+ public void HandleTopSwipeByPosition_Reveal_MainDownShift_TogglesDrawer()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings { Transition = Transition.Reveal, DrawerHeight = 300 };
+ nav.ScreenHeight = 1200;
+
+ SetPrivateField(nav, "_mainContentGrid", new SfGrid { TranslationY = 300 });
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid { TranslationX = 100 });
+ SetPrivateField(nav, "_drawerLayout", new SfGrid());
+
+ SetPrivateField(nav, "_remainDrawerHeight", 200); // >= 150
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleTopSwipeByPosition");
+
+ Assert.True((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
+ [Fact]
+ public void TestHandleBottomSwipeOut()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ var greyOverlayGrid = new SfGrid();
+ var drawerLayout = new SfGrid();
+ SetPrivateField(navigationDrawer, "_greyOverlayGrid", greyOverlayGrid);
+ SetPrivateField(navigationDrawer, "_drawerLayout", drawerLayout);
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", true);
+ SetPrivateField(navigationDrawer, "_isTransitionDifference", true);
+ navigationDrawer.DrawerSettings.Transition = Transition.Reveal;
+ navigationDrawer.ContentView.TranslationY = 0;
+ InvokePrivateMethod(navigationDrawer, "HandleBottomSwipeOut");
+ bool isDrawerOpen;
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.False(isDrawerOpen);
+ navigationDrawer.DrawerSettings.Transition = Transition.Push;
+ var actualDrawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as Grid;
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", true);
+ SetPrivateField(navigationDrawer, "_isTransitionDifference", true);
+ Assert.NotNull(actualDrawerLayout);
+ actualDrawerLayout.TranslationY = ((navigationDrawer.ScreenHeight / 2) + (navigationDrawer.DrawerSettings.DrawerHeight / 2)) - Convert.ToDouble(GetPrivateField(navigationDrawer, "_drawerMoveTop"));
+ InvokePrivateMethod(navigationDrawer, "HandleBottomSwipeOut");
+ isDrawerOpen = Convert.ToBoolean(GetPrivateField(navigationDrawer, "_isDrawerOpen"));
+ Assert.False(isDrawerOpen);
+ }
+
+ [Fact]
+ public void HandleBottomSwipeByPosition_NonReveal_AtBottomEdge_TogglesDrawer()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings
+ {
+ Transition = Transition.Push,
+ DrawerHeight = 300
+ };
+ nav.ScreenHeight = 1200;
+
+ double expectedY = (1200 / 2.0) - (300 / 2.0) - 300; // = 150 - 300 = -150
+ var drawerLayout = new SfGrid { TranslationY = expectedY };
+ SetPrivateField(nav, "_drawerLayout", drawerLayout);
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid());
+
+ SetPrivateField(nav, "_remainDrawerHeight", -(300 / 2.0)); // -150
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleBottomSwipeByPosition");
+
+ Assert.True((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
+ [Fact]
+ public void HandleBottomSwipeByPosition_Reveal_MainShiftedUp_TogglesDrawer()
+ {
+ var nav = new SfNavigationDrawer();
+ nav.DrawerSettings = new DrawerSettings
+ {
+ Transition = Transition.Reveal,
+ DrawerHeight = 300
+ };
+ nav.ScreenHeight = 1200;
+
+ SetPrivateField(nav, "_drawerLayout", new SfGrid());
+ SetPrivateField(nav, "_mainContentGrid", new SfGrid { TranslationY = -300 });
+ SetPrivateField(nav, "_greyOverlayGrid", new SfGrid { TranslationX = 20 }); // < ScreenWidth
+
+ SetPrivateField(nav, "_remainDrawerHeight", -200);
+ SetPrivateField(nav, "_isDrawerOpen", false);
+ SetPrivateField(nav, "_isTransitionDifference", false);
+
+ InvokePrivateMethod(nav, "HandleBottomSwipeByPosition");
+
+ Assert.True((bool?)GetPrivateField(nav, "_isDrawerOpen"));
+ }
+
protected object? InvokePrivateStaticMethod(T obj, string methodName, params object[] parameters)
{
var method = typeof(T).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
@@ -1570,6 +2035,133 @@ public void TestIsTouchOutsideDrawerBoundsOnBottom()
#endregion
+ #region Drawer Scripts
+
+ [Fact]
+ public void Test_ParentFlowDirection1()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ navigationDrawer.FlowDirection = FlowDirection.RightToLeft;
+ var drawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as SfGrid;
+ Assert.Equal(FlowDirection.RightToLeft, drawerLayout?.FlowDirection);
+ }
+
+ [Fact]
+ public void Test_ParentFlowDirection2()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ navigationDrawer.FlowDirection = FlowDirection.RightToLeft;
+ var drawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as SfGrid;
+ Assert.Equal(FlowDirection.RightToLeft, drawerLayout?.FlowDirection);
+ navigationDrawer.FlowDirection = FlowDirection.LeftToRight;
+ Assert.Equal(FlowDirection.LeftToRight, drawerLayout?.FlowDirection);
+ }
+
+ [Fact]
+ public void Test_ParentFlowDirection3()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid(), IsOpen = true };
+ InvokePrivateMethod(navigationDrawer, "UpdateToggleInEvent");
+ Assert.True(navigationDrawer.IsOpen);
+ InvokePrivateMethod(navigationDrawer, "UpdateToggleOutEvent");
+ Assert.False(navigationDrawer.IsOpen);
+ navigationDrawer.FlowDirection = FlowDirection.RightToLeft;
+ var drawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as SfGrid;
+ Assert.Equal(FlowDirection.RightToLeft, drawerLayout?.FlowDirection);
+ navigationDrawer.FlowDirection = FlowDirection.LeftToRight;
+ Assert.Equal(FlowDirection.LeftToRight, drawerLayout?.FlowDirection);
+ }
+
+ [Fact]
+ public void Test_ParentFlowDirection4()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid(), IsOpen = true };
+ InvokePrivateMethod(navigationDrawer, "UpdateToggleInEvent");
+ Assert.True(navigationDrawer.IsOpen);
+ InvokePrivateMethod(navigationDrawer, "UpdateToggleOutEvent");
+ Assert.False(navigationDrawer.IsOpen);
+ navigationDrawer.FlowDirection = FlowDirection.RightToLeft;
+ var drawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as SfGrid;
+ Assert.Equal(FlowDirection.RightToLeft, drawerLayout?.FlowDirection);
+ }
+
+ [Theory]
+ [InlineData(100)]
+ [InlineData(0)]
+ [InlineData(600)]
+ public void Test_DrawerWidth(double drawerWidth)
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ navigationDrawer.DrawerSettings.DrawerFooterView = new Label();
+ navigationDrawer.DrawerSettings.DrawerFooterHeight = 350;
+ navigationDrawer.DrawerSettings.DrawerWidth = drawerWidth;
+ var drawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as SfGrid;
+ if (drawerLayout != null)
+ {
+ foreach (var child in drawerLayout.Children)
+ {
+ var width = child.DesiredSize.Width;
+ }
+ }
+ Assert.Equal(drawerWidth, drawerLayout?.WidthRequest);
+ }
+
+ [Fact]
+ public void Test_Bug907855_1()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ navigationDrawer.DrawerSettings.DrawerFooterView = new Label();
+ navigationDrawer.DrawerSettings.DrawerFooterHeight = 350;
+ navigationDrawer.DrawerSettings.DrawerWidth = -1;
+ var drawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as SfGrid;
+ if (drawerLayout != null)
+ {
+ foreach (var child in drawerLayout.Children)
+ {
+ var width = child.DesiredSize.Width;
+ }
+ }
+ Assert.Equal(200, drawerLayout?.WidthRequest);
+ }
+
+ [Fact]
+ public void Test_Bug907855_10()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ navigationDrawer.DrawerSettings.DrawerFooterView = new Label();
+ navigationDrawer.DrawerSettings.DrawerFooterHeight = 350;
+ navigationDrawer.DrawerSettings.DrawerWidth = -10;
+ var drawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as SfGrid;
+ if (drawerLayout != null)
+ {
+ foreach (var child in drawerLayout.Children)
+ {
+ var width = child.DesiredSize.Width;
+ }
+ }
+ Assert.Equal(200, drawerLayout?.WidthRequest);
+ }
+
+ [Fact]
+ public void Test_Bug907855_11()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer() { DrawerSettings = new DrawerSettings(), ContentView = new Grid() };
+ navigationDrawer.DrawerSettings.DrawerFooterView = new Label();
+ navigationDrawer.DrawerSettings.DrawerFooterHeight = 350;
+ navigationDrawer.DrawerSettings.DrawerWidth = -100;
+ var drawerLayout = GetPrivateField(navigationDrawer, "_drawerLayout") as SfGrid;
+ if (drawerLayout != null)
+ {
+ foreach (var child in drawerLayout.Children)
+ {
+ var width = child.DesiredSize.Width;
+ }
+ }
+ Assert.Equal(200, drawerLayout?.WidthRequest);
+ }
+
+ #endregion
+
#region events
[Fact]
@@ -1600,6 +2192,20 @@ public void TestDrawerOpenedInvoked()
Assert.True(fired);
}
+ [Fact]
+ public void TestDrawerOpeningInvoked()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer
+ {
+ ContentView = new Grid(),
+ DrawerSettings = new DrawerSettings { Transition = Transition.Reveal }
+ };
+ var fired = false;
+ navigationDrawer.DrawerOpening += (sender, e) => fired = true;
+ InvokePrivateMethod(navigationDrawer, "SetDrawerOpeningEvent");
+ Assert.True(fired);
+ }
+
[Fact]
public void TestDrawerToggledInvoked()
{
@@ -1609,6 +2215,51 @@ public void TestDrawerToggledInvoked()
InvokePrivateMethod(navigationDrawer, "OnDrawerOpenedToggledEvent");
Assert.True(fired);
}
+
+ [Theory]
+ [InlineData(PointerActions.Pressed, Position.Left)]
+ [InlineData(PointerActions.Pressed, Position.Right)]
+ [InlineData(PointerActions.Pressed, Position.Top)]
+ [InlineData(PointerActions.Pressed, Position.Bottom)]
+ [InlineData(PointerActions.Moved, Position.Left)]
+ [InlineData(PointerActions.Exited, Position.Left)]
+ [InlineData(PointerActions.Cancelled, Position.Left)]
+ public void Test_Drawer_OnHandleTouch(PointerActions action, Position position)
+ {
+ var navigationDrawer = new SfNavigationDrawer();
+ navigationDrawer.DrawerSettings.Position = position;
+ SetPrivateField(navigationDrawer, "_isPressed", true);
+ SetPrivateField(navigationDrawer, "_isMoved", true);
+ SetPrivateField(navigationDrawer, "_isDrawerOpen", true);
+ var eventArgs = new Syncfusion.Maui.Toolkit.Internals.PointerEventArgs(1, action, new Point(30, 30));
+
+ var exception = Record.Exception(() => InvokePrivateMethod(navigationDrawer, "OnHandleTouchInteraction", action, new Point(30, 30)));
+ Assert.Null(exception);
+ }
+
+ [Theory]
+ [InlineData(PointerActions.Pressed)]
+ [InlineData(PointerActions.Released)]
+ [InlineData(PointerActions.Moved)]
+ [InlineData(PointerActions.Exited)]
+ [InlineData(PointerActions.Cancelled)]
+ public void Test_Drawer_OnTouch(PointerActions action)
+ {
+ var navigationDrawer = new SfNavigationDrawer();
+ var eventArgs = new Syncfusion.Maui.Toolkit.Internals.PointerEventArgs(1, action, new Point(30, 30));
+
+ var exception = Record.Exception(() => ((ITouchListener)navigationDrawer).OnTouch(eventArgs));
+ Assert.Null(exception);
+ }
+
+ [Fact]
+ public void Test_ValidateCurrentDuration()
+ {
+ SfNavigationDrawer navigationDrawer = new SfNavigationDrawer();
+ var currentDuration = InvokePrivateMethod(navigationDrawer, "ValidateCurrentDuration", 0);
+ Assert.Equal(1.0, currentDuration);
+ }
+
#endregion
}
}
\ No newline at end of file
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Navigation/SfTabViewUnitTests.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Navigation/SfTabViewUnitTests.cs
index 0ad99647..be5a6813 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Navigation/SfTabViewUnitTests.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Navigation/SfTabViewUnitTests.cs
@@ -80,6 +80,17 @@ private TabItemCollection PopulateMixedItemsCollection()
return tabItems;
}
+ private TabItemCollection PopulateLabelImageItemsCollection()
+ {
+ var tabViewItems = new TabItemCollection
+ {
+ new SfTabItem { Header = "TAB 1", ImageSource ="SampleImage1.png", Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "TAB 2", ImageSource ="SampleImage1.png", Content = new Label { Text = "Content 2" } },
+ new SfTabItem { Header = "TAB 3", ImageSource ="SampleImage1.png", Content = new Label { Text = "Content 3" } }
+ };
+ return tabViewItems;
+ }
+
private TabItemCollection PopulateLabelItemsCollection()
{
var tabViewItems = new TabItemCollection
@@ -91,6 +102,20 @@ private TabItemCollection PopulateLabelItemsCollection()
return tabViewItems;
}
+ private TabItemCollection PopulateLabelItemsCollectionMore()
+ {
+ var tabViewItems = new TabItemCollection
+ {
+ new SfTabItem { Header = "TAB 1", Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "TAB 2", Content = new Label { Text = "Content 2" } },
+ new SfTabItem { Header = "TAB 3", Content = new Label { Text = "Content 3" } },
+ new SfTabItem { Header = "TAB 4", Content = new Label { Text = "Content 4" } },
+ new SfTabItem { Header = "TAB 5", Content = new Label { Text = "Content 5" } },
+ new SfTabItem { Header = "TAB 6", Content = new Label { Text = "Content 6" } }
+ };
+ return tabViewItems;
+ }
+
private List PopulateLargeData()
{
List largeDataSet = Enumerable.Range(1, 1000)
@@ -466,6 +491,19 @@ public void TestTabView()
Assert.Empty(tabView.Items);
}
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void EnableVirtualization(bool expected)
+ {
+ var tabView = new SfTabView
+ {
+ EnableVirtualization = expected
+ };
+
+ Assert.Equal(expected, tabView.EnableVirtualization);
+ }
+
#endregion
#region TabView Public Events
@@ -765,6 +803,467 @@ public void TestIndicatorPlacementPropertyShouldUpdateValue()
Assert.Equal(TabIndicatorPlacement.Top, tabBar.IndicatorPlacement);
}
+ [Fact]
+ public void TestIndicatorPlacementPropertyShouldBeFill()
+ {
+ tabBar.IndicatorPlacement = TabIndicatorPlacement.Fill;
+ Assert.Equal(TabIndicatorPlacement.Fill, tabBar.IndicatorPlacement);
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Bottom)]
+ [InlineData(TabIndicatorPlacement.Top)]
+ [InlineData(TabIndicatorPlacement.Fill)]
+ public void TestIndicatorPositionInTabHeaderContainer(TabIndicatorPlacement expected)
+ {
+ var tabView = new SfTabView();
+ tabView.IndicatorPlacement = expected;
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfGrid? parentGrid = GetPrivateField(tabBar, "_tabHeaderContentContainer") as SfGrid;
+ Assert.NotNull(parentGrid);
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ if (expected == TabIndicatorPlacement.Fill)
+ {
+ Assert.Equal(border, parentGrid.Children[0]);
+ }
+ else
+ {
+ Assert.Equal(border, parentGrid.Children[1]);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Bottom)]
+ [InlineData(TabIndicatorPlacement.Top)]
+ [InlineData(TabIndicatorPlacement.Fill)]
+ public void TestIndicatorLayoutPositionInTabHeaderContainer(TabIndicatorPlacement expected)
+ {
+ var tabView = new SfTabView();
+ tabView.IndicatorPlacement = expected;
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfGrid? parentGrid = GetPrivateField(tabBar, "_tabHeaderContentContainer") as SfGrid;
+ Assert.NotNull(parentGrid);
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(border);
+ if (expected == TabIndicatorPlacement.Fill)
+ {
+ Assert.Equal(LayoutOptions.Fill, border.VerticalOptions);
+ }
+ else if (expected == TabIndicatorPlacement.Top)
+ {
+ Assert.Equal(LayoutOptions.Start, border.VerticalOptions);
+ }
+ else
+ {
+ Assert.Equal(LayoutOptions.End, border.VerticalOptions);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabBarPlacement.Bottom, 500, TabIndicatorPlacement.Bottom)]
+ [InlineData(TabBarPlacement.Bottom, 250, TabIndicatorPlacement.Bottom)]
+ [InlineData(TabBarPlacement.Bottom, 500, TabIndicatorPlacement.Fill)]
+ [InlineData(TabBarPlacement.Bottom, 250, TabIndicatorPlacement.Fill)]
+ [InlineData(TabBarPlacement.Bottom, 500, TabIndicatorPlacement.Top)]
+ [InlineData(TabBarPlacement.Bottom, 250, TabIndicatorPlacement.Top)]
+ public void BottomContentTransition_Bottom(TabBarPlacement tabBarPlacement, double contentTransition, TabIndicatorPlacement tabIndicatorPlacement)
+ {
+ var tabView = new SfTabView();
+ tabView.Items = PopulateLabelItemsCollectionMore();
+ tabView.TabBarPlacement = tabBarPlacement;
+ tabView.ContentTransitionDuration = contentTransition;
+ tabView.IndicatorPlacement = tabIndicatorPlacement;
+ SfGrid? parentGrid = GetPrivateField(tabView, "_parentGrid") as SfGrid;
+ Assert.NotNull(parentGrid);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.NotNull(horizontalContent);
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(border);
+ if (tabBarPlacement is TabBarPlacement.Bottom)
+ {
+ Assert.Equal(0, Grid.GetRow(horizontalContent));
+ Assert.Equal(1, Grid.GetRow(tabBar));
+ }
+ else
+ {
+ Assert.Equal(0, Grid.GetRow(tabBar));
+ Assert.Equal(1, Grid.GetRow(horizontalContent));
+ }
+
+ Assert.Equal(contentTransition, tabBar.ContentTransitionDuration);
+
+ if (tabIndicatorPlacement == TabIndicatorPlacement.Fill)
+ {
+ Assert.Equal(LayoutOptions.Fill, border.VerticalOptions);
+ }
+ else if (tabIndicatorPlacement == TabIndicatorPlacement.Top)
+ {
+ Assert.Equal(LayoutOptions.Start, border.VerticalOptions);
+ }
+ else
+ {
+ Assert.Equal(LayoutOptions.End, border.VerticalOptions);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabBarPlacement.Bottom, 500, TabIndicatorPlacement.Bottom, FlowDirection.RightToLeft)]
+ [InlineData(TabBarPlacement.Bottom, 250, TabIndicatorPlacement.Bottom, FlowDirection.RightToLeft)]
+ [InlineData(TabBarPlacement.Bottom, 500, TabIndicatorPlacement.Fill, FlowDirection.RightToLeft)]
+ [InlineData(TabBarPlacement.Bottom, 250, TabIndicatorPlacement.Fill, FlowDirection.RightToLeft)]
+ [InlineData(TabBarPlacement.Bottom, 500, TabIndicatorPlacement.Top, FlowDirection.RightToLeft)]
+ [InlineData(TabBarPlacement.Bottom, 250, TabIndicatorPlacement.Top, FlowDirection.RightToLeft)]
+ public void Direction_BottomContentTransition_Bottom(TabBarPlacement tabBarPlacement, double contentTransition, TabIndicatorPlacement tabIndicatorPlacement, FlowDirection flowDirection)
+ {
+ var tabView = new SfTabView();
+ tabView.FlowDirection = flowDirection;
+ tabView.Items = PopulateLabelItemsCollectionMore();
+ tabView.TabBarPlacement = tabBarPlacement;
+ tabView.ContentTransitionDuration = contentTransition;
+ tabView.IndicatorPlacement = tabIndicatorPlacement;
+ SfGrid? parentGrid = GetPrivateField(tabView, "_parentGrid") as SfGrid;
+ Assert.NotNull(parentGrid);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.NotNull(horizontalContent);
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(border);
+ if (tabBarPlacement is TabBarPlacement.Bottom)
+ {
+ Assert.Equal(0, Grid.GetRow(horizontalContent));
+ Assert.Equal(1, Grid.GetRow(tabBar));
+ }
+ else
+ {
+ Assert.Equal(0, Grid.GetRow(tabBar));
+ Assert.Equal(1, Grid.GetRow(horizontalContent));
+ }
+
+ Assert.Equal(contentTransition, tabBar.ContentTransitionDuration);
+
+ if (tabIndicatorPlacement == TabIndicatorPlacement.Fill)
+ {
+ Assert.Equal(LayoutOptions.Fill, border.VerticalOptions);
+ }
+ else if (tabIndicatorPlacement == TabIndicatorPlacement.Top)
+ {
+ Assert.Equal(LayoutOptions.Start, border.VerticalOptions);
+ }
+ else
+ {
+ Assert.Equal(LayoutOptions.End, border.VerticalOptions);
+ }
+ }
+
+ [Theory]
+ [InlineData(500, TabIndicatorPlacement.Bottom, FlowDirection.RightToLeft)]
+ [InlineData(250, TabIndicatorPlacement.Bottom, FlowDirection.RightToLeft)]
+ [InlineData(500, TabIndicatorPlacement.Fill, FlowDirection.RightToLeft)]
+ [InlineData(250, TabIndicatorPlacement.Fill, FlowDirection.RightToLeft)]
+ [InlineData(500, TabIndicatorPlacement.Top, FlowDirection.RightToLeft)]
+ [InlineData(250, TabIndicatorPlacement.Top, FlowDirection.RightToLeft)]
+ public void Direction_TopContentTransition(double contentTransition, TabIndicatorPlacement tabIndicatorPlacement, FlowDirection flowDirection)
+ {
+ var tabView = new SfTabView();
+ tabView.FlowDirection = flowDirection;
+ tabView.Items = PopulateLabelItemsCollectionMore();
+ tabView.ContentTransitionDuration = contentTransition;
+ tabView.IndicatorPlacement = tabIndicatorPlacement;
+ SfGrid? parentGrid = GetPrivateField(tabView, "_parentGrid") as SfGrid;
+ Assert.NotNull(parentGrid);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.NotNull(horizontalContent);
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(border);
+
+ Assert.Equal(contentTransition, tabBar.ContentTransitionDuration);
+
+ if (tabIndicatorPlacement == TabIndicatorPlacement.Fill)
+ {
+ Assert.Equal(LayoutOptions.Fill, border.VerticalOptions);
+ }
+ else if (tabIndicatorPlacement == TabIndicatorPlacement.Top)
+ {
+ Assert.Equal(LayoutOptions.Start, border.VerticalOptions);
+ }
+ else
+ {
+ Assert.Equal(LayoutOptions.End, border.VerticalOptions);
+ }
+ }
+
+ [Theory]
+ [InlineData(500, TabIndicatorPlacement.Bottom)]
+ [InlineData(250, TabIndicatorPlacement.Bottom)]
+ [InlineData(500, TabIndicatorPlacement.Fill)]
+ [InlineData(250, TabIndicatorPlacement.Fill)]
+ [InlineData(500, TabIndicatorPlacement.Top)]
+ [InlineData(250, TabIndicatorPlacement.Top)]
+ public void TopContentTransition(double contentTransition, TabIndicatorPlacement tabIndicatorPlacement)
+ {
+ var tabView = new SfTabView();
+ tabView.Items = PopulateLabelItemsCollectionMore();
+ tabView.ContentTransitionDuration = contentTransition;
+ tabView.IndicatorPlacement = tabIndicatorPlacement;
+ SfGrid? parentGrid = GetPrivateField(tabView, "_parentGrid") as SfGrid;
+ Assert.NotNull(parentGrid);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.NotNull(horizontalContent);
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(border);
+
+ Assert.Equal(contentTransition, tabBar.ContentTransitionDuration);
+
+ if (tabIndicatorPlacement == TabIndicatorPlacement.Fill)
+ {
+ Assert.Equal(LayoutOptions.Fill, border.VerticalOptions);
+ }
+ else if (tabIndicatorPlacement == TabIndicatorPlacement.Top)
+ {
+ Assert.Equal(LayoutOptions.Start, border.VerticalOptions);
+ }
+ else
+ {
+ Assert.Equal(LayoutOptions.End, border.VerticalOptions);
+ }
+ }
+
+ [Theory]
+ [InlineData(FlowDirection.RightToLeft)]
+ [InlineData(FlowDirection.LeftToRight)]
+ public void FlowDirection1(FlowDirection flowDirection)
+ {
+ var tabView = new SfTabView()
+ {
+ FlowDirection = flowDirection,
+ };
+
+ tabView.Items = PopulateLabelItemsCollectionMore();
+
+ SfGrid? parentGrid = GetPrivateField(tabView, "_parentGrid") as SfGrid;
+ Assert.NotNull(parentGrid);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.NotNull(horizontalContent);
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(border);
+ Assert.Equal(flowDirection, tabBar.FlowDirection);
+ Assert.Equal(flowDirection, parentGrid.FlowDirection);
+ }
+
+ [Theory]
+ [InlineData(TabBarPlacement.Bottom, FlowDirection.LeftToRight)]
+ public void FlowDirection2(TabBarPlacement tabBarPlacement, FlowDirection flowDirection)
+ {
+ var tabView = new SfTabView()
+ {
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelItemsCollectionMore();
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ SfGrid? parentGrid = GetPrivateField(tabView, "_parentGrid") as SfGrid;
+ Assert.NotNull(parentGrid);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.NotNull(horizontalContent);
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(border);
+ Assert.Equal(FlowDirection.RightToLeft, parentGrid.FlowDirection);
+ Assert.Equal(FlowDirection.RightToLeft, tabBar.FlowDirection);
+ tabView.FlowDirection = flowDirection;
+ tabView.TabBarPlacement = tabBarPlacement;
+ if (tabBarPlacement is TabBarPlacement.Bottom)
+ {
+ Assert.Equal(0, Grid.GetRow(horizontalContent));
+ Assert.Equal(1, Grid.GetRow(tabBar));
+ }
+ else
+ {
+ Assert.Equal(0, Grid.GetRow(tabBar));
+ Assert.Equal(1, Grid.GetRow(horizontalContent));
+ }
+
+ Assert.Equal(flowDirection, tabBar.FlowDirection);
+ Assert.Equal(flowDirection, parentGrid.FlowDirection);
+ }
+
+ [Theory]
+ [InlineData(TabBarPlacement.Bottom, FlowDirection.RightToLeft)]
+ [InlineData(TabBarPlacement.Top, FlowDirection.RightToLeft)]
+ public void FlowDirection3(TabBarPlacement tabBarPlacement, FlowDirection flowDirection)
+ {
+ var tabView = new SfTabView()
+ {
+ FlowDirection = flowDirection,
+ };
+
+ tabView.Items = PopulateLabelItemsCollectionMore();
+
+ SfGrid? parentGrid = GetPrivateField(tabView, "_parentGrid") as SfGrid;
+ Assert.NotNull(parentGrid);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.NotNull(horizontalContent);
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(border);
+ Assert.Equal(flowDirection, tabBar.FlowDirection);
+ Assert.Equal(flowDirection, parentGrid.FlowDirection);
+ tabView.TabBarPlacement = tabBarPlacement;
+ if (tabBarPlacement is TabBarPlacement.Bottom)
+ {
+ Assert.Equal(0, Grid.GetRow(horizontalContent));
+ Assert.Equal(1, Grid.GetRow(tabBar));
+ }
+ else
+ {
+ Assert.Equal(0, Grid.GetRow(tabBar));
+ Assert.Equal(1, Grid.GetRow(horizontalContent));
+ }
+ }
+
+ [Theory]
+ [InlineData(TabBarPlacement.Bottom, FlowDirection.LeftToRight)]
+ [InlineData(TabBarPlacement.Top, FlowDirection.LeftToRight)]
+ public void FlowDirection4(TabBarPlacement tabBarPlacement, FlowDirection flowDirection)
+ {
+ var tabView = new SfTabView()
+ {
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelItemsCollectionMore();
+
+ SfGrid? parentGrid = GetPrivateField(tabView, "_parentGrid") as SfGrid;
+ Assert.NotNull(parentGrid);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.NotNull(horizontalContent);
+ Assert.Equal(FlowDirection.RightToLeft, tabBar.FlowDirection);
+ Assert.Equal(FlowDirection.RightToLeft, parentGrid.FlowDirection);
+ tabView.FlowDirection = flowDirection;
+ tabView.TabBarPlacement = tabBarPlacement;
+ if (tabBarPlacement is TabBarPlacement.Bottom)
+ {
+ Assert.Equal(0, Grid.GetRow(horizontalContent));
+ Assert.Equal(1, Grid.GetRow(tabBar));
+ }
+ else
+ {
+ Assert.Equal(0, Grid.GetRow(tabBar));
+ Assert.Equal(1, Grid.GetRow(horizontalContent));
+ }
+
+ Assert.Equal(flowDirection, tabBar.FlowDirection);
+ Assert.Equal(flowDirection, parentGrid.FlowDirection);
+ }
+
+ [Theory]
+ [InlineData(50)]
+ [InlineData(200)]
+ [InlineData(1000)]
+ [InlineData(50, true)]
+ public void MAUI_SfTabView_LoadTesting(int numberOfItems, bool removeNewItems = false)
+ {
+ var tabView = new SfTabView();
+ tabView.Items = PopulateLabelItemsCollectionMore();
+ var existingCount = tabView.Items.Count;
+ for (int i = 0; i < numberOfItems; i++)
+ {
+ var item = new SfTabItem()
+ {
+ Header = $"Item {tabView.Items.Count + 1}",
+ Content = new Label()
+ {
+ Text = $"Item {i}",
+ }
+ };
+ tabView.Items.Add(item);
+ }
+
+ var totalCount = existingCount + numberOfItems;
+ Assert.Equal(totalCount, tabView.Items.Count);
+
+ if (removeNewItems)
+ {
+ for (int i = totalCount - 1; i > existingCount - 1; i--)
+ {
+ tabView.Items.RemoveAt(i);
+ }
+
+ Assert.Equal(existingCount, tabView.Items.Count);
+ }
+ }
+
+ [Theory]
+ [InlineData(2)]
+ [InlineData(2, true)]
+ [InlineData(2, true, true)]
+ [InlineData(2, false, true)]
+ public void TestTab(int numberOfItems, bool removeNewItems = false, bool changeFlowDirection = false)
+ {
+ var tabView = new SfTabView();
+ tabView.Items = PopulateLabelItemsCollectionMore();
+ var existingCount = tabView.Items.Count;
+ for (int i = 0; i < numberOfItems; i++)
+ {
+ var item = new SfTabItem()
+ {
+ Header = $"Item {tabView.Items.Count + 1}",
+ Content = new Label()
+ {
+ Text = $"Item {i}",
+ }
+ };
+ tabView.Items.Add(item);
+ }
+
+ var totalCount = existingCount + numberOfItems;
+ Assert.Equal(totalCount, tabView.Items.Count);
+
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ SfGrid? parentGrid = GetPrivateField(tabView, "_parentGrid") as SfGrid;
+ Assert.NotNull(parentGrid);
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.NotNull(horizontalContent);
+
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+
+ Assert.Equal(0, Grid.GetRow(horizontalContent));
+ Assert.Equal(1, Grid.GetRow(tabBar));
+
+ if (removeNewItems)
+ {
+ for (int i = totalCount - 1; i > existingCount - 1; i--)
+ {
+ tabView.Items.RemoveAt(i);
+ }
+
+ Assert.Equal(existingCount, tabView.Items.Count);
+ }
+
+ if (changeFlowDirection)
+ {
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+ Assert.Equal(FlowDirection.RightToLeft, tabBar.FlowDirection);
+ Assert.Equal(FlowDirection.RightToLeft, parentGrid.FlowDirection);
+ }
+ }
+
[Fact]
public void TestTabWidthModePropertyShouldBeDefault()
{
@@ -1081,58 +1580,187 @@ public void TestUpdateFlowDirection()
Assert.Equal(FlowDirection.MatchParent, flowDirection);
}
- #endregion
-
- #region TabBar Public Method
-
[Fact]
- public void TestOnTapInvalidIndexOutOfRange()
+ public void TestUpdateTabIndicatorPosition()
{
- var items = tabBar.Items;
- var tabItem = new SfTabItem();
- items.Add(tabItem);
- TapEventArgs tapEvent = new TapEventArgs(new Point(10, 2), 1);
- var selectedIndex = tabBar.SelectedIndex;
- tabBar.OnTap(tabItem, tapEvent);
- Assert.Equal(0, selectedIndex);
- }
+ SfTabView tabView = new SfTabView()
+ {
+ Items = new TabItemCollection()
+ {
+ new SfTabItem() {Header="Tab1"},
+ new SfTabItem() {Header = "Tab2"},
+ new SfTabItem() {Header="Tab3"},
+ new SfTabItem() {Header = "Tab4"},
+ },
+ IsScrollButtonEnabled = true
- [Fact]
- public void TestOnTapValidIndexMultipleItems()
- {
- SfTabBar _sfTabBar = [];
- var items = _sfTabBar.Items;
- var tabItem1 = new SfTabItem();
- var tabItem2 = new SfTabItem();
- items.Add(tabItem1);
- items.Add(tabItem2);
+ };
- TapEventArgs tapEvent = new TapEventArgs(new Point(10, 2), 1);
- var selectedIndex = tabBar.SelectedIndex;
- tabBar.OnTap(tabItem1, tapEvent);
- tabBar.OnTap(tabItem2, tapEvent);
- Assert.Equal(_sfTabBar.SelectedIndex, selectedIndex);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ tabBar.UpdateTabIndicatorPosition();
+ SfScrollView? scrollView = GetPrivateField(tabBar, "_tabHeaderScrollView") as SfScrollView;
+ Assert.NotNull(scrollView);
}
- #endregion
-
- #region Behaviour Tests
-
[Theory]
- [InlineData(TextAlignment.End)]
- [InlineData(TextAlignment.Start)]
- [InlineData(TextAlignment.Center)]
- public void TestSfTabBarHeaderHorizontalTextAlignment(TextAlignment expected)
+ [InlineData(3, 4, 3)]
+ [InlineData(2, 3, 2)]
+ public void TestClearHeaderItem(double index, double selectedIndex, double expected)
{
- SfTabView tabView = new SfTabView
+ var tabitems = new TabItemCollection()
{
- HeaderHorizontalTextAlignment = expected
+ new SfTabItem() {Header="Tab1"},
+ new SfTabItem() {Header = "Tab2"},
+ new SfTabItem() {Header="Tab3"},
+ new SfTabItem() {Header = "Tab4"},
};
- SfTabBar? tabBar = (tabView.Children.FirstOrDefault() as SfGrid)?.Children.FirstOrDefault() as SfTabBar;
- Assert.Equal(expected, tabBar?.HeaderHorizontalTextAlignment);
- }
- [Theory]
+ SfTabBar tabBar = new SfTabBar()
+ {
+ Items = tabitems,
+ SelectedIndex = (int)selectedIndex,
+ IsScrollButtonEnabled = true
+ };
+
+ SfHorizontalStackLayout? horizontalStackLayout = GetPrivateField(tabBar, "_tabHeaderItemContent") as SfHorizontalStackLayout;
+ Assert.NotNull(horizontalStackLayout);
+ Assert.Equal(4, horizontalStackLayout.Children.Count);
+ tabBar.ClearHeaderItem(tabitems[3], (int)index);
+ SfHorizontalStackLayout? stackLayout = GetPrivateField(tabBar, "_tabHeaderItemContent") as SfHorizontalStackLayout;
+ Assert.NotNull(stackLayout);
+ Assert.Equal(3, stackLayout.Children.Count);
+ Assert.Equal(expected, tabBar.SelectedIndex);
+
+ }
+
+ [Fact]
+ public void TestClearLastHeaderItem()
+ {
+ var tabitems = new TabItemCollection()
+ {
+ new SfTabItem() {Header="Tab1"},
+ new SfTabItem() {Header = "Tab2"},
+ new SfTabItem() {Header="Tab3"},
+ new SfTabItem() {Header = "Tab4"},
+ };
+
+ SfTabBar tabBar = new SfTabBar()
+ {
+ Items = tabitems,
+ SelectedIndex = 4
+ };
+
+ SfHorizontalStackLayout? horizontalStackLayout = GetPrivateField(tabBar, "_tabHeaderItemContent") as SfHorizontalStackLayout;
+ Assert.NotNull(horizontalStackLayout);
+ Assert.Equal(4, horizontalStackLayout.Children.Count);
+ tabBar.ClearHeaderItem(tabitems[3], 4);
+ Assert.Equal(3, tabBar.SelectedIndex);
+
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Fill)]
+ [InlineData(TabIndicatorPlacement.Bottom)]
+ [InlineData(TabIndicatorPlacement.Top)]
+ public void TestUpdateSelectionIndicatorZIndex(TabIndicatorPlacement tabIndicatorPlacement)
+ {
+ var tabitems = new TabItemCollection()
+ {
+ new SfTabItem() {Header="Tab1"},
+ new SfTabItem() {Header = "Tab2"},
+ new SfTabItem() {Header="Tab3"},
+ new SfTabItem() {Header = "Tab4"},
+ };
+
+ SfTabBar tabBar = new SfTabBar()
+ {
+ Items = tabitems,
+ IndicatorPlacement = tabIndicatorPlacement,
+ SelectedIndex = 2
+ };
+
+ tabBar.UpdateSelectionIndicatorZIndex();
+
+ SfGrid? sfGrid = GetPrivateField(tabBar, "_tabHeaderContentContainer") as SfGrid;
+ Border? border = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(sfGrid);
+ Assert.NotNull(border);
+ if (tabIndicatorPlacement == TabIndicatorPlacement.Fill)
+ {
+
+ Assert.True(sfGrid.Children.Contains(border!));
+ Assert.Equal(0, sfGrid.IndexOf(border));
+ }
+ else
+ {
+ Assert.True(sfGrid.Children.Contains(border!));
+ Assert.NotEqual(0, sfGrid.Children.IndexOf(border));
+ }
+ }
+
+ #endregion
+
+ #region TabBar Public Method
+
+ [Fact]
+ public void TestOnTapInvalidIndexOutOfRange()
+ {
+ var items = tabBar.Items;
+ var tabItem = new SfTabItem();
+ items.Add(tabItem);
+ TapEventArgs tapEvent = new TapEventArgs(new Point(10, 2), 1);
+ var selectedIndex = tabBar.SelectedIndex;
+ tabBar.OnTap(tabItem, tapEvent);
+ Assert.Equal(0, selectedIndex);
+ }
+
+ [Fact]
+ public void TestOnTapValidIndexMultipleItems()
+ {
+ SfTabBar _sfTabBar = [];
+ var items = _sfTabBar.Items;
+ var tabItem1 = new SfTabItem();
+ var tabItem2 = new SfTabItem();
+ items.Add(tabItem1);
+ items.Add(tabItem2);
+
+ TapEventArgs tapEvent = new TapEventArgs(new Point(10, 2), 1);
+ var selectedIndex = tabBar.SelectedIndex;
+ tabBar.OnTap(tabItem1, tapEvent);
+ tabBar.OnTap(tabItem2, tapEvent);
+ Assert.Equal(_sfTabBar.SelectedIndex, selectedIndex);
+ }
+
+ [Fact]
+ public void TestTabBarOnTapWithInvalidSender()
+ {
+ SfTabBar tabBar = new SfTabBar();
+ SfTabItem tabItem = new SfTabItem();
+ var tapArgs = new TapEventArgs(new Point(50, 50), 1);
+ var exception = Record.Exception(() => tabBar.OnTap(tabItem, tapArgs));
+ Assert.Null(exception);
+ }
+
+ #endregion
+
+ #region Behaviour Tests
+
+ [Theory]
+ [InlineData(TextAlignment.End)]
+ [InlineData(TextAlignment.Start)]
+ [InlineData(TextAlignment.Center)]
+ public void TestSfTabBarHeaderHorizontalTextAlignment(TextAlignment expected)
+ {
+ SfTabView tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = expected
+ };
+ SfTabBar? tabBar = (tabView.Children.FirstOrDefault() as SfGrid)?.Children.FirstOrDefault() as SfTabBar;
+ Assert.Equal(expected, tabBar?.HeaderHorizontalTextAlignment);
+ }
+
+ [Theory]
[InlineData(IndicatorWidthMode.Fit)]
[InlineData(IndicatorWidthMode.Stretch)]
public void TestSfTabBarIndicatorWidthMode(IndicatorWidthMode expected)
@@ -1770,6 +2398,15 @@ public void TestSfTabBarUpdateTabPadding()
Assert.Equal(new Thickness(0), (tabHeaderContentContainer as SfGrid)?.Padding);
}
+ [Fact]
+ public void TestUpdateTabPadding()
+ {
+ SfTabBar tabBar = new SfTabBar();
+ tabBar.Items = PopulateMixedItemsCollection();
+ InvokePrivateMethod(tabBar, "UpdateTabPadding");
+ Assert.Equal(0, tabBar.SelectedIndex);
+ }
+
[Fact]
public void TestGetNextVisibleItem()
{
@@ -1790,6 +2427,132 @@ public void TestOnTabItemTouched()
InvokePrivateMethod(tabBar, "OnTabItemTouched", item, pointerEventArgs);
}
+ [Theory]
+ [InlineData(TextAlignment.Center)]
+ [InlineData(TextAlignment.End)]
+ [InlineData(TextAlignment.Start)]
+ [InlineData(TextAlignment.Justify)]
+ public void TestUpdateHeaderHorizontalAlignment(TextAlignment alignment)
+ {
+ PopulateTabBarItems();
+ tabBar.HeaderHorizontalTextAlignment = alignment;
+ foreach (var tabItem in tabBar.Items)
+ {
+ Assert.Equal(alignment, tabItem.HeaderHorizontalTextAlignment);
+ }
+
+ }
+
+ [Theory]
+ [InlineData(TabBarDisplayMode.Default)]
+ [InlineData(TabBarDisplayMode.Image)]
+ [InlineData(TabBarDisplayMode.Text)]
+ public void TestHeaderDisplayModeDynamicChanges(TabBarDisplayMode tabBarDisplayMode)
+ {
+ SfTabBar tabBar = new SfTabBar();
+ var imageSource = new FileImageSource { File = "icon.png" };
+ var tabItems = new TabItemCollection
+ {
+ new SfTabItem()
+ {
+ Header = "Call",
+ ImageSource = imageSource,
+ Content = new Button()
+ {
+ Text = "Tab Item1",
+ }
+ },
+ new SfTabItem()
+ {
+ Header = "Favorites",
+ ImageSource = imageSource,
+ Content = new Button()
+ {
+ Text = "Tab Item2",
+ }
+ },
+ new SfTabItem()
+ {
+ Header = "Contacts",
+ ImageSource = imageSource,
+ Content = new Button()
+ {
+ Text = "Tab Item3",
+ }
+ }
+ };
+
+ tabBar.Items = tabItems;
+ tabBar.HeaderDisplayMode = tabBarDisplayMode;
+ foreach (var item in tabBar.Items)
+ {
+ if (item.ImageSource != null && !string.IsNullOrEmpty(item.Header))
+ {
+ Assert.Equal(tabBarDisplayMode, item.HeaderDisplayMode);
+ }
+ }
+ }
+
+ [Fact]
+ public void TestUpdateItemSourceDynamically()
+ {
+ SfTabBar tabBar = new SfTabBar()
+ {
+ SelectedIndex = 2,
+
+ };
+
+ tabBar.ItemsSource = new ObservableCollection()
+ {
+ new Model(){Name="Tab1"},
+ new Model(){Name="Tab2"},
+ new Model(){Name="Tab3"}
+ };
+
+ Assert.NotNull(tabBar.ItemsSource);
+ Assert.Equal(3, tabBar.ItemsSource.Count);
+ Assert.Null(tabBar.HeaderItemTemplate);
+ Assert.Equal(2, tabBar.SelectedIndex);
+ tabBar.ItemsSource.Clear();
+ Assert.Empty(tabBar.ItemsSource);
+ tabBar.ItemsSource = new ObservableCollection()
+ {
+ new Model() { Name="Item1"},
+ new Model() { Name="Item2"},
+ new Model() { Name="Item3"},
+ new Model() { Name="Item4"}
+ };
+
+ Assert.NotNull(tabBar.ItemsSource);
+ Assert.Equal(4, tabBar.ItemsSource.Count);
+
+ }
+
+ [Theory]
+ [InlineData(21, 28)]
+ [InlineData(27, 36)]
+ [InlineData(6, 8)]
+ public void TestCalculateTabHeaderImageSize(double size, double expectedsize)
+ {
+ SfTabBar tabBar = new SfTabBar()
+ {
+ Items = new TabItemCollection()
+ {
+ new SfTabItem { Header = "TAB 1", ImageSource ="SampleImage1.png", ImageSize = size, Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "TAB 2", ImageSource ="SampleImage1.png", ImageSize = size, Content = new Label { Text = "Content 2" } },
+ new SfTabItem { Header = "TAB 3", ImageSource ="SampleImage1.png",ImageSize = size, Content = new Label { Text = "Content 3" } }
+ }
+ };
+
+ for (int i = 0; i < tabBar.Count; i++)
+ {
+ var result = InvokePrivateMethod(tabBar, "CalculateTabHeaderImageSize", [tabBar.Items[i].ImageSize]);
+ Assert.NotNull(result);
+ Assert.Equal(expectedsize, result);
+
+ }
+ }
+
[Fact]
public void TestHandleTabItemTapped()
{
@@ -1815,6 +2578,34 @@ public void TestHandleTabItemTapped()
}
}
+ [Fact]
+ public void TestScrollButtonDisabledColorWhenScrollEnabled()
+ {
+ var tabBar = new SfTabBar()
+ {
+ Items = new TabItemCollection()
+ {
+ new SfTabItem { Header="Tab1" },
+ new SfTabItem { Header="Tab2" },
+ },
+
+ };
+
+ tabBar.IsScrollButtonEnabled = true;
+ ArrowIcon? forwardArrow = GetPrivateField(tabBar, "_forwardArrow") as ArrowIcon;
+ ArrowIcon? backwardArrow = GetPrivateField(tabBar, "_backwardArrow") as ArrowIcon;
+ forwardArrow!.IsEnabled = false;
+ backwardArrow!.IsEnabled = false;
+ SetPrivateField(tabBar, "_forwardArrow", forwardArrow!);
+ SetPrivateField(tabBar, "_backwardArrow", backwardArrow!);
+ tabBar.ScrollButtonDisabledIconColor = Colors.Red;
+ ArrowIcon? resultForwardArrow = GetPrivateField(tabBar, "_forwardArrow") as ArrowIcon;
+ ArrowIcon? resultBackwardArrow = GetPrivateField(tabBar, "_backwardArrow") as ArrowIcon;
+ Assert.False(resultForwardArrow!.IsEnabled);
+ Assert.False(resultBackwardArrow!.IsEnabled);
+ Assert.Equal(Colors.Red, resultForwardArrow.ScrollButtonColor);
+ Assert.Equal(Colors.Red, resultBackwardArrow.ScrollButtonColor);
+ }
[Fact]
public void TestMixedObjectItemSource()
@@ -1903,6 +2694,82 @@ public void TestUpdateScrollButtonColor()
Assert.Equal(Colors.Red, forwardArrow?.ScrollButtonColor);
Assert.Equal(Colors.Blue, backwardArrow?.ScrollButtonColor);
}
+
+ [Fact]
+ public void TestTabBarUpdateItems()
+ {
+ SfTabBar tabBar = new SfTabBar()
+ {
+ IsScrollButtonEnabled = true,
+ Items = PopulateLabelImageItemsCollection(),
+ SelectedIndex = 1,
+ };
+
+ Assert.Equal(1, tabBar.SelectedIndex);
+ Assert.NotNull(tabBar.Items);
+ SfGrid? sfGrid = GetPrivateField(tabBar, "_tabHeaderParentContainer") as SfGrid;
+ ArrowIcon? forwardArrow = GetPrivateField(tabBar, "_forwardArrow") as ArrowIcon;
+ ArrowIcon? backwardArrow = GetPrivateField(tabBar, "_backwardArrow") as ArrowIcon;
+ Assert.NotNull(sfGrid);
+ Assert.NotNull(forwardArrow);
+ Assert.NotNull(backwardArrow);
+ Assert.True(sfGrid.Children.Contains(forwardArrow));
+ Assert.True(sfGrid.Children.Contains(backwardArrow));
+
+ }
+
+ [Fact]
+ public void TestUpdateTabItemWidth()
+ {
+ SfTabBar tabBar = new SfTabBar()
+ {
+ WidthRequest = 200,
+ Items = new TabItemCollection()
+ {
+ new SfTabItem() { Header= "Tab 1"},
+ new SfTabItem() { Header= "Tab 2"}
+ },
+ };
+
+ for (int i = 0; i < tabBar.Items.Count; i++)
+ {
+ if (tabBar.Items[i] is not null)
+ {
+ if (tabBar.Items[i].Parent is SfGrid)
+ {
+ var grid = tabBar.Items[i].Parent as SfGrid;
+ Assert.NotNull(grid);
+ };
+ }
+
+ }
+ }
+
+ [Fact]
+ public void TestUpdateIndicatorPosition()
+ {
+ SfTabBar tabBar = new SfTabBar()
+ {
+ Items = new TabItemCollection()
+ {
+ new SfTabItem() { Header="Tab1"},
+ new SfTabItem() {Header ="Tab2"},
+ new SfTabItem() {Header = "Tab3"},
+ new SfTabItem() {Header = "Tab4"}
+ }
+ };
+
+
+ SfHorizontalStackLayout? horizontalStackLayout = GetPrivateField(tabBar, "_tabHeaderItemContent") as SfHorizontalStackLayout;
+ Assert.NotNull(horizontalStackLayout);
+ Assert.Equal(4, horizontalStackLayout.Children.Count);
+ SetPrivateField(tabBar, "_removedItemWidth", 100);
+ InvokePrivateMethod(tabBar, "UpdateIndicatorPosition", [300, 40]);
+ double? result1 = (double?)GetPrivateField(tabBar, "_removedItemWidth");
+ Assert.NotNull(result1);
+ Assert.Equal(0, result1);
+ }
+
#endregion
#region TabView Internal Methods
@@ -1965,29 +2832,96 @@ public void TestTabItemTappedEvent(bool shouldRaise)
#region TabView Private Methods
+ [Theory]
+ [InlineData(-10)]
+ [InlineData(0)]
+ [InlineData(8)]
+ public void TestUpdateIndicatorStrokeThickness(double value)
+ {
+ var tabView = new SfTabView();
+ InvokePrivateMethod(tabView, "UpdateIndicatorStrokeThickness", [value]);
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(value, tabBar?.IndicatorStrokeThickness);
+ Border? indicator = GetPrivateField(tabBar, "_tabSelectionIndicator") as Border;
+ Assert.Equal(value, indicator?.HeightRequest);
+ }
+
[Fact]
- public void TestHeaderContainerRaisesSelectionChangingEvent()
+ public void TestItemsSource()
{
var tabView = new SfTabView();
- var wasEventRaised = false;
- tabView.SelectionChanging += (sender, e) =>
- {
- wasEventRaised = true;
- };
- var eventArgs = new SelectionChangingEventArgs();
- var methodInfo = typeof(SfTabView).GetMethod("HeaderContainer_SelectionChanging", BindingFlags.NonPublic | BindingFlags.Instance);
- methodInfo?.Invoke(tabView, [null, eventArgs]);
- Assert.True(wasEventRaised, "SelectionChanging event was not raised.");
+ InvokePrivateMethod(tabView, "UpdateItemsSource");
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ SfHorizontalContent? horizontalContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.Null(tabView.ItemsSource);
+ Assert.Null(tabBar?.ItemsSource);
+ Assert.Null(tabBar?.HeaderItemTemplate);
+ Assert.Null(horizontalContent?.ItemsSource);
+ Assert.Null(horizontalContent?.ContentItemTemplate);
}
[Fact]
- public void TestSelectionChangingEventRaises()
+ public void TestUpdateItems()
{
var tabView = new SfTabView();
- var wasEventRaised = false;
- tabView.SelectionChanging += (sender, e) =>
- {
- wasEventRaised = true;
+ tabView.Items = PopulateMixedItemsCollection();
+ tabView.SelectedIndex = -1;
+ InvokePrivateMethod(tabView, "UpdateItems");
+ Assert.Equal(0, tabView.SelectedIndex);
+ }
+
+ [Theory]
+ [InlineData(20)]
+ [InlineData(70)]
+ [InlineData(-54)]
+ [InlineData(-20)]
+ public void TestUpdateTabBarHeight(double height)
+ {
+ SfTabView tabView = new SfTabView()
+ {
+ IndicatorPlacement = TabIndicatorPlacement.Fill,
+ Items = PopulateLabelItemsCollection()
+ };
+
+ tabView.TabBarHeight = height;
+ SfTabBar? tabBar = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.NotNull(tabBar);
+ Border? border = GetPrivateField(tabBar!, "_tabSelectionIndicator") as Border;
+ Assert.NotNull(border);
+ if (height > 0)
+ {
+ Assert.Equal(height, border!.HeightRequest);
+ }
+ else
+ {
+ Assert.Equal(48, border!.HeightRequest);
+ }
+
+ }
+
+ [Fact]
+ public void TestHeaderContainerRaisesSelectionChangingEvent()
+ {
+ var tabView = new SfTabView();
+ var wasEventRaised = false;
+ tabView.SelectionChanging += (sender, e) =>
+ {
+ wasEventRaised = true;
+ };
+ var eventArgs = new SelectionChangingEventArgs();
+ var methodInfo = typeof(SfTabView).GetMethod("HeaderContainer_SelectionChanging", BindingFlags.NonPublic | BindingFlags.Instance);
+ methodInfo?.Invoke(tabView, [null, eventArgs]);
+ Assert.True(wasEventRaised, "SelectionChanging event was not raised.");
+ }
+
+ [Fact]
+ public void TestSelectionChangingEventRaises()
+ {
+ var tabView = new SfTabView();
+ var wasEventRaised = false;
+ tabView.SelectionChanging += (sender, e) =>
+ {
+ wasEventRaised = true;
};
var eventArgs = new SelectionChangingEventArgs();
var methodInfo = typeof(SfTabView).GetMethod("TabContentContainer_SelectionChanging", BindingFlags.NonPublic | BindingFlags.Instance);
@@ -2320,6 +3254,19 @@ public void TestHeaderDisplayModeSetter(TabBarDisplayMode expectedValue)
Assert.Equal(expectedValue, actualValue);
}
+ [Theory]
+ [InlineData(TabImagePosition.Top)]
+ [InlineData(TabImagePosition.Bottom)]
+ [InlineData(TabImagePosition.Left)]
+ [InlineData(TabImagePosition.Right)]
+ public void TestImagePositionSetter(TabImagePosition expectedValue)
+ {
+ var tabView = new TabViewMaterialVisualStyle();
+ tabView.ImagePosition = expectedValue;
+ var actualValue = (TabImagePosition)tabView.GetValue(TabViewMaterialVisualStyle.ImagePositionProperty); // Correct usage
+ Assert.Equal(expectedValue, actualValue);
+ }
+
#endregion
#region TabItem Properties
@@ -2591,6 +3538,17 @@ public void TestImageSize(double value)
}
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void TestIsSelected(bool value)
+ {
+ SfTabItem _tabItem = new SfTabItem();
+ _tabItem.IsSelected = value;
+ Assert.Equal(value, _tabItem.IsSelected);
+
+ }
+
#endregion
#region SelectionChangingEventArgs property
@@ -2664,6 +3622,19 @@ public void CheckTestOnTouchReleased()
Assert.Equal(touchEventArgs.Action, pointerActions);
}
+ [Fact]
+ public void TestOnTouchReleaseWhenFilledState()
+ {
+ SfTabItem item = new SfTabItem();
+ item.IndicatorPlacement = TabIndicatorPlacement.Fill;
+ item.IsSelected = true;
+ SetPrivateField(item, "_isPreviousItemSelected", true);
+ var touchEventArgs = new PointerEventArgs(1, PointerActions.Released, new Point(50, 50));
+ item.OnTouch(touchEventArgs);
+ PointerActions pointerActions = PointerActions.Released;
+ Assert.Equal(touchEventArgs.Action, pointerActions);
+ }
+
[Theory]
[InlineData(true)]
[InlineData(false)]
@@ -2816,6 +3787,19 @@ public void TestOnTouchReleasedActionForNegativePointValues(bool value)
Assert.Equal(touchEventArgs.TouchPoint, new Point(-500, 50));
}
+ [Fact]
+ public void TestOnTouchReleaseWhenItemDisabled()
+ {
+ SfTabItem item = new SfTabItem()
+ {
+ IsEnabled = false,
+ };
+
+ var touchEventArgs = new PointerEventArgs(1, PointerActions.Released, new Point(-500, 50));
+ item.OnTouch(touchEventArgs);
+ Assert.Equal(touchEventArgs.TouchPoint, new Point(-500, 50));
+ }
+
#endregion
#region HorizontalContent properties
@@ -3128,6 +4112,54 @@ public void GetNextVisibleItemIndexMethodCheck()
Assert.Equal(2, index);
}
+ [Fact]
+ public void GetNextVisibleItemIndexWhenItemIsVisibleFalse()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ var tabItems = new TabItemCollection()
+ {
+ new SfTabItem { Header = "TAB 1", Content = new Label { Text = "Content 1" } , IsVisible=true},
+ new SfTabItem { Header = "TAB 2", Content = new Button { Text = "Content 2" }, IsVisible=false },
+ new SfTabItem { Header = "TAB 3", Content = new Microsoft.Maui.Controls.Picker() { }, IsVisible=true},
+ new SfTabItem { Header = "TAB 4", Content = new Label { Text = "Content 4" } , IsVisible=false},
+ new SfTabItem { Header = "TAB 5", Content = new Button { Text = "Content 5" }, IsVisible=false },
+ new SfTabItem { Header = "TAB 6", Content = new Microsoft.Maui.Controls.Picker() { }, IsVisible=true}
+ };
+ horizontal.Items = tabItems;
+ horizontal.SelectedIndex = 2;
+ SetPrivateField(horizontal, "_isTowardsRight", true);
+ var nextIndex = InvokePrivateMethod(horizontal, "GetNextVisibleItemIndex");
+ Assert.NotNull(nextIndex);
+ double index = Convert.ToDouble(nextIndex);
+ Assert.Equal(6, horizontal.Items.Count);
+ Assert.Equal(5, index);
+ }
+
+ [Fact]
+ public void GetNextVisibleItemIndexMethodCheckWhenItemNull()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ var nextIndex = InvokePrivateMethod(horizontal, "GetNextVisibleItemIndex");
+ double index = Convert.ToDouble(nextIndex!);
+ Assert.Equal(0, index);
+ }
+
+ [Fact]
+ public void GetNextItemIndexMethodCheckWhenItemsSourceNull()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ var nextIndex = InvokePrivateMethod(horizontal, "GetNextItemIndex");
+ int index = Convert.ToInt32(nextIndex!);
+ ;
+ Assert.Equal(0, index);
+ }
+
[Theory]
[InlineData(0)]
[InlineData(3)]
@@ -3177,6 +4209,27 @@ public void HorizontalContentItemSourceCheck()
}
}
+ [Fact]
+ public void TestHorizontalContentItemsSource()
+ {
+ var itemSource1 = PopulateButtonsListItemsSource();
+ var itemSource2 = PopulateMixedObjectItemsSource();
+ DataTemplate template1 = new DataTemplate(() => new Label { Text = "Test" });
+ DataTemplate template2 = new DataTemplate(() => new Button { Text = "Test" });
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ horizontal.ItemsSource = itemSource1;
+ horizontal.ContentItemTemplate = template1;
+ Assert.Equal(horizontal.ItemsSource, itemSource1);
+ Assert.Equal(horizontal.ContentItemTemplate, template1);
+ horizontal.ItemsSource = itemSource2;
+ horizontal.ContentItemTemplate = template2;
+ Assert.NotEqual(horizontal.ItemsSource, itemSource1);
+ Assert.Equal(horizontal.ItemsSource, itemSource2);
+ Assert.Equal(horizontal.ContentItemTemplate, template2);
+ }
+
[Fact]
public void TestInitializeControl()
{
@@ -3610,6 +4663,22 @@ public void TestItemSource()
Assert.Equal(1, eventCount);
}
+ [Fact]
+ public void TestHorizontalContentItems()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ horizontal.SelectedIndex = 1;
+ horizontal.Items = PopulateMixedItemsCollection();
+ int eventCount = 0;
+ horizontal.SelectionChanging += (sender, args) => eventCount++;
+ SelectionChangingEventArgs args = new SelectionChangingEventArgs();
+ InvokePrivateMethod(horizontal, "RaiseSelectionChanging");
+ Assert.Equal(1, eventCount);
+ Assert.Equal(3, horizontal.Items.Count);
+ }
+
[Fact]
public void TestGetVisibleItemsCount()
{
@@ -3723,6 +4792,272 @@ public void TestRaiseSelectionChangingEventCheck()
Assert.True(eventInvoked);
}
+ [Fact]
+ public void TestUpdateContentItems()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ horizontal.Items = PopulateMixedItemsCollection();
+ InvokePrivateMethod(horizontal, "UpdateItems");
+ SfHorizontalStackLayout? stackLayout1 = GetPrivateField(horizontal, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ Assert.NotNull(horizontal.Items);
+ Assert.NotEmpty(stackLayout1?.Children!);
+ horizontal.Items = null!;
+ InvokePrivateMethod(horizontal, "UpdateItems");
+ SfHorizontalStackLayout? stackLayout2 = GetPrivateField(horizontal, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ Assert.Null(horizontal?.Items);
+ Assert.Empty(stackLayout2?.Children!);
+ }
+
+ [Fact]
+ public void TestAddTabContentItemsWithContent()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ SfTabItem tabItem = new SfTabItem()
+ {
+ Content = new Label { Text = "Tab1", BackgroundColor = Colors.Red }
+ };
+
+ InvokePrivateMethod(horizontal, "AddTabContentItems", [tabItem, -1]);
+ SfHorizontalStackLayout? stackLayout = GetPrivateField(horizontal, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ Assert.NotNull(stackLayout);
+ var sfGrid = stackLayout.Children.Last() as SfGrid;
+ Assert.NotNull(sfGrid);
+ Assert.Contains(tabItem.Content, sfGrid.Children);
+ }
+
+ [Fact]
+ public void TestAddTabContentItemsWithContentValidIndex()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ horizontal.SelectedIndex = -1;
+ horizontal.Items = PopulateMixedItemsCollection();
+ SfTabItem tabItem = new SfTabItem()
+ {
+ Content = new Label { Text = "Tab1", BackgroundColor = Colors.Red }
+ };
+
+ InvokePrivateMethod(horizontal, "AddTabContentItems", [tabItem, 0]);
+ SfHorizontalStackLayout? stackLayout1 = GetPrivateField(horizontal, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ Assert.NotNull(stackLayout1);
+ var grid = stackLayout1.Children[0] as SfGrid;
+ Assert.NotNull(grid);
+ Assert.Contains(tabItem.Content, grid.Children);
+ Assert.Equal(0, horizontal.SelectedIndex);
+ }
+
+ [Fact]
+ public void TestAddTabContentItemsWithoutContent()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ SfTabItem tabItem = new SfTabItem();
+ InvokePrivateMethod(horizontal, "AddTabContentItems", [tabItem, -1]);
+ SfHorizontalStackLayout? stackLayout = GetPrivateField(horizontal, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ Assert.NotNull(stackLayout);
+ SfGrid? sfGrid = stackLayout.Children.Last() as SfGrid;
+ Assert.NotNull(sfGrid);
+ Assert.False(sfGrid.Children.Any());
+ }
+
+ [Fact]
+ public void TestAddTabContentItemsWithoutContentValidIndex()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ horizontal.SelectedIndex = -1;
+ horizontal.Items = PopulateMixedItemsCollection();
+ SfTabItem tabItem = new SfTabItem();
+ InvokePrivateMethod(horizontal, "AddTabContentItems", [tabItem, 0]);
+ SfHorizontalStackLayout? stackLayout1 = GetPrivateField(horizontal, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ Assert.NotNull(stackLayout1);
+ var grid = stackLayout1.Children[0] as SfGrid;
+ Assert.NotNull(grid);
+ Assert.False(grid.Children.Any());
+ Assert.Equal(0, horizontal.SelectedIndex);
+ }
+
+ [Fact]
+ public void TestOnHandleTouchInteractionPressed()
+ {
+ SfTabView sfTabView = new SfTabView()
+ {
+ EnableSwiping = true,
+ SelectedIndex = 2
+ };
+
+ SfTabBar tabBar = new SfTabBar();
+ tabBar.SelectedIndex = 2;
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, tabBar);
+ horizontal.ContentItemTemplate = new DataTemplate(() => new Label { Text = "Content1" });
+ horizontal.ContentWidth = 600;
+ horizontal.OnHandleTouchInteraction(PointerActions.Pressed, new Point(10, 10));
+ var currentIndex = GetPrivateField(horizontal, "_currentIndex");
+ var xPosition = GetPrivateField(horizontal, "_initialXPosition");
+ SfTabBar? sftabBar = GetPrivateField(horizontal, "_tabBar") as SfTabBar;
+ double expectedXPosition = -600 * 2;
+ Assert.Equal(expectedXPosition, xPosition);
+
+ }
+
+ [Fact]
+ public void TestOnHandleTouchInteractionMoved()
+ {
+ SfTabView sfTabView = new SfTabView()
+ {
+ EnableSwiping = true,
+ SelectedIndex = 1
+ };
+
+ sfTabView.ItemsSource = PopulateMixedObjectItemsSource();
+ sfTabView.ContentItemTemplate = new DataTemplate(() => new Label { Text = "Content" });
+ SfHorizontalContent? horizontal = GetPrivateField(sfTabView, "_tabContentContainer") as SfHorizontalContent;
+ SetPrivateField(horizontal!, "_isPressed", true);
+ SetPrivateField(horizontal!, "_visibleItemCount", 3);
+ horizontal!.OnHandleTouchInteraction(PointerActions.Moved, new Point(600, 600));
+ var oldValue = GetPrivateField(horizontal, "_oldPoint");
+ Assert.Equal(new Point(600, 600), oldValue);
+
+ }
+
+ [Theory]
+ [InlineData(PointerActions.Released)]
+ [InlineData(PointerActions.Pressed)]
+ [InlineData(PointerActions.Moved)]
+ [InlineData(PointerActions.Cancelled)]
+ [InlineData(PointerActions.Exited)]
+ public void TestOnTouch(PointerActions pointerActions)
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ var eventArgs = new PointerEventArgs(1, pointerActions, new Point(10, 10));
+ var exception = Record.Exception(() => ((ITouchListener)horizontal).OnTouch(eventArgs));
+ Assert.Null(exception);
+
+ }
+
+ [Fact]
+ public void TestTranslateXPositionWhenSelectionChangingEventIsCancelled()
+ {
+ SfTabView sfTabView = new SfTabView()
+ {
+ Items = PopulateLabelItemsCollection(),
+ SelectedIndex = 0
+ };
+
+ SfTabBar? tabBar = GetPrivateField(sfTabView, "_tabHeaderContainer") as SfTabBar;
+ SfHorizontalContent? horizontalContent = GetPrivateField(sfTabView, "_tabContentContainer") as SfHorizontalContent;
+ horizontalContent!.SelectionChanging += (sender, e) =>
+ {
+ e.Cancel = true;
+ };
+
+ SetPrivateField(horizontalContent!, "_isPreviousItemVisible", true);
+ SetPrivateField(horizontalContent!, "_isNextItemVisible", true);
+ SetPrivateField(horizontalContent!, "_currentIndex", 0);
+ SetNonPublicProperty(horizontalContent, "IsTowardsRight", true);
+ SetPrivateField(horizontalContent!, "_contentWidth", 400);
+ InvokePrivateMethod(horizontalContent!, "TranslateXPosition", [-200]);
+ SfHorizontalStackLayout? stackLayout = GetPrivateField(horizontalContent, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ Assert.Equal(0, stackLayout!.TranslationX);
+ }
+
+ [Fact]
+ public void TestTranslateXPositionWhenSelectionChangingEvent()
+ {
+ SfTabView sfTabView = new SfTabView()
+ {
+ Items = PopulateLabelItemsCollection(),
+ SelectedIndex = 0
+ };
+
+ SfTabBar? tabBar = GetPrivateField(sfTabView, "_tabHeaderContainer") as SfTabBar;
+ SfHorizontalContent? horizontalContent = GetPrivateField(sfTabView, "_tabContentContainer") as SfHorizontalContent;
+ var eventTriggered = false;
+ horizontalContent!.SelectionChanging += (sender, e) =>
+ {
+ eventTriggered = true;
+ };
+
+ SetPrivateField(horizontalContent!, "_isPreviousItemVisible", true);
+ SetPrivateField(horizontalContent!, "_isNextItemVisible", true);
+ SetPrivateField(horizontalContent!, "_currentIndex", 0);
+ SetNonPublicProperty(horizontalContent, "IsTowardsRight", true);
+ SetPrivateField(horizontalContent!, "_contentWidth", 400);
+ InvokePrivateMethod(horizontalContent!, "TranslateXPosition", [-200]);
+ SfHorizontalStackLayout? stackLayout = GetPrivateField(horizontalContent, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ SfTabBar? tabBar1 = GetPrivateField(horizontalContent, "_tabBar") as SfTabBar;
+ Assert.Equal(-200, stackLayout!.TranslationX);
+ Assert.True(eventTriggered);
+ }
+
+ [Theory]
+ [InlineData(0, true, -50, false, true, -50, true)]
+ [InlineData(1, true, -50, false, true, -400, true)]
+ [InlineData(2, true, -250, true, true, 400, false)]
+ [InlineData(2, true, -100, true, false, 400, false)]
+ public void TestTranslateXPosition(int currentIndex, bool isTowardRight, double difference, bool isPreviousItem, bool isNextItem, double expectedTranlationX, bool expectedValue)
+ {
+ SfTabView sfTabView = new SfTabView()
+ {
+ Items = PopulateLabelItemsCollection(),
+ SelectedIndex = currentIndex
+ };
+
+ SfTabBar? tabBar = GetPrivateField(sfTabView, "_tabHeaderContainer") as SfTabBar;
+ SfHorizontalContent? horizontalContent = GetPrivateField(sfTabView, "_tabContentContainer") as SfHorizontalContent;
+ var eventTriggered = false;
+ horizontalContent!.SelectionChanging += (sender, e) =>
+ {
+ eventTriggered = true;
+ };
+
+ SetPrivateField(horizontalContent!, "_isPreviousItemVisible", isPreviousItem);
+ SetPrivateField(horizontalContent!, "_isNextItemVisible", isNextItem);
+ SetPrivateField(horizontalContent!, "_currentIndex", currentIndex);
+ SetNonPublicProperty(horizontalContent, "IsTowardsRight", isTowardRight);
+ SetPrivateField(horizontalContent!, "_contentWidth", 400);
+ InvokePrivateMethod(horizontalContent!, "TranslateXPosition", [difference]);
+ SfHorizontalStackLayout? stackLayout = GetPrivateField(horizontalContent, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ SfTabBar? tabBar1 = GetPrivateField(horizontalContent, "_tabBar") as SfTabBar;
+ Assert.Equal(expectedTranlationX, stackLayout!.TranslationX);
+ Assert.Equal(expectedValue, eventTriggered);
+ }
+
+ [Fact]
+ public void LoadsItems_WithVirtualization()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar sfTabBar = new SfTabBar();
+ var horizontalContent = new SfHorizontalContent(sfTabView, sfTabBar)
+ {
+ EnableVirtualization = true
+ };
+
+ var tabItem1 = new SfTabItem { Header = "Tab1", IsVisible = true };
+ var tabItem2 = new SfTabItem { Header = "Tab2", IsVisible = true };
+ horizontalContent.Items.Add(tabItem1);
+ horizontalContent.Items.Add(tabItem2);
+
+ var horizontalStackLayout = new SfHorizontalStackLayout();
+ horizontalStackLayout.Children.Add(new BoxView());
+ horizontalStackLayout.Children.Add(new BoxView());
+ SetPrivateField(horizontalContent, "_horizontalStackLayout", horizontalStackLayout);
+
+ InvokePrivateMethod(horizontalContent, "LoadItemsContent", 0);
+
+ var layout = (SfHorizontalStackLayout?)GetPrivateField(horizontalContent, "_horizontalStackLayout");
+ Assert.IsType(layout?.Children[0]);
+ Assert.True(layout.Children[0] is SfGrid, "Expected a SfGrid at index 0 after virtualization.");
+ }
#endregion
@@ -3785,6 +5120,17 @@ public void TestOnTouchMethod()
Assert.False(isPressed);
}
+ [Fact]
+ public void TestOnTap()
+ {
+ SfTabView sfTabView = new SfTabView();
+ SfTabBar views = new SfTabBar();
+ SfHorizontalContent horizontal = new SfHorizontalContent(sfTabView, views);
+ var tapArgs = new TapEventArgs(new Point(50, 50), 1);
+ var exception = Record.Exception(() => horizontal.OnTap(tapArgs));
+ Assert.Null(exception);
+ }
+
#endregion
#region General Tests
@@ -4081,6 +5427,2483 @@ public void TestOnTouchExited()
}
#endregion
+ #region CornerRadiusScripts
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, 20)]
+ [InlineData(TabIndicatorPlacement.Top, 0)]
+ [InlineData(TabIndicatorPlacement.Top, -20)]
+ [InlineData(TabIndicatorPlacement.Bottom, 20)]
+ [InlineData(TabIndicatorPlacement.Bottom, 0)]
+ [InlineData(TabIndicatorPlacement.Bottom, -20)]
+ [InlineData(TabIndicatorPlacement.Fill, 20)]
+ [InlineData(TabIndicatorPlacement.Fill, 0)]
+ [InlineData(TabIndicatorPlacement.Fill, -20)]
+ public void Test_Bottom_CornerRadius(TabIndicatorPlacement tabIndicatorPlacement, double radius)
+ {
+ var cornerRadius = new CornerRadius(radius);
+ var tabView = new SfTabView
+ {
+ IndicatorCornerRadius = cornerRadius,
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabBarPlacement = TabBarPlacement.Bottom
+ };
+
+ tabView.Items = PopulateLabelItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var indicatorRoundRectangle = GetPrivateField(tabHeader, "_roundRectangle") as RoundRectangle;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+
+ Assert.Equal(cornerRadius, indicatorRoundRectangle?.CornerRadius);
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, 20)]
+ [InlineData(TabIndicatorPlacement.Top, 0)]
+ [InlineData(TabIndicatorPlacement.Top, -20)]
+ [InlineData(TabIndicatorPlacement.Bottom, 20)]
+ [InlineData(TabIndicatorPlacement.Bottom, 0)]
+ [InlineData(TabIndicatorPlacement.Bottom, -20)]
+ [InlineData(TabIndicatorPlacement.Fill, 20)]
+ [InlineData(TabIndicatorPlacement.Fill, 0)]
+ [InlineData(TabIndicatorPlacement.Fill, -20)]
+ public void Test_Top_CornerRadius(TabIndicatorPlacement tabIndicatorPlacement, double radius)
+ {
+ var cornerRadius = new CornerRadius(radius);
+ var tabView = new SfTabView
+ {
+ IndicatorCornerRadius = cornerRadius,
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabBarPlacement = TabBarPlacement.Top
+ };
+
+ tabView.Items = PopulateLabelItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var indicatorRoundRectangle = GetPrivateField(tabHeader, "_roundRectangle") as RoundRectangle;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(0, tabHeaderIndex);
+ Assert.Equal(1, tabContentIndex);
+
+ Assert.Equal(cornerRadius, indicatorRoundRectangle?.CornerRadius);
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, 20)]
+ [InlineData(TabIndicatorPlacement.Top, 0)]
+ [InlineData(TabIndicatorPlacement.Top, -20)]
+ [InlineData(TabIndicatorPlacement.Bottom, 20)]
+ [InlineData(TabIndicatorPlacement.Bottom, 0)]
+ [InlineData(TabIndicatorPlacement.Bottom, -20)]
+ [InlineData(TabIndicatorPlacement.Fill, 20)]
+ [InlineData(TabIndicatorPlacement.Fill, 0)]
+ [InlineData(TabIndicatorPlacement.Fill, -20)]
+ public void Test_Bottom_CornerRadius_RTL(TabIndicatorPlacement tabIndicatorPlacement, double radius)
+ {
+ var cornerRadius = new CornerRadius(radius);
+ var tabView = new SfTabView
+ {
+ IndicatorCornerRadius = cornerRadius,
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft
+ };
+
+ tabView.Items = PopulateLabelItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var indicatorRoundRectangle = GetPrivateField(tabHeader, "_roundRectangle") as RoundRectangle;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+
+ Assert.Equal(cornerRadius, indicatorRoundRectangle?.CornerRadius);
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, 20)]
+ [InlineData(TabIndicatorPlacement.Top, 0)]
+ [InlineData(TabIndicatorPlacement.Top, -20)]
+ [InlineData(TabIndicatorPlacement.Bottom, 20)]
+ [InlineData(TabIndicatorPlacement.Bottom, 0)]
+ [InlineData(TabIndicatorPlacement.Bottom, -20)]
+ [InlineData(TabIndicatorPlacement.Fill, 20)]
+ [InlineData(TabIndicatorPlacement.Fill, 0)]
+ [InlineData(TabIndicatorPlacement.Fill, -20)]
+ public void Test_Top_CornerRadius_RTL(TabIndicatorPlacement tabIndicatorPlacement, double radius)
+ {
+ var cornerRadius = new CornerRadius(radius);
+ var tabView = new SfTabView
+ {
+ IndicatorCornerRadius = cornerRadius,
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabBarPlacement = TabBarPlacement.Top,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var indicatorRoundRectangle = GetPrivateField(tabHeader, "_roundRectangle") as RoundRectangle;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(0, tabHeaderIndex);
+ Assert.Equal(1, tabContentIndex);
+
+ Assert.Equal(cornerRadius, indicatorRoundRectangle?.CornerRadius);
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ }
+
+ #endregion
+
+ #region HeaderFolder Scripts
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ public void Test_DisplayHeaderMode_Bottom_Default(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader!.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem.HeaderDisplayMode);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ var actualIndicatorPlacement = (tabHeader!.Items[i]).IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ public void Test_DisplayHeaderMode_Default(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Top,
+ };
+
+ tabView.Items = PopulateLabelItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(0, tabHeaderIndex);
+ Assert.Equal(1, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader!.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem.HeaderDisplayMode);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ var actualIndicatorPlacement = (tabHeader!.Items[i]).IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ public void Test_DisplayHeaderMode_Bottom_Default_RTL(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft
+ };
+
+ tabView.Items = PopulateLabelItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader!.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem.HeaderDisplayMode);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ var actualIndicatorPlacement = (tabHeader!.Items[i]).IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Default)]
+ public void Test_DisplayHeaderMode_Default_RTL(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Top,
+ FlowDirection = FlowDirection.RightToLeft
+ };
+
+ tabView.Items = PopulateLabelItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(0, tabHeaderIndex);
+ Assert.Equal(1, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader!.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem.HeaderDisplayMode);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ var actualIndicatorPlacement = (tabHeader!.Items[i]).IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ public void Test_DisplayHeaderMode_Bottom_Text(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader!.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem.HeaderDisplayMode);
+
+ var actualIndicatorPlacement = tabItem.IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ public void Test_DisplayHeaderMode_Text(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Top,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(0, tabHeaderIndex);
+ Assert.Equal(1, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader!.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem.HeaderDisplayMode);
+
+ var actualIndicatorPlacement = tabItem.IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ public void Test_DisplayHeaderMode_Bottom_Text_RTL(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader!.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem.HeaderDisplayMode);
+
+ var actualIndicatorPlacement = tabItem.IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Text)]
+ public void Test_DisplayHeaderMode_Text_RTL(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Top,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(0, tabHeaderIndex);
+ Assert.Equal(1, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader!.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem.HeaderDisplayMode);
+
+ var actualIndicatorPlacement = tabItem.IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ public void Test_DisplayHeaderMode_Bottom_Image(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader?.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem?.HeaderDisplayMode);
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+
+ var actualIndicatorPlacement = tabItem?.IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ public void Test_DisplayHeaderMode_Image(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Top,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(0, tabHeaderIndex);
+ Assert.Equal(1, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader?.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem?.HeaderDisplayMode);
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+
+ var actualIndicatorPlacement = tabItem?.IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ public void Test_DisplayHeaderMode_Bottom_Image_RTL(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader?.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem?.HeaderDisplayMode);
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+
+ var actualIndicatorPlacement = tabItem?.IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Theory]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Top, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Bottom, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Fit, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ [InlineData(TabIndicatorPlacement.Fill, IndicatorWidthMode.Stretch, TabWidthMode.Default, TabBarDisplayMode.Image)]
+ public void Test_DisplayHeaderMode_Image_RTL(TabIndicatorPlacement tabIndicatorPlacement, IndicatorWidthMode indicatorWidthMode, TabWidthMode tabWidthMode, TabBarDisplayMode tabBarDisplayMode)
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorPlacement = tabIndicatorPlacement,
+ TabWidthMode = tabWidthMode,
+ IndicatorWidthMode = indicatorWidthMode,
+ HeaderDisplayMode = tabBarDisplayMode,
+ TabBarPlacement = TabBarPlacement.Top,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(0, tabHeaderIndex);
+ Assert.Equal(1, tabContentIndex);
+
+ Assert.Equal(tabIndicatorPlacement, tabHeader?.IndicatorPlacement);
+ Assert.Equal(indicatorWidthMode, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(tabWidthMode, tabHeader?.TabWidthMode);
+ Assert.Equal(tabBarDisplayMode, tabHeader?.HeaderDisplayMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var tabItem = tabHeader?.Items[i];
+ Assert.Equal(tabBarDisplayMode, tabItem?.HeaderDisplayMode);
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+
+ var actualIndicatorPlacement = tabItem?.IndicatorPlacement;
+ Assert.Equal(tabIndicatorPlacement, actualIndicatorPlacement);
+ }
+ }
+
+ [Fact]
+ public void Test_HeaderText1()
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorWidthMode = IndicatorWidthMode.Stretch,
+ };
+ var tabItems = new TabItemCollection
+ {
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 2" } }
+ };
+
+ tabView.Items = tabItems;
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(IndicatorWidthMode.Stretch, tabHeader?.IndicatorWidthMode);
+ }
+
+ [Fact]
+ public void Test_HeaderText2()
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorWidthMode = IndicatorWidthMode.Stretch,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+ var tabItems = new TabItemCollection
+ {
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 2" } }
+ };
+
+ tabView.Items = tabItems;
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(IndicatorWidthMode.Stretch, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ }
+
+ [Fact]
+ public void Test_HeaderText4()
+ {
+ var tabView = new SfTabView
+ {
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+ var tabItems = new TabItemCollection
+ {
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 2" } }
+ };
+
+ tabView.Items = tabItems;
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ }
+
+ [Fact]
+ public void Test_HeaderText5()
+ {
+ var tabView = new SfTabView
+ {
+ IndicatorWidthMode = IndicatorWidthMode.Stretch,
+ FlowDirection = FlowDirection.RightToLeft,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+ var tabItems = new TabItemCollection
+ {
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 2" } }
+ };
+
+ tabView.Items = tabItems;
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(IndicatorWidthMode.Stretch, tabHeader?.IndicatorWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ }
+
+ [Fact]
+ public void Test_HeaderText7()
+ {
+ var tabView = new SfTabView
+ {
+ FlowDirection = FlowDirection.RightToLeft,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+ var tabItems = new TabItemCollection
+ {
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "MAUI", Content = new Label { Text = "Content 2" } }
+ };
+
+ tabView.Items = tabItems;
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Centre_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Centre_Direction_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Centre_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ TabWidthMode = TabWidthMode.Default,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Centre_Direction_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ TabWidthMode = TabWidthMode.Default,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_End_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_End_Direction_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Direction_Start1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_End_Direction_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_End_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_End_ImagePosition2()
+ {
+ var tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_End1()
+ {
+ var tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Start_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Start_Direction_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Start_Direction_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Start_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignment_Start1()
+ {
+ var tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Centre_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Centre_Direction_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Centre_Direction_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Centre_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Centre1()
+ {
+ var tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Direction_Centre1()
+ {
+ var tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = TextAlignment.Center,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Center, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Direction_End1()
+ {
+ var tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Direction_Start1()
+ {
+ var tabView = new SfTabView
+ {
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_End_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_End_Direction_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_End_Direction_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_End_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.End,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.End, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Start_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Start_Direction_Default1()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Start_Direction_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ FlowDirection = FlowDirection.RightToLeft,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ Assert.Equal(FlowDirection.RightToLeft, tabHeader?.FlowDirection);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ [Fact]
+ public void Test_TabHeaderAlignmentBottom_Start_ImagePosition_Default2()
+ {
+ var tabView = new SfTabView
+ {
+ TabWidthMode = TabWidthMode.Default,
+ HeaderHorizontalTextAlignment = TextAlignment.Start,
+ TabBarPlacement = TabBarPlacement.Bottom,
+ };
+
+ tabView.Items = PopulateLabelImageItemsCollection();
+ var tabHeader = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContent = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var tabContentIndex = Grid.GetRow(tabContent);
+ var tabHeaderIndex = Grid.GetRow(tabHeader);
+
+ Assert.Equal(1, tabHeaderIndex);
+ Assert.Equal(0, tabContentIndex);
+ Assert.Equal(TabWidthMode.Default, tabHeader?.TabWidthMode);
+ foreach (var item in tabView.Items)
+ {
+ item.ImagePosition = TabImagePosition.Top;
+ }
+ for (int i = 0; i < tabView.Items.Count; i++)
+ {
+ var actualAlignment = (tabHeader!.Items[i]).HeaderHorizontalTextAlignment;
+ Assert.Equal(TextAlignment.Start, actualAlignment);
+
+ var expectedPosition = tabView.Items[i].ImagePosition;
+ var actualPosition = (tabHeader!.Items[i]).ImagePosition;
+ Assert.Equal(expectedPosition, actualPosition);
+ }
+ }
+
+ #endregion
+
+ #region Bugs
+
+ [Theory]
+ [InlineData(0)]
+ [InlineData(1)]
+ [InlineData(2)]
+ public void Bug_860909(int index)
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.SelectedIndex = index;
+ Assert.Equal(((Label)tabItems[index].Content).Text, ((Label)tabItems[(int)tabView.SelectedIndex].Content).Text);
+ }
+
+ [Fact]
+ public void Bug_870790()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarHeight = 50;
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(tabView.TabBarHeight, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Theory]
+ [InlineData(true, 1)]
+ public void MAUI860875(bool value, int recursiveCount)
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ int count = 0;
+ tabView.SelectionChanged += (sender, e) =>
+ {
+ e.Handled = value;
+ count++;
+ };
+ tabView.SelectedIndex = 1;
+ Assert.Equal(recursiveCount, count);
+ }
+
+ [Fact]
+ public void XAMARIN_19362()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var tabContentContainer = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var horizontalStackLayout = GetPrivateField(tabContentContainer, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ Assert.Equal(3, horizontalStackLayout!.Count);
+ tabView.Items.Add(new SfTabItem { Header = "TAB 4", Content = new Label { Text = "Content 4" } });
+ tabView.Items.Add(new SfTabItem { Header = "TAB 5", Content = new Label { Text = "Content 5" } });
+ Assert.Equal(5, horizontalStackLayout!.Count);
+
+ }
+
+ [Fact]
+ public void XAMARIN_19663()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var tabContentContainer = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ var horizontalStackLayout = GetPrivateField(tabContentContainer, "_horizontalStackLayout") as SfHorizontalStackLayout;
+ tabView.Items.RemoveAt(2);
+ Assert.Equal(2, horizontalStackLayout!.Count);
+ tabView.Items.RemoveAt(1);
+ Assert.Single(horizontalStackLayout!.Children);
+ tabView.Items.RemoveAt(0);
+ Assert.Empty(horizontalStackLayout!.Children);
+ }
+
+ [Fact]
+ public void XAMARIN_25935()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ bool eventTriggered = false;
+ tabView.SelectionChanged += (sender, e) =>
+ {
+ eventTriggered = true;
+ };
+ tabView.SelectedIndex = 2;
+ Assert.True(eventTriggered);
+ }
+
+ [Fact]
+ public void XAMARIN_39415()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ double index = -1;
+ tabView.SelectionChanged += (sender, e) =>
+ {
+ index = e.NewIndex;
+ };
+ for (int i = tabView.Items.Count - 1; i >= 0; i--)
+ {
+ tabView.SelectedIndex = i;
+ Assert.Equal(tabView.SelectedIndex, index);
+ }
+ }
+
+ [Fact]
+ public void XAMARIN_41311()
+ {
+ var tabView = new SfTabView();
+ var tabItems = new TabItemCollection
+ {
+ new SfTabItem { Header = "SfTabItem1", FontSize=20, Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "SfTabItem2", FontSize=20, Content = new Label { Text = "Content 2" } },
+ new SfTabItem { Header = "SfTabItem3", FontSize=20, Content = new Label { Text = "Content 3" } },
+ new SfTabItem { Header = "SfTabItem1", FontSize=20, Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "SfTabItem2", FontSize=20, Content = new Label { Text = "Content 2" } },
+ new SfTabItem { Header = "SfTabItem3", FontSize=20, Content = new Label { Text = "Content 3" } },
+ new SfTabItem { Header = "SfTabItem1", FontSize=20, Content = new Label { Text = "Content 1" } },
+ new SfTabItem { Header = "SfTabItem2", FontSize=20, Content = new Label { Text = "Content 2" } },
+ new SfTabItem { Header = "SfTabItem3", FontSize=20, Content = new Label { Text = "Content 3" } }
+ };
+
+ tabView.Items = tabItems;
+ tabView.TabWidthMode = TabWidthMode.Default;
+ var materialStyle = new TabViewMaterialVisualStyle();
+ var headerLabel = GetPrivateField(materialStyle, "_header") as SfLabel;
+ Assert.Equal(LineBreakMode.TailTruncation, headerLabel!.LineBreakMode);
+ }
+
+ [Fact]
+ public void XAMARIN_42654()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.SelectedIndex = 2;
+ Assert.True(tabView.Items[(int)tabView.SelectedIndex].IsVisible);
+ }
+
+ [Theory]
+ [InlineData(0, 2)]
+ [InlineData(1, 0)]
+ public void XAMARIN_866018(double index1, double index2)
+ {
+ var tabView1 = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView1.Items = tabItems;
+ tabView1.SelectedIndex =Convert.ToInt32(index1);
+
+ var tabView2 = new SfTabView();
+ tabView2.Items = PopulateLabelItemsCollection();
+ tabView2.SelectedIndex =Convert.ToInt32(index2);
+
+ var verticalStackLayout = new VerticalStackLayout();
+ verticalStackLayout.Add(tabView1);
+ verticalStackLayout.Add(tabView2);
+
+ Assert.Equal(index1, tabView1.SelectedIndex);
+ Assert.Equal(index2, tabView2.SelectedIndex);
+ }
+
+ #endregion
+
+ #region Indicator
+
+ [Fact]
+ public void IndicatorBackground1()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ var color = new SolidColorBrush(Colors.Violet);
+ tabView.IndicatorBackground = color;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackground2()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ var color = new SolidColorBrush(Colors.Violet);
+ tabView.IndicatorBackground = color;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackground3()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ var color = new SolidColorBrush(Colors.Violet);
+ tabView.IndicatorBackground = color;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackground7()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var color = new SolidColorBrush(Colors.Violet);
+ tabView.IndicatorBackground = color;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackground9()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var color = new SolidColorBrush(Colors.DarkGreen);
+ tabView.IndicatorBackground = color;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackground12()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ var color = new SolidColorBrush(Colors.Violet);
+ tabView.IndicatorBackground = color;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackgroundPlacement1()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var color = new SolidColorBrush(Colors.Black);
+ tabView.IndicatorBackground = color;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Fill;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackgroundPlacement4()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var color = new SolidColorBrush(Colors.Black);
+ tabView.IndicatorBackground = color;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackgroundPlacement7()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var color = new SolidColorBrush(Colors.Black);
+ tabView.IndicatorBackground = color;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackgroundPlacement10()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var color = new SolidColorBrush(Colors.Black);
+ tabView.IndicatorBackground = color;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Fill;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackgroundPlacement13()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var color = new SolidColorBrush(Colors.Black);
+ tabView.IndicatorBackground = color;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorBackgroundPlacement16()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ var color = new SolidColorBrush(Colors.Black);
+ tabView.IndicatorBackground = color;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal((Brush)color, (Brush)tabSelectionIndicator!.BackgroundColor);
+ }
+
+ [Fact]
+ public void IndicatorPlacement2()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal(LayoutOptions.Start, tabSelectionIndicator!.VerticalOptions);
+ }
+
+ [Fact]
+ public void IndicatorPlacement4()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Fill;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal(tabView.TabBarHeight, tabSelectionIndicator!.HeightRequest);
+ }
+
+ [Fact]
+ public void IndicatorPlacement5()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal(LayoutOptions.Start, tabSelectionIndicator!.VerticalOptions);
+ }
+
+ [Fact]
+ public void IndicatorPlacement6()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Fill;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal(tabView.TabBarHeight, tabSelectionIndicator!.HeightRequest);
+ }
+
+ [Fact]
+ public void IndicatorPlacement11()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal(LayoutOptions.Start, tabSelectionIndicator!.VerticalOptions);
+ }
+
+ [Fact]
+ public void IndicatorPlacement13()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabSelectionIndicator = GetPrivateField(tabHeaderContainer, "_tabSelectionIndicator") as Border;
+ Assert.Equal(LayoutOptions.Start, tabSelectionIndicator!.VerticalOptions);
+ }
+
+ #endregion
+ #region Customization
+
+ [Fact]
+ public void Tabbarbackground1()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarBackground = Colors.Gray;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(Colors.Gray, tabHeaderContainer!.Background);
+ }
+
+ [Fact]
+ public void Tabbarbackground4()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarBackground = Colors.Gray;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(Colors.Gray, tabHeaderContainer!.Background);
+ }
+
+ [Fact]
+ public void Tabbarbackground7()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarBackground = Colors.Gray;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(Colors.Gray, tabHeaderContainer!.Background);
+ }
+
+ [Fact]
+ public void TabbarbackgroundIndicatorplacement1()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarBackground = Colors.Gray;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Fill;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(Colors.Gray, tabHeaderContainer!.Background);
+ }
+
+ [Fact]
+ public void TabbarbackgroundIndicatorplacement4()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarBackground = Colors.Gray;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(Colors.Gray, tabHeaderContainer!.Background);
+ }
+
+ [Fact]
+ public void TabbarbackgroundIndicatorplacement10()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarBackground = Colors.Gray;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Fill;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(Colors.Gray, tabHeaderContainer!.Background);
+ }
+
+ [Fact]
+ public void TabbarbackgroundIndicatorplacement19()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarBackground = Colors.Gray;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(Colors.Gray, tabHeaderContainer!.Background);
+ }
+
+ [Fact]
+ public void TabbarbackgroundIndicatorplacement22()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarBackground = Colors.Gray;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(Colors.Gray, tabHeaderContainer!.Background);
+ }
+
+ [Fact]
+ public void TabbarbackgroundIndicatorplacement28()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarBackground = Colors.Gray;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(Colors.Gray, tabHeaderContainer!.Background);
+ }
+
+ [Fact]
+ public void TabBarHeight1()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarHeight = 100;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(100, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Fact]
+ public void TabBarHeight2()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarHeight = 100;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(100, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Fact]
+ public void TabBarHeight3()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarHeight = 100;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(100, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Fact]
+ public void TabBarHeight7()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ tabView.TabBarHeight = 100;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(100, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Fact]
+ public void TabBarHeight9()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ tabView.TabBarHeight = 100;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(100, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Fact]
+ public void TabBarHeight11()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarHeight = 100;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(100, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Fact]
+ public void TabBarHeight15()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ tabView.TabBarHeight = 100;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(100, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Fact]
+ public void TabBarHeight17()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.IndicatorWidthMode = IndicatorWidthMode.Stretch;
+ tabView.TabBarHeight = 100;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(100, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Fact]
+ public void TabBarHeight18()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarHeight = 100;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ Assert.Equal(100, tabHeaderContainer!.HeightRequest);
+ }
+
+ [Fact]
+ public void TabBarPlacement1()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContentContainer = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.Equal(0, Grid.GetRow(tabContentContainer));
+ Assert.Equal(1, Grid.GetRow(tabHeaderContainer));
+ }
+
+ [Fact]
+ public void TabBarPlacement4()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContentContainer = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.Equal(0, Grid.GetRow(tabContentContainer));
+ Assert.Equal(1, Grid.GetRow(tabHeaderContainer));
+ }
+
+ [Fact]
+ public void TabBarPlacement6()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Fill;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContentContainer = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.Equal(0, Grid.GetRow(tabContentContainer));
+ Assert.Equal(1, Grid.GetRow(tabHeaderContainer));
+ }
+
+ [Fact]
+ public void TabBarPlacement10()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContentContainer = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.Equal(0, Grid.GetRow(tabContentContainer));
+ Assert.Equal(1, Grid.GetRow(tabHeaderContainer));
+ }
+
+ [Fact]
+ public void TabBarPlacement12()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Top;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContentContainer = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.Equal(0, Grid.GetRow(tabContentContainer));
+ Assert.Equal(1, Grid.GetRow(tabHeaderContainer));
+ }
+
+ [Fact]
+ public void TabBarPlacement15()
+ {
+ var tabView = new SfTabView();
+ var tabItems = PopulateLabelItemsCollection();
+
+ tabView.Items = tabItems;
+ tabView.TabBarPlacement = TabBarPlacement.Bottom;
+ tabView.IndicatorPlacement = TabIndicatorPlacement.Fill;
+ tabView.FlowDirection = FlowDirection.RightToLeft;
+
+ var tabHeaderContainer = GetPrivateField(tabView, "_tabHeaderContainer") as SfTabBar;
+ var tabContentContainer = GetPrivateField(tabView, "_tabContentContainer") as SfHorizontalContent;
+ Assert.Equal(0, Grid.GetRow(tabContentContainer));
+ Assert.Equal(1, Grid.GetRow(tabHeaderContainer));
+ }
+
+ #endregion
+
#region Center Button
[Fact]
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DatePickerUnitTest.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DatePickerUnitTest.cs
index 038f19f2..7ea4ac25 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DatePickerUnitTest.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DatePickerUnitTest.cs
@@ -393,6 +393,125 @@ public void DatePicker_RelativePosition_GetAndSet(PickerRelativePosition positio
Assert.Equal(position, actualValue);
}
+ [Fact]
+ public void HeaderTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfDatePicker picker = new SfDatePicker();
+
+ picker.HeaderView.Height = 50;
+ picker.HeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Header Content" };
+ });
+
+ Assert.NotNull(picker.HeaderTemplate);
+ }
+
+ [Fact]
+ public void HeaderTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfDatePicker picker = new SfDatePicker();
+
+ Assert.Null(picker.HeaderTemplate);
+
+ picker.HeaderView.Height = 50;
+ picker.HeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Header Content" };
+ });
+
+ Assert.NotNull(picker.HeaderTemplate);
+ }
+
+ [Fact]
+ public void ColumnHeaderTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfDatePicker picker = new SfDatePicker();
+
+ picker.ColumnHeaderView.Height = 50;
+ picker.ColumnHeaderTemplate = new DataTemplate(() =>
+ {
+
+ var grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = 5
+ };
+
+ var label = new Label
+ {
+ Text = "Column Header Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ grid.Add(label);
+ return grid;
+ });
+
+ Assert.NotNull(picker.ColumnHeaderTemplate);
+ }
+
+ [Fact]
+ public void ColumnHeaderTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfDatePicker picker = new SfDatePicker();
+
+ Assert.Null(picker.ColumnHeaderTemplate);
+
+ picker.ColumnHeaderView.Height = 50;
+ picker.ColumnHeaderTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = 5
+ };
+
+ var label = new Label
+ {
+ Text = "Column Header Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ grid.Add(label);
+ return grid;
+ });
+
+ Assert.NotNull(picker.ColumnHeaderTemplate);
+ }
+
+ [Fact]
+ public void FooterTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfDatePicker picker = new SfDatePicker();
+
+ picker.FooterView.Height = 50;
+ picker.FooterTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Footer Content" };
+ });
+
+ Assert.NotNull(picker.FooterTemplate);
+ }
+
+ [Fact]
+ public void FooterTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfDatePicker picker = new SfDatePicker();
+
+ Assert.Null(picker.FooterTemplate);
+
+ picker.FooterView.Height = 50;
+ picker.FooterTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Footer Content" };
+ });
+
+ Assert.NotNull(picker.FooterTemplate);
+ }
+
[Fact]
public void SelectionChangedCommand_Execute_ReturnsTrue()
{
@@ -1684,7 +1803,7 @@ public void GetDays_SingleDigitFormat_ReturnsSingleDigitDays()
[InlineData(new string[] { "01", "02", "03", "04", "05" }, "dd", 3, 2)]
[InlineData(new string[] { "01", "03", "05", "07", "09" }, "dd", 4, 2)]
[InlineData(new string[] { "01", "02", "03", "04", "05" }, "dd", 10, 4)]
- [InlineData(new string[] {}, "dd", 5, -1)]
+ [InlineData(new string[] { }, "dd", 5, -1)]
[InlineData(new string[] { "1", "2", "3", "4", "5" }, "d", 3, 2)]
public void GetDayIndex_ReturnsCorrectIndex(string[] days, string format, int day, int exceptedValue)
{
@@ -1697,10 +1816,10 @@ public void GetDayIndex_ReturnsCorrectIndex(string[] days, string format, int da
[InlineData(new string[] { "2020", "2021", "2022", "2023", "2024" }, 2022, 2)]
[InlineData(new string[] { "2020", "2022", "2024", "2026", "2028" }, 2023, 2)]
[InlineData(new string[] { "2020", "2021", "2022", "2023", "2024" }, 2030, 4)]
- [InlineData(new string[] {}, 2022, -1)]
+ [InlineData(new string[] { }, 2022, -1)]
public void GetYearIndex_ReturnsCorrectIndex(string[] years, int year, int exceptedValue)
{
- var observedYears = new ObservableCollection (years);
+ var observedYears = new ObservableCollection(years);
int index = DatePickerHelper.GetYearIndex(observedYears, year);
Assert.Equal(exceptedValue, index);
}
@@ -1709,7 +1828,7 @@ public void GetYearIndex_ReturnsCorrectIndex(string[] years, int year, int excep
[InlineData(new string[] { "01", "02", "03", "04", "05" }, "MM", 3, 2)]
[InlineData(new string[] { "01", "03", "05", "07", "09" }, "MM", 4, 2)]
[InlineData(new string[] { "01", "02", "03", "04", "05" }, "MM", 10, 4)]
- [InlineData(new string[] {}, "MM", 5, -1)]
+ [InlineData(new string[] { }, "MM", 5, -1)]
[InlineData(new string[] { "1", "2", "3", "4", "5" }, "M", 3, 2)]
public void GetMonthIndex_ReturnsCorrectIndex(string[] months, string format, int month, int exceptedValue)
{
@@ -2711,5 +2830,299 @@ public void GenerateYearColumn_WithYearInterval_ReturnsCorrectColumn()
}
#endregion
+
+ #region PopupSizeFeature
+
+ [Fact]
+ public void DatePicker_PopupSize1()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ double expectedWidth = 100;
+ double expectedHeight = 200;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenPopupSizeIsNotSet()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ double expectedWidth = 300;
+ double expectedHeight = 240;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenPopupSizeOnPropertyChange()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ double expectedWidth = 50;
+ double expectedHeight = 20;
+
+ sfDatePicker.PopupWidth = 100;
+ sfDatePicker.PopupHeight = 200;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenHeaderHeightProvided_PopupSizeProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ headerView.Height = 50;
+ sfDatePicker.HeaderView = headerView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 400;
+
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenHeaderHeightProvided_PopupSizeNotProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ headerView.Height = 50;
+ sfDatePicker.HeaderView = headerView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 290;
+
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenColumnHeaderProvided_PopupSizeProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ DatePickerColumnHeaderView columnHeaderView = new DatePickerColumnHeaderView();
+ columnHeaderView.Height = 50;
+ sfDatePicker.ColumnHeaderView = columnHeaderView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 400;
+
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenColumnHeaderProvided_PopupSizeNotProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ DatePickerColumnHeaderView columnHeaderView = new DatePickerColumnHeaderView();
+ columnHeaderView.Height = 50;
+ sfDatePicker.ColumnHeaderView = columnHeaderView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 290;
+
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_PopupSizeProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ sfDatePicker.ItemHeight = 10;
+ double expectedWidth = 200;
+ double expectedHeight = 640;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_PopupSizeNotProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ sfDatePicker.ItemHeight = 10;
+ double expectedWidth = 200;
+ double expectedHeight = 50;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_ItemsLessThanFive()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ sfDatePicker.ItemHeight = 10;
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 30;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_ItemsGreaterThanFive()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ sfDatePicker.ItemHeight = 10;
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 50;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenFooterHeightProvided_PopupSizeProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ PickerFooterView pickerFooterView = new PickerFooterView();
+ pickerFooterView.Height = 50;
+ sfDatePicker.FooterView = pickerFooterView;
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 50;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenFooterHeightProvided_PopupSizeNotProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ PickerFooterView pickerFooterView = new PickerFooterView();
+ pickerFooterView.Height = 50;
+ sfDatePicker.FooterView = pickerFooterView;
+ double expectedWidth = 200;
+ double expectedHeight = 290;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenAllHeaderHeightProvided_PopupSizeProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ DatePickerColumnHeaderView pickerColumnHeaderView = new DatePickerColumnHeaderView();
+ PickerFooterView footerView = new PickerFooterView();
+ sfDatePicker.ItemHeight = 10;
+ headerView.Height = 50;
+ pickerColumnHeaderView.Height = 50;
+ footerView.Height = 50;
+ sfDatePicker.HeaderView = headerView;
+ sfDatePicker.ColumnHeaderView = pickerColumnHeaderView;
+ sfDatePicker.FooterView = footerView;
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 50 + 50 + 50 + 50;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenAllHeaderHeightProvided_PopupSizeNotProvided()
+ {
+ SfDatePicker sfDatePicker = new SfDatePicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ DatePickerColumnHeaderView pickerColumnHeaderView = new DatePickerColumnHeaderView();
+ PickerFooterView footerView = new PickerFooterView();
+ headerView.Height = 50;
+ pickerColumnHeaderView.Height = 50;
+ footerView.Height = 50;
+ sfDatePicker.HeaderView = headerView;
+ sfDatePicker.ColumnHeaderView = pickerColumnHeaderView;
+ sfDatePicker.FooterView = footerView;
+ double expectedWidth = 200;
+ double expectedHeight = 50 + 50 + 50;
+ sfDatePicker.PopupWidth = expectedWidth;
+ sfDatePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDatePicker.PopupWidth;
+ double actualHeight = sfDatePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ #endregion
}
}
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DateTimePickerUnitTest.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DateTimePickerUnitTest.cs
index 69af4cc4..046999f0 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DateTimePickerUnitTest.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/DateTimePickerUnitTest.cs
@@ -438,6 +438,122 @@ public void DateTimePicker_RelativePosition_GetAndSet(PickerRelativePosition pos
Assert.Equal(position, actualValue);
}
+ [Fact]
+ public void HeaderTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfDateTimePicker picker = new SfDateTimePicker();
+
+ picker.HeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Header Content" };
+ });
+
+ Assert.NotNull(picker.HeaderTemplate);
+ }
+
+ [Fact]
+ public void HeaderTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfDateTimePicker picker = new SfDateTimePicker();
+
+ Assert.Null(picker.HeaderTemplate);
+
+ picker.HeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Header Content" };
+ });
+
+ Assert.NotNull(picker.HeaderTemplate);
+ }
+
+ [Fact]
+ public void ColumnHeaderTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfDateTimePicker picker = new SfDateTimePicker();
+
+ picker.ColumnHeaderView.Height = 50;
+ picker.ColumnHeaderTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = 5
+ };
+
+ var label = new Label
+ {
+ Text = "Column Header Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ grid.Add(label);
+ return grid;
+ });
+
+ Assert.NotNull(picker.ColumnHeaderTemplate);
+ }
+
+ [Fact]
+ public void ColumnHeaderTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfDateTimePicker picker = new SfDateTimePicker();
+
+ Assert.Null(picker.ColumnHeaderTemplate);
+
+ picker.ColumnHeaderView.Height = 50;
+ picker.ColumnHeaderTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = 5
+ };
+
+ var label = new Label
+ {
+ Text = "Column Header Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ grid.Add(label);
+ return grid;
+ });
+
+ Assert.NotNull(picker.ColumnHeaderTemplate);
+ }
+
+ [Fact]
+ public void FooterTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfDateTimePicker picker = new SfDateTimePicker();
+
+ picker.FooterView.Height = 50;
+ picker.FooterTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Footer Content" };
+ });
+
+ Assert.NotNull(picker.FooterTemplate);
+ }
+
+ [Fact]
+ public void FooterTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfDateTimePicker picker = new SfDateTimePicker();
+
+ Assert.Null(picker.FooterTemplate);
+
+ picker.FooterView.Height = 50;
+ picker.FooterTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Footer Content" };
+ });
+
+ Assert.NotNull(picker.FooterTemplate);
+ }
+
[Fact]
public void SelectionChangedCommand_Execute_ReturnsTrue()
{
@@ -1493,5 +1609,272 @@ public void SelectionChangedInvoked()
}
#endregion
+
+ #region PopupSizeFeature
+
+ [Fact]
+ public void DateTimePicker_PopupSize1()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ double expectedWidth = 100;
+ double expectedHeight = 200;
+ sfDateTimePicker.PopupWidth = expectedWidth;
+ sfDateTimePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DateTimePicker_PopupSize_WhenPopupSizeIsNotSet()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ double expectedWidth = 300;
+ double expectedHeight = 290;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DateTimePicker_PopupSize_WhenPopupSizeOnPropertyChange()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ double expectedWidth = 50;
+ double expectedHeight = 20;
+
+ sfDateTimePicker.PopupWidth = 100;
+ sfDateTimePicker.PopupHeight = 200;
+ sfDateTimePicker.PopupWidth = expectedWidth;
+ sfDateTimePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DateTimePicker_PopupSize_WhenHeaderHeightProvided_PopupSizeProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ DateTimePickerHeaderView headerView = new DateTimePickerHeaderView();
+ headerView.Height = 50;
+ sfDateTimePicker.HeaderView = headerView;
+
+ double expectedWidth = 300;
+ double expectedHeight = 290;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DateTimePicker_PopupSize_WhenHeaderHeightProvided_PopupSizeNotProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ DateTimePickerHeaderView headerView = new DateTimePickerHeaderView();
+ headerView.Height = 50;
+ sfDateTimePicker.HeaderView = headerView;
+
+ double expectedWidth = 300;
+ double expectedHeight = 290;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DateTimePicker_PopupSize_WhenColumnHeaderProvided_PopupSizeProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ DateTimePickerColumnHeaderView columnHeaderView = new DateTimePickerColumnHeaderView();
+ columnHeaderView.Height = 50;
+ sfDateTimePicker.ColumnHeaderView = columnHeaderView;
+
+ double expectedWidth = 300;
+ double expectedHeight = 300;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenColumnHeaderProvided_PopupSizeNotProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ DateTimePickerColumnHeaderView columnHeaderView = new DateTimePickerColumnHeaderView();
+ columnHeaderView.Height = 50;
+ sfDateTimePicker.ColumnHeaderView = columnHeaderView;
+
+ double expectedWidth = 300;
+ double expectedHeight = 300;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_PopupSizeProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ sfDateTimePicker.ItemHeight = 10;
+
+ double expectedWidth = 300;
+ double expectedHeight = 140;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_PopupSizeNotProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ sfDateTimePicker.ItemHeight = 10;
+ double expectedWidth = 300;
+ double expectedHeight = 140;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_ItemsLessThanFive()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ sfDateTimePicker.ItemHeight = 10;
+ double expectedWidth = 300;
+ double expectedHeight = 140;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_ItemsGreaterThanFive()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ sfDateTimePicker.ItemHeight = 10;
+ double expectedWidth = 300;
+ double expectedHeight = 140;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenFooterHeightProvided_PopupSizeProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ PickerFooterView pickerFooterView = new PickerFooterView();
+ pickerFooterView.Height = 50;
+ sfDateTimePicker.FooterView = pickerFooterView;
+ double expectedWidth = 300;
+ double expectedHeight = 290 + 50;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenFooterHeightProvided_PopupSizeNotProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ PickerFooterView pickerFooterView = new PickerFooterView();
+ pickerFooterView.Height = 50;
+ sfDateTimePicker.FooterView = pickerFooterView;
+ double expectedWidth = 300;
+ double expectedHeight = 340;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenAllHeaderHeightProvided_PopupSizeProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ DateTimePickerHeaderView headerView = new DateTimePickerHeaderView();
+ DateTimePickerColumnHeaderView pickerColumnHeaderView = new DateTimePickerColumnHeaderView();
+ PickerFooterView footerView = new PickerFooterView();
+ sfDateTimePicker.ItemHeight = 10;
+ headerView.Height = 50;
+ pickerColumnHeaderView.Height = 50;
+ footerView.Height = 50;
+ sfDateTimePicker.HeaderView = headerView;
+ sfDateTimePicker.ColumnHeaderView = pickerColumnHeaderView;
+ sfDateTimePicker.FooterView = footerView;
+ double expectedWidth = 300;
+ double expectedHeight = 200;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenAllHeaderHeightProvided_PopupSizeNotProvided()
+ {
+ SfDateTimePicker sfDateTimePicker = new SfDateTimePicker();
+ DateTimePickerHeaderView headerView = new DateTimePickerHeaderView();
+ DateTimePickerColumnHeaderView pickerColumnHeaderView = new DateTimePickerColumnHeaderView();
+ PickerFooterView footerView = new PickerFooterView();
+ headerView.Height = 50;
+ pickerColumnHeaderView.Height = 50;
+ footerView.Height = 50;
+ sfDateTimePicker.HeaderView = headerView;
+ sfDateTimePicker.ColumnHeaderView = pickerColumnHeaderView;
+ sfDateTimePicker.FooterView = footerView;
+ double expectedWidth = 300;
+ double expectedHeight = 350;
+
+ double actualWidth = sfDateTimePicker.PopupWidth;
+ double actualHeight = sfDateTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ #endregion
}
}
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerBaseMethods.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerBaseMethods.cs
index 95a226de..5309dedb 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerBaseMethods.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerBaseMethods.cs
@@ -110,6 +110,52 @@ public void TestAddOrRemoveHeaderLayout_WhenRemove()
Assert.Null(pickerStackLayout.Children.FirstOrDefault(c => c is HeaderLayout));
}
+ [Fact]
+ public void TestAddOrRemoveHeaderLayout_WhenPickerHeaderTemplate()
+ {
+ var picker = new SfPicker();
+ picker.HeaderView.Height = 40;
+
+ Label expectedValue = new Label { Text = "Header Content" };
+ picker.HeaderTemplate = new DataTemplate(() =>
+ {
+ return expectedValue;
+ });
+ ;
+
+ SetPrivateField(picker, "_headerLayout", null);
+
+ ObservableCollection cityName = new ObservableCollection();
+ cityName.Add("Chennai");
+ cityName.Add("Mumbai");
+ cityName.Add("Delhi");
+ cityName.Add("Kolkata");
+ PickerColumn pickerColumn = new PickerColumn()
+ {
+ HeaderText = "Select City",
+ ItemsSource = cityName,
+ SelectedIndex = 1,
+ };
+ picker.Columns.Add(pickerColumn);
+
+ InvokePrivateMethod(picker, "AddOrRemoveHeaderLayout");
+
+ PickerBase pickerBase = (PickerBase)picker;
+
+ var layout = GetPrivateField(pickerBase, "_headerLayout");
+ HeaderLayout headerLayout = new HeaderLayout(pickerBase);
+
+ if (layout != null)
+ {
+ headerLayout = (HeaderLayout)layout;
+ }
+ Label actualValue = (Label)headerLayout.Children[0];
+
+ Assert.NotNull(picker.HeaderTemplate);
+ Assert.Equal(expectedValue, actualValue);
+ picker.Columns.Clear();
+ }
+
[Fact]
public void TestAddOrRemoveFooterLayout_WhenAdd()
{
@@ -134,6 +180,56 @@ public void TestAddOrRemoveFooterLayout_WhenRemove()
Assert.Null(pickerStackLayout.Children.FirstOrDefault(c => c is FooterLayout));
}
+ [Fact]
+ public void TestAddOrRemoveFooterLayout_WhenPickerFooterTemplate()
+ {
+ var picker = new SfPicker();
+ picker.FooterView.Height = 40;
+
+ Label expectedValue = new Label { Text = "Footer Content" };
+ picker.FooterTemplate = new DataTemplate(() =>
+ {
+ return expectedValue;
+ });
+
+ SetPrivateField(picker, "_footerLayout", null);
+
+ ObservableCollection cityName = new ObservableCollection();
+ cityName.Add("Chennai");
+ cityName.Add("Mumbai");
+ cityName.Add("Delhi");
+ cityName.Add("Kolkata");
+ cityName.Add("Bangalore");
+ cityName.Add("Hyderabad");
+ cityName.Add("Pune");
+ PickerColumn pickerColumn = new PickerColumn()
+ {
+ HeaderText = "Select City",
+ ItemsSource = cityName,
+ SelectedIndex = 1,
+ };
+ picker.Columns.Add(pickerColumn);
+
+ InvokePrivateMethod(picker, "AddOrRemoveFooterLayout");
+
+ PickerBase pickerBase = (PickerBase)picker;
+
+ var layout = GetPrivateField(pickerBase, "_footerLayout");
+ FooterLayout footerLayout = new FooterLayout(pickerBase);
+
+ if (layout != null)
+ {
+ footerLayout = (FooterLayout)layout;
+ }
+ Label actualValue = (Label)footerLayout.Children[0];
+
+ Assert.NotNull(pickerBase.FooterTemplate);
+ Assert.Equal(expectedValue, actualValue);
+
+ Assert.Equal(pickerColumn.SelectedIndex, picker.Columns[0].SelectedIndex);
+ picker.Columns.Clear();
+ }
+
[Theory]
[MemberData(nameof(BrushColorData))]
public void ToColor_ReturnsColor_WhenCalled(Brush brush, Color expectedColor)
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerUnitTest.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerUnitTest.cs
index 09033623..82ef64fb 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerUnitTest.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/PickerUnitTest.cs
@@ -42,7 +42,7 @@ public void PickerColumn_Width_GetAndSet(int expectedValue)
[InlineData("Red", "Blue", "Yellow")]
[InlineData(1, 2, 3)]
[InlineData(0.1, 0.2, 0.3)]
- [InlineData((float)0.1 ,(float)0.2, (float)0.3)]
+ [InlineData((float)0.1, (float)0.2, (float)0.3)]
[InlineData("String", 1, 0.2)]
public void PickerColumn_ItemSource_GetAndSet(object value, object value1, object value2)
{
@@ -454,6 +454,135 @@ public void Picker_RelativePosition_GetAndSet(PickerRelativePosition position)
Assert.Equal(position, actualValue);
}
+ [Theory]
+ [InlineData(false)]
+ [InlineData(true)]
+ public void Picker_EnableLooping_GetAndSet(bool expectedValue)
+ {
+ SfPicker picker = new SfPicker();
+
+ picker.EnableLooping = expectedValue;
+ bool actualValue = picker.EnableLooping;
+
+ Assert.Equal(expectedValue, actualValue);
+ }
+
+ [Fact]
+ public void HeaderTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfPicker picker = new SfPicker();
+
+ picker.HeaderView.Height = 50;
+ picker.HeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Header Content" };
+ });
+
+ Assert.NotNull(picker.HeaderTemplate);
+ }
+
+ [Fact]
+ public void HeaderTemplate_GetAndSet_UsingDataTemplate_WhencCalledDynamic()
+ {
+ SfPicker picker = new SfPicker();
+
+ Assert.Null(picker.HeaderTemplate);
+
+ picker.HeaderView.Height = 50;
+ picker.HeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Header Content" };
+ });
+
+ Assert.NotNull(picker.HeaderTemplate);
+ }
+
+ [Fact]
+ public void ColumnHeaderTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfPicker picker = new SfPicker();
+
+ picker.ColumnHeaderView.Height = 50;
+
+ ObservableCollection cityName = new ObservableCollection();
+ cityName.Add("Chennai");
+ cityName.Add("Mumbai");
+ cityName.Add("Delhi");
+ cityName.Add("Kolkata");
+ PickerColumn pickerColumn = new PickerColumn()
+ {
+ HeaderText = "Select City",
+ ItemsSource = cityName,
+ SelectedIndex = 1,
+ };
+ picker.Columns.Add(pickerColumn);
+
+ picker.ColumnHeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Column Header Content" };
+ });
+
+ Assert.NotNull(picker.ColumnHeaderTemplate);
+ }
+
+ [Fact]
+ public void ColumnHeaderTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfPicker picker = new SfPicker();
+
+ Assert.Null(picker.ColumnHeaderTemplate);
+
+ picker.ColumnHeaderView.Height = 50;
+ ObservableCollection cityName = new ObservableCollection();
+ cityName.Add("Chennai");
+ cityName.Add("Mumbai");
+ cityName.Add("Delhi");
+ cityName.Add("Kolkata");
+ PickerColumn pickerColumn = new PickerColumn()
+ {
+ HeaderText = "Select City",
+ ItemsSource = cityName,
+ SelectedIndex = 1,
+ };
+ picker.Columns.Add(pickerColumn);
+ picker.ColumnHeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Column Header Content" };
+ });
+
+ Assert.NotNull(picker.ColumnHeaderTemplate);
+ }
+
+ [Fact]
+ public void FooterTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfPicker picker = new SfPicker();
+
+ picker.FooterView.Height = 50;
+ picker.FooterTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Footer Content" };
+ });
+
+ Assert.NotNull(picker.FooterTemplate);
+ }
+
+ [Fact]
+ public void FooterTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfPicker picker = new SfPicker();
+
+ Assert.Null(picker.FooterTemplate);
+
+ picker.FooterView.Height = 50;
+ picker.FooterTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Footer Content" };
+ });
+
+ Assert.NotNull(picker.FooterTemplate);
+ }
+
[Fact]
public void AcceptCommand_Execute_ReturnsTrue()
{
@@ -1064,18 +1193,6 @@ public void ColumnHeaderSettings_TextStyle_GetAndSet_PickerTextStyle_FontAutoSca
Assert.Equal(expectedValue, actualValue.FontAutoScalingEnabled);
}
- [Fact]
- public void ColumnHeaderSettings_GetAndSet_Null()
- {
- SfPicker picker = new SfPicker();
-
-#pragma warning disable CS8625
- picker.ColumnHeaderView = null;
-#pragma warning restore CS8625
-
- Assert.Null(picker.ColumnHeaderView);
- }
-
#endregion
#region Footer Settings Public Properties
@@ -1391,5 +1508,969 @@ public void SelectionChangedInvoked()
}
#endregion
+
+ #region PopupSizeFeature
+
+ [Fact]
+ public void Picker_PopupSize1()
+ {
+ SfPicker sfPicker = new SfPicker();
+ double expectedPopupWidth = 100;
+ double expectedPopupHeight = 200;
+ sfPicker.PopupWidth = expectedPopupWidth;
+ sfPicker.PopupHeight = expectedPopupHeight;
+
+ double actualPopupWidth = sfPicker.PopupWidth;
+ double actualPopupHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedPopupWidth, actualPopupWidth);
+ Assert.Equal(expectedPopupHeight, actualPopupHeight);
+ }
+
+
+ [Fact]
+ public void Picker_PopupSize_WhenPopupSizeIsNotSet()
+ {
+ SfPicker sfPicker = new SfPicker();
+ double expectedWidth = 0;
+ double expectedHeight = 0;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+
+ [Fact]
+ public void Picker_PopupSize_WhenPopupSizeOnPropertyChange()
+ {
+ SfPicker sfPicker = new SfPicker();
+ double expectedWidth = 50;
+ double expectedHeight = 20;
+
+ sfPicker.PopupWidth = 100;
+ sfPicker.PopupHeight = 200;
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenHeaderHeightProvided_PopupSizeProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ headerView.Height = 50;
+ sfPicker.HeaderView = headerView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 400;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenHeaderHeightProvided_PopupSizeNotProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ headerView.Height = 50;
+ sfPicker.HeaderView = headerView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 290;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenColumnHeaderProvided_PopupSizeProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ PickerColumnHeaderView columnHeaderView = new PickerColumnHeaderView();
+ columnHeaderView.Height = 50;
+ sfPicker.ColumnHeaderView = columnHeaderView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 400;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenColumnHeaderProvided_PopupSizeNotProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ PickerColumnHeaderView columnHeaderView = new PickerColumnHeaderView();
+ columnHeaderView.Height = 50;
+ sfPicker.ColumnHeaderView = columnHeaderView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 290;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenItemHeightProvided_PopupSizeProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ sfPicker.ItemHeight = 10;
+
+ ObservableCollection dataSource = new ObservableCollection()
+ {
+ "Pink", "Green", "Blue", "Yellow", "Orange",
+ };
+
+ PickerColumn pickerColumn = new PickerColumn();
+ pickerColumn.ItemsSource = dataSource;
+ sfPicker.Columns = new ObservableCollection() { pickerColumn };
+
+ double expectedWidth = 200;
+ double expectedHeight = 640;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenItemHeightProvided_PopupSizeNotProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ sfPicker.ItemHeight = 10;
+
+ ObservableCollection dataSource = new ObservableCollection()
+ {
+ "Pink", "Green", "Blue", "Yellow", "Orange",
+ };
+
+ PickerColumn pickerColumn = new PickerColumn();
+ pickerColumn.ItemsSource = dataSource;
+ sfPicker.Columns = new ObservableCollection() { pickerColumn };
+
+ double expectedWidth = 200;
+ double expectedHeight = 50;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenItemHeightProvided_ItemsLessThanFive()
+ {
+ SfPicker sfPicker = new SfPicker();
+ sfPicker.ItemHeight = 10;
+ ObservableCollection dataSource = new ObservableCollection()
+ {
+ "Pink", "Green", "Blue",
+ };
+ PickerColumn pickerColumn = new PickerColumn();
+ pickerColumn.ItemsSource = dataSource;
+ sfPicker.Columns = new ObservableCollection() { pickerColumn };
+
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 30;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenItemHeightProvided_ItemsGreaterThanFive()
+ {
+ SfPicker sfPicker = new SfPicker();
+ sfPicker.ItemHeight = 10;
+ ObservableCollection dataSource = new ObservableCollection()
+ {
+ "Pink", "Green", "Blue", "Yellow", "Orange", "White", "Black"
+ };
+ PickerColumn pickerColumn = new PickerColumn();
+ pickerColumn.ItemsSource = dataSource;
+ sfPicker.Columns = new ObservableCollection() { pickerColumn };
+
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 50;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenFooterHeightProvided_PopupSizeProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ PickerFooterView pickerFooterView = new PickerFooterView();
+ pickerFooterView.Height = 50;
+ sfPicker.FooterView = pickerFooterView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 50;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenFooterHeightProvided_PopupSizeNotProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ PickerFooterView pickerFooterView = new PickerFooterView();
+ pickerFooterView.Height = 50;
+ sfPicker.FooterView = pickerFooterView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 290;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenAllHeaderHeightProvided_PopupSizeProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ PickerColumnHeaderView pickerColumnHeaderView = new PickerColumnHeaderView();
+ PickerFooterView footerView = new PickerFooterView();
+ sfPicker.ItemHeight = 10;
+ ObservableCollection dataSource = new ObservableCollection()
+ {
+ "Pink", "Green", "Blue", "Yellow", "Orange",
+ };
+ PickerColumn pickerColumn = new PickerColumn();
+ pickerColumn.ItemsSource = dataSource;
+ sfPicker.Columns = new ObservableCollection() { pickerColumn };
+ headerView.Height = 50;
+ pickerColumnHeaderView.Height = 50;
+ footerView.Height = 50;
+ sfPicker.HeaderView = headerView;
+ sfPicker.ColumnHeaderView = pickerColumnHeaderView;
+ sfPicker.FooterView = footerView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 50 + 50 + 50 + 50;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void Picker_PopupSize_WhenAllHeaderHeightProvided_PopupSizeNotProvided()
+ {
+ SfPicker sfPicker = new SfPicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ PickerColumnHeaderView pickerColumnHeaderView = new PickerColumnHeaderView();
+ PickerFooterView footerView = new PickerFooterView();
+ sfPicker.ItemHeight = 10;
+ ObservableCollection dataSource = new ObservableCollection()
+ {
+ "Pink", "Green", "Blue", "Yellow", "Orange",
+ };
+ PickerColumn pickerColumn = new PickerColumn();
+ pickerColumn.ItemsSource = dataSource;
+ sfPicker.Columns = new ObservableCollection() { pickerColumn };
+ headerView.Height = 50;
+ pickerColumnHeaderView.Height = 50;
+ footerView.Height = 50;
+ sfPicker.HeaderView = headerView;
+ sfPicker.ColumnHeaderView = pickerColumnHeaderView;
+ sfPicker.FooterView = footerView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 50 + 50 + 50;
+
+ sfPicker.PopupWidth = expectedWidth;
+ sfPicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfPicker.PopupWidth;
+ double actualHeight = sfPicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ #endregion
+
+ #region PickerScrollView Tests
+
+ [Fact]
+ public void UpdateSelectedIndex_WithSelectedIndex_PreservesSelectedIndex()
+ {
+ var picker = new SfPicker();
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = new ObservableCollection { "Item1", "Item2", "Item3", "Item4", "Item5" },
+ SelectedIndex = 2
+ };
+
+ picker.Columns.Add(pickerColumn);
+ Assert.Equal(2, pickerColumn.SelectedIndex);
+ Assert.Equal("Item3", pickerColumn.SelectedItem);
+ }
+
+ [Fact]
+ public void UpdateSelectedIndex_NegativeSelectedIndex_ResetsSelection()
+ {
+ var picker = new SfPicker();
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = new ObservableCollection { "Item1", "Item2", "Item3", "Item4", "Item5" },
+ SelectedIndex = 2
+ };
+
+ picker.Columns.Add(pickerColumn);
+ pickerColumn.SelectedIndex = -1;
+ Assert.Equal(-1, pickerColumn.SelectedIndex);
+ Assert.Null(pickerColumn.SelectedItem);
+ }
+
+ [Fact]
+ public void UpdateSelectedIndex_LoopingEnabledWithOutOfRangeIndex_LimitsToValidIndex()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = true
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = new ObservableCollection { "Item1", "Item2", "Item3", "Item4", "Item5" },
+ SelectedIndex = 2
+ };
+
+ picker.Columns.Add(pickerColumn);
+ pickerColumn.SelectedIndex = 10;
+
+ Assert.Equal(10, pickerColumn.SelectedIndex); //// Return last index 4, but picker container as null so selected index not update.
+ }
+
+ [Fact]
+ public void UpdateSelectedIndex_EmptyItemsSource_ReturnsNegativeOne()
+ {
+ var picker = new SfPicker();
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = new ObservableCollection(),
+ SelectedIndex = 0
+ };
+
+ picker.Columns.Add(pickerColumn);
+ Assert.Equal(0, pickerColumn.SelectedIndex);
+ }
+
+ [Fact]
+ public void Picker_ItemHeightChange_UpdatesLayout()
+ {
+ var picker = new SfPicker();
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = new ObservableCollection { "Item1", "Item2", "Item3", "Item4", "Item5" },
+ SelectedIndex = 2
+ };
+
+ picker.Columns.Add(pickerColumn);
+ double initialItemHeight = picker.ItemHeight;
+ picker.ItemHeight = 60;
+ Assert.Equal(60, picker.ItemHeight);
+ Assert.Equal(2, pickerColumn.SelectedIndex);
+ }
+
+ [Fact]
+ public void Picker_LoopingChange_MaintainsSelectedIndex()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = false
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = new ObservableCollection { "Item1", "Item2", "Item3", "Item4", "Item5" },
+ SelectedIndex = 2
+ };
+
+ picker.Columns.Add(pickerColumn);
+ picker.EnableLooping = true;
+ Assert.True(picker.EnableLooping);
+ Assert.Equal(2, pickerColumn.SelectedIndex);
+ }
+
+ [Theory]
+ [InlineData(-1, null)]
+ [InlineData(0, "Item1")]
+ [InlineData(2, "Item3")]
+ [InlineData(4, "Item5")]
+ public void Picker_SelectedIndexChange_UpdatesSelectedItem(int selectedIndex, string? expectedItem)
+ {
+ var picker = new SfPicker();
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = new ObservableCollection { "Item1", "Item2", "Item3", "Item4", "Item5" },
+ SelectedIndex = 0
+ };
+
+ picker.Columns.Add(pickerColumn);
+ pickerColumn.SelectedIndex = selectedIndex;
+ Assert.Equal(selectedIndex, pickerColumn.SelectedIndex);
+ Assert.Equal(expectedItem, pickerColumn.SelectedItem);
+ }
+
+ [Theory]
+ [InlineData(true)]
+ [InlineData(false)]
+ public void Picker_EnableLooping_UpdatesPickerBehavior(bool enableLooping)
+ {
+ var picker = new SfPicker();
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = new ObservableCollection { "Item1", "Item2", "Item3", "Item4", "Item5" },
+ SelectedIndex = 2
+ };
+
+ picker.Columns.Add(pickerColumn);
+ picker.EnableLooping = enableLooping;
+ Assert.Equal(enableLooping, picker.EnableLooping);
+ Assert.Equal(2, pickerColumn.SelectedIndex);
+ }
+
+ [Fact]
+ public void Picker_ViewportItemCountGreaterThanItemsCount_LoopingIsInvalid()
+ {
+ var picker = new SfPicker();
+ picker.EnableLooping = true;
+ picker.ItemHeight = 40;
+
+ //// Create a small collection that's likely less than viewport size.
+ var items = new ObservableCollection
+ {
+ "Item1",
+ "Item2"
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 0
+ };
+
+ picker.Columns.Add(pickerColumn);
+ double pickerHeight = 200;
+ double viewPortItemCount = pickerHeight / picker.ItemHeight;
+ int itemsCount = PickerHelper.GetItemsCount(pickerColumn.ItemsSource);
+ bool isLoopingEffectivelyValid = itemsCount > viewPortItemCount;
+ Assert.False(isLoopingEffectivelyValid);
+ Assert.True(picker.EnableLooping);
+ }
+
+ [Theory]
+ [InlineData(200, 40, 3)]
+ [InlineData(200, 40, 7)]
+ [InlineData(120, 40, 2)]
+ [InlineData(120, 40, 4)]
+ [InlineData(180, 30, 6)]
+ [InlineData(250, 50, 5)]
+ public void Picker_CheckViewportItemCountAffectsLoopingValidity(double pickerHeight, double itemHeight, int itemCount)
+ {
+ var picker = new SfPicker();
+ picker.EnableLooping = true;
+ picker.ItemHeight = itemHeight;
+
+ var items = new ObservableCollection();
+ for (int i = 1; i <= itemCount; i++)
+ {
+ items.Add($"Item{i}");
+ }
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 0
+ };
+ picker.Columns.Add(pickerColumn);
+
+ double viewPortItemCount = pickerHeight / itemHeight;
+ bool isLoopingEffectivelyValid = itemCount > viewPortItemCount;
+ if (itemCount > viewPortItemCount)
+ {
+ Assert.True(isLoopingEffectivelyValid);
+ }
+ else
+ {
+ Assert.False(isLoopingEffectivelyValid);
+ }
+
+ Assert.True(picker.EnableLooping);
+ }
+
+ [Fact]
+ public void Picker_LoopingEnabled_ScrollsToSameIndexAfterFullCycle()
+ {
+ var picker = new SfPicker();
+ picker.EnableLooping = true;
+ picker.ItemHeight = 40;
+ var items = new ObservableCollection
+ {
+ "Item1",
+ "Item2",
+ "Item3",
+ "Item4",
+ "Item5",
+ "Item6",
+ "Item7"
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 0
+ };
+
+ picker.Columns.Add(pickerColumn);
+
+ int initialIndex = pickerColumn.SelectedIndex;
+ int totalItems = items.Count;
+ for (int i = 0; i < totalItems; i++)
+ {
+ pickerColumn.SelectedIndex = (initialIndex + i + 1) % totalItems;
+ }
+
+ Assert.Equal(initialIndex, pickerColumn.SelectedIndex);
+ Assert.Equal(items[initialIndex], pickerColumn.SelectedItem);
+ }
+
+ [Fact]
+ public void Picker_SetColumnCount_AffectsViewportCalculation()
+ {
+ var picker = new SfPicker();
+ picker.EnableLooping = true;
+ picker.ItemHeight = 40;
+
+ //// Create multiple columns to potentially affect viewport calculations.
+ for (int col = 0; col < 3; col++)
+ {
+ var items = new ObservableCollection();
+ for (int i = 1; i <= 5; i++)
+ {
+ items.Add($"Column{col + 1}_Item{i}");
+ }
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 0
+ };
+ picker.Columns.Add(pickerColumn);
+ }
+
+ double pickerHeight = 200;
+ double viewPortItemCount = pickerHeight / picker.ItemHeight;
+ int itemsCount = PickerHelper.GetItemsCount(picker.Columns[0].ItemsSource);
+ bool isLoopingEffectivelyValid = itemsCount > viewPortItemCount;
+ //// With our simulated values, looping should be valid (5 items > viewport).
+ Assert.False(isLoopingEffectivelyValid);
+ Assert.Equal(3, picker.Columns.Count);
+ }
+
+ [Fact]
+ public void Picker_ItemHeightChange_AffectsViewportItemCount()
+ {
+ var picker = new SfPicker();
+ picker.EnableLooping = true;
+ picker.ItemHeight = 40;
+ var items = new ObservableCollection
+ {
+ "Item1",
+ "Item2",
+ "Item3",
+ "Item4",
+ "Item5"
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 2
+ };
+
+ picker.Columns.Add(pickerColumn);
+ double pickerHeight = 200;
+ double initialViewPortItemCount = pickerHeight / picker.ItemHeight;
+ bool initialLoopingValid = items.Count > initialViewPortItemCount;
+
+ picker.ItemHeight = 80;
+ double newViewPortItemCount = pickerHeight / picker.ItemHeight;
+ bool newLoopingValid = items.Count > newViewPortItemCount;
+
+ Assert.True(picker.EnableLooping);
+ Assert.True(initialViewPortItemCount > newViewPortItemCount);
+ Assert.Equal(items.Count > initialViewPortItemCount, initialLoopingValid);
+ Assert.Equal(items.Count > newViewPortItemCount, newLoopingValid);
+ }
+
+ [Fact]
+ public void Picker_ViewportItemCountLessThanItemsCount_LoopingIsValid()
+ {
+ var picker = new SfPicker();
+ picker.EnableLooping = true;
+ picker.ItemHeight = 40;
+
+ //// Create a larger collection that exceeds typical viewport.
+ var items = new ObservableCollection();
+ for (int i = 1; i <= 10; i++)
+ {
+ items.Add($"Item{i}");
+ }
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 2
+ };
+ picker.Columns.Add(pickerColumn);
+
+ double simulatedPickerHeight = 200;
+ double viewPortItemCount = simulatedPickerHeight / picker.ItemHeight;
+ int itemsCount = PickerHelper.GetItemsCount(pickerColumn.ItemsSource);
+ bool isLoopingEffectivelyValid = itemsCount > viewPortItemCount;
+ Assert.True(isLoopingEffectivelyValid);
+ Assert.True(picker.EnableLooping);
+ }
+
+ [Fact]
+ public void Picker_LoopingEnabledDynamically_UpdatesSelection()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = false,
+ ItemHeight = 40
+ };
+
+ var items = new ObservableCollection
+ {
+ "Item1", "Item2", "Item3", "Item4", "Item5", "Item6", "Item7"
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 6
+ };
+
+ picker.Columns.Add(pickerColumn);
+ picker.EnableLooping = true;
+ int originalSelectedIndex = pickerColumn.SelectedIndex;
+ pickerColumn.SelectedIndex = 0;
+ Assert.Equal(0, pickerColumn.SelectedIndex);
+ Assert.Equal("Item1", pickerColumn.SelectedItem);
+ }
+
+ [Fact]
+ public void Picker_UpdateItemsSource_WithLoopingEnabled_MaintainsValidSelection()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = true,
+ ItemHeight = 40
+ };
+
+ var initialItems = new ObservableCollection
+ {
+ "Item1", "Item2", "Item3", "Item4", "Item5"
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = initialItems,
+ SelectedIndex = 2
+ };
+
+ picker.Columns.Add(pickerColumn);
+ var newItems = new ObservableCollection
+ {
+ "NewItem1", "NewItem2", "NewItem3"
+ };
+
+ pickerColumn.ItemsSource = newItems;
+ Assert.True(pickerColumn.SelectedIndex >= 0 && pickerColumn.SelectedIndex < newItems.Count);
+ if (pickerColumn.SelectedIndex == 2)
+ {
+ Assert.Equal("NewItem3", pickerColumn.SelectedItem);
+ }
+ else
+ {
+ Assert.Equal(newItems[pickerColumn.SelectedIndex], pickerColumn.SelectedItem);
+ }
+ }
+
+ [Fact]
+ public void Picker_LoopingWithLargeItemCount_HandlesBoundaryConditions()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = true,
+ ItemHeight = 40
+ };
+
+ var items = new ObservableCollection();
+ for (int i = 1; i <= 50; i++)
+ {
+ items.Add($"Item{i}");
+ }
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 0
+ };
+
+ picker.Columns.Add(pickerColumn);
+ pickerColumn.SelectedIndex = 49;
+ Assert.Equal(49, pickerColumn.SelectedIndex);
+ Assert.Equal("Item50", pickerColumn.SelectedItem);
+
+ pickerColumn.SelectedIndex = 0;
+
+ Assert.Equal(0, pickerColumn.SelectedIndex);
+ Assert.Equal("Item1", pickerColumn.SelectedItem);
+ }
+
+ [Fact]
+ public void Picker_LoopingEnabledWithMultipleColumns_EachColumnScrollsIndependently()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = true,
+ ItemHeight = 40
+ };
+
+ var items1 = new ObservableCollection
+ {
+ "Red", "Green", "Blue"
+ };
+
+ var colorColumn = new PickerColumn
+ {
+ ItemsSource = items1,
+ SelectedIndex = 0,
+ HeaderText = "Color"
+ };
+
+ picker.Columns.Add(colorColumn);
+ var items2 = new ObservableCollection
+ {
+ "Circle", "Square", "Triangle", "Diamond"
+ };
+
+ var shapeColumn = new PickerColumn
+ {
+ ItemsSource = items2,
+ SelectedIndex = 0,
+ HeaderText = "Shape"
+ };
+
+ picker.Columns.Add(shapeColumn);
+ for (int i = 0; i < items1.Count; i++)
+ {
+ colorColumn.SelectedIndex = i % items1.Count;
+ }
+
+ shapeColumn.SelectedIndex = 2;
+
+ Assert.Equal(2, colorColumn.SelectedIndex);
+ Assert.Equal("Blue", colorColumn.SelectedItem);
+
+ Assert.Equal(2, shapeColumn.SelectedIndex);
+ Assert.Equal("Triangle", shapeColumn.SelectedItem);
+ }
+
+ [Fact]
+ public void Picker_DisableLoopingDuringScrolling_HandlesTransitionGracefully()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = true,
+ ItemHeight = 40
+ };
+
+ var items = new ObservableCollection
+ {
+ "Item1", "Item2", "Item3", "Item4", "Item5"
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 4
+ };
+
+ picker.Columns.Add(pickerColumn);
+ picker.EnableLooping = false;
+
+ Assert.Equal(4, pickerColumn.SelectedIndex);
+ Assert.Equal("Item5", pickerColumn.SelectedItem);
+ Assert.False(picker.EnableLooping);
+ }
+
+ [Fact]
+ public void Picker_EmptyColumnAddedToLoopingPicker_HandlesGracefully()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = true,
+ ItemHeight = 40
+ };
+
+ var emptyColumn = new PickerColumn
+ {
+ ItemsSource = new ObservableCollection(),
+ SelectedIndex = 0
+ };
+
+ picker.Columns.Add(emptyColumn);
+
+ Assert.Equal(0, emptyColumn.SelectedIndex);
+ Assert.Equal("", emptyColumn.SelectedItem);
+ Assert.True(picker.EnableLooping);
+ }
+
+ [Fact]
+ public void Picker_LoopingWithSingleItem_BehavesCorrectly()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = true,
+ ItemHeight = 40
+ };
+
+ var items = new ObservableCollection
+ {
+ "SingleItem"
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 0
+ };
+
+ picker.Columns.Add(pickerColumn);
+ pickerColumn.SelectedIndex = 1;
+
+ Assert.Equal(1, pickerColumn.SelectedIndex);
+ Assert.Equal("SingleItem", pickerColumn.SelectedItem);
+ Assert.True(picker.EnableLooping);
+ }
+
+ [Fact]
+ public void Picker_DynamicallyChangingItemHeight_UpdatesViewportCalculation()
+ {
+ var picker = new SfPicker
+ {
+ EnableLooping = true,
+ ItemHeight = 40
+ };
+
+ var items = new ObservableCollection
+ {
+ "Item1", "Item2", "Item3", "Item4", "Item5"
+ };
+
+ var pickerColumn = new PickerColumn
+ {
+ ItemsSource = items,
+ SelectedIndex = 2
+ };
+
+ picker.Columns.Add(pickerColumn);
+
+ double pickerHeight = 200;
+ double initialViewportItems = Math.Floor(pickerHeight / picker.ItemHeight);
+
+ picker.ItemHeight = 100;
+ double newViewportItems = Math.Floor(pickerHeight / picker.ItemHeight);
+
+ Assert.Equal(2, pickerColumn.SelectedIndex);
+ Assert.NotEqual(initialViewportItems, newViewportItems);
+ Assert.True(initialViewportItems > newViewportItems);
+ }
+
+ #endregion
}
}
diff --git a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerUnitTest.cs b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerUnitTest.cs
index cb62b426..18965feb 100644
--- a/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerUnitTest.cs
+++ b/maui/tests/Syncfusion.Maui.Toolkit.UnitTest/Picker/TimePickerUnitTest.cs
@@ -378,6 +378,124 @@ public void TimePicker_RelativePosition_GetAndSet(PickerRelativePosition positio
Assert.Equal(position, actualValue);
}
+ [Fact]
+ public void HeaderTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfTimePicker picker = new SfTimePicker();
+
+ picker.HeaderView.Height = 50;
+ picker.HeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Header Content" };
+ });
+
+ Assert.NotNull(picker.HeaderTemplate);
+ }
+
+ [Fact]
+ public void HeaderTemplate_GetAndSet_UsingDataTemplate_WhenaCalledDynamic()
+ {
+ SfTimePicker picker = new SfTimePicker();
+
+ Assert.Null(picker.HeaderTemplate);
+
+ picker.HeaderView.Height = 50;
+ picker.HeaderTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Header Content" };
+ });
+
+ Assert.NotNull(picker.HeaderTemplate);
+ }
+
+ [Fact]
+ public void ColumnHeaderTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfTimePicker picker = new SfTimePicker();
+
+ picker.ColumnHeaderView.Height = 50;
+ picker.ColumnHeaderTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = 5
+ };
+
+ var label = new Label
+ {
+ Text = "Column Header Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ grid.Add(label);
+ return grid;
+ });
+
+ Assert.NotNull(picker.ColumnHeaderTemplate);
+ }
+
+ [Fact]
+ public void ColumnHeaderTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfTimePicker picker = new SfTimePicker();
+
+ Assert.Null(picker.ColumnHeaderTemplate);
+
+ picker.ColumnHeaderView.Height = 50;
+ picker.ColumnHeaderTemplate = new DataTemplate(() =>
+ {
+ var grid = new Grid
+ {
+ BackgroundColor = Colors.LightGray,
+ Padding = 5
+ };
+
+ var label = new Label
+ {
+ Text = "Column Header Content",
+ HorizontalOptions = LayoutOptions.Center,
+ VerticalOptions = LayoutOptions.Center
+ };
+
+ grid.Add(label);
+ return grid;
+ });
+
+ Assert.NotNull(picker.ColumnHeaderTemplate);
+ }
+
+ [Fact]
+ public void FooterTemplate_GetAndSet_UsingDataTemplate()
+ {
+ SfTimePicker picker = new SfTimePicker();
+
+ picker.FooterView.Height = 50;
+ picker.FooterTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Footer Content" };
+ });
+
+ Assert.NotNull(picker.FooterTemplate);
+ }
+
+ [Fact]
+ public void FooterTemplate_GetAndSet_UsingDataTemplate_WhenCalledDynamic()
+ {
+ SfTimePicker picker = new SfTimePicker();
+
+ Assert.Null(picker.FooterTemplate);
+
+ picker.FooterView.Height = 50;
+ picker.FooterTemplate = new DataTemplate(() =>
+ {
+ return new Label { Text = "Footer Content" };
+ });
+
+ Assert.NotNull(picker.FooterTemplate);
+ }
+
[Fact]
public void SelectionChangedCommand_Execute_ReturnsTrue()
{
@@ -2654,5 +2772,292 @@ public void UpdateMinimumMaximumTime_ShouldUpdateMeridiemColumn_WhenSelectedHour
}
#endregion
+
+ #region PopupSizeFeature
+
+ [Fact]
+ public void DatePicker_PopupSize1()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ double expectedWidth = 100;
+ double expectedHeight = 200;
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenPopupSizeIsNotSet()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ double expectedWidth = 300;
+ double expectedHeight = 240;
+
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenPopupSizeOnPropertyChange()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ double expectedWidth = 50;
+ double expectedHeight = 20;
+
+ sfTimePicker.PopupWidth = 100;
+ sfTimePicker.PopupHeight = 200;
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenHeaderHeightProvided_PopupSizeProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ headerView.Height = 50;
+ sfTimePicker.HeaderView = headerView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 400;
+
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenHeaderHeightProvided_PopupSizeNotProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ headerView.Height = 50;
+ sfTimePicker.HeaderView = headerView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 290;
+
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenColumnHeaderProvided_PopupSizeProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ TimePickerColumnHeaderView columnHeaderView = new TimePickerColumnHeaderView();
+ columnHeaderView.Height = 50;
+ sfTimePicker.ColumnHeaderView = columnHeaderView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 400;
+
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenColumnHeaderProvided_PopupSizeNotProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ TimePickerColumnHeaderView columnHeaderView = new TimePickerColumnHeaderView();
+ columnHeaderView.Height = 50;
+ sfTimePicker.ColumnHeaderView = columnHeaderView;
+
+ double expectedWidth = 200;
+ double expectedHeight = 290;
+
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_PopupSizeProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ sfTimePicker.ItemHeight = 10;
+
+ double expectedWidth = 200;
+ double expectedHeight = 640;
+
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ }
+
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_PopupSizeNotProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ sfTimePicker.ItemHeight = 10;
+ double expectedWidth = 300;
+ double expectedHeight = 90;
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_ItemsLessThanFive()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ sfTimePicker.ItemHeight = 10;
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 30;
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenItemHeightProvided_ItemsGreaterThanFive()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ sfTimePicker.ItemHeight = 10;
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 50;
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenFooterHeightProvided_PopupSizeProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ PickerFooterView pickerFooterView = new PickerFooterView();
+ pickerFooterView.Height = 50;
+ sfTimePicker.FooterView = pickerFooterView;
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 50;
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenFooterHeightProvided_PopupSizeNotProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ PickerFooterView pickerFooterView = new PickerFooterView();
+ pickerFooterView.Height = 50;
+ sfTimePicker.FooterView = pickerFooterView;
+ double expectedWidth = 200;
+ double expectedHeight = 290;
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenAllHeaderHeightProvided_PopupSizeProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ TimePickerColumnHeaderView pickerColumnHeaderView = new TimePickerColumnHeaderView();
+ PickerFooterView footerView = new PickerFooterView();
+ sfTimePicker.ItemHeight = 10;
+ headerView.Height = 50;
+ pickerColumnHeaderView.Height = 50;
+ footerView.Height = 50;
+ sfTimePicker.HeaderView = headerView;
+ sfTimePicker.ColumnHeaderView = pickerColumnHeaderView;
+ sfTimePicker.FooterView = footerView;
+ double expectedWidth = 200;
+ double expectedHeight = 290 + 50 + 50 + 50 + 50;
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ [Fact]
+ public void DatePicker_PopupSize_WhenAllHeaderHeightProvided_PopupSizeNotProvided()
+ {
+ SfTimePicker sfTimePicker = new SfTimePicker();
+ PickerHeaderView headerView = new PickerHeaderView();
+ TimePickerColumnHeaderView pickerColumnHeaderView = new TimePickerColumnHeaderView();
+ PickerFooterView footerView = new PickerFooterView();
+ headerView.Height = 50;
+ pickerColumnHeaderView.Height = 50;
+ footerView.Height = 50;
+ sfTimePicker.HeaderView = headerView;
+ sfTimePicker.ColumnHeaderView = pickerColumnHeaderView;
+ sfTimePicker.FooterView = footerView;
+ double expectedWidth = 200;
+ double expectedHeight = 50 + 50 + 50;
+ sfTimePicker.PopupWidth = expectedWidth;
+ sfTimePicker.PopupHeight = expectedHeight;
+ double actualWidth = sfTimePicker.PopupWidth;
+ double actualHeight = sfTimePicker.PopupHeight;
+
+ Assert.Equal(expectedWidth, actualWidth);
+ Assert.Equal(expectedHeight, actualHeight);
+ }
+
+ #endregion
}
}