Skip to content

Commit 9a112fb

Browse files
Merge pull request #117 from Harsha-SF4223/main
Implement the Empty Point support in the Cartesian Chart in MAUI Toolkit
2 parents def58d2 + 7345cc4 commit 9a112fb

39 files changed

+1477
-98
lines changed

maui/samples/Gallery/SampleList/CartesianChartSamplesList.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8" ?>
22
<SyncfusionControls>
33
<ControlCategory Name="Data Visualization">
4-
<Control Title="Cartesian Charts" ControlName="SfCartesianChart" Image="cartesianchart.png" Description="Plot over 20+ chart types with extensive features like data label, trackball and zooming.">
4+
<Control Title="Cartesian Charts" ControlName="SfCartesianChart" StatusTag="Updated" Image="cartesianchart.png" Description="Plot over 20+ chart types with extensive features like data label, trackball and zooming.">
55
<Category Title="Chart Types">
66
<SubCategory Title="Column">
77
<CardLayout>
@@ -170,6 +170,8 @@
170170

171171
<Sample Title="Annotation" SampleName="ShapeAnnotationSample" Description="This sample showcases the shape annotation support, which includes line, horizontal line, vertical line, rectangle, and ellipse." SearchTags="annotation, chart annotation"/>
172172

173+
<Sample Title="Empty Points" StatusTag="New" SampleName="EmptyPointSupport" SearchTags="empty points, missing data points, missing values, incomplete data, data gaps, incomplete records, blank entries"/>
174+
173175
<Category Title="Plot band">
174176
<Sample Title="Horizontal" SampleName="HorizontalPlotBand" Platforms="Android,iOS" SearchTags="horizontal plot, band, plotband"/>
175177
<Sample Title="Horizontal" SampleName="HorizontalPlotBandWindows" Platforms="Windows,MacCatalyst" SearchTags="horizontal plot, band, plot band"/>
Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
<?xml version="1.0" encoding="utf-8" ?>
2+
<localCore:SampleView xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
3+
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
4+
x:Class="Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart.EmptyPointSupport"
5+
xmlns:local="clr-namespace:Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart"
6+
xmlns:localCore="clr-namespace:Syncfusion.Maui.ControlsGallery;assembly=Syncfusion.Maui.ControlsGallery"
7+
xmlns:chart="clr-namespace:Syncfusion.Maui.Toolkit.Charts;assembly=Syncfusion.Maui.Toolkit">
8+
9+
<localCore:SampleView.Resources>
10+
<ResourceDictionary>
11+
<ResourceDictionary.MergedDictionaries>
12+
<local:CartesianChartColorResources/>
13+
</ResourceDictionary.MergedDictionaries>
14+
</ResourceDictionary>
15+
</localCore:SampleView.Resources>
16+
17+
<localCore:SampleView.Content>
18+
<chart:SfCartesianChart x:Name="Chart" HorizontalOptions="Fill" VerticalOptions="Fill">
19+
20+
<chart:SfCartesianChart.BindingContext>
21+
<local:EmptyPointViewModel x:Name="ViewModel"/>
22+
</chart:SfCartesianChart.BindingContext>
23+
24+
<chart:SfCartesianChart.Title>
25+
<Label Text="Crop Yield Across Climate Zones" Margin="0,0,0,5" HorizontalOptions="Fill" HorizontalTextAlignment="Center" VerticalOptions="Center" FontSize="16"/>
26+
</chart:SfCartesianChart.Title>
27+
28+
<chart:SfCartesianChart.XAxes>
29+
<chart:CategoryAxis IsVisible="True" ShowMajorGridLines="False" EdgeLabelsDrawingMode="Shift" LabelRotation="{OnPlatform Android=-45,iOS=-45}"/>
30+
</chart:SfCartesianChart.XAxes>
31+
32+
<chart:SfCartesianChart.YAxes>
33+
<chart:NumericalAxis ShowMajorGridLines="True" ShowMinorGridLines="False" Minimum="0" Interval="20" Maximum="100">
34+
<chart:NumericalAxis.AxisLineStyle>
35+
<chart:ChartLineStyle StrokeWidth="0" />
36+
</chart:NumericalAxis.AxisLineStyle>
37+
<chart:NumericalAxis.Title>
38+
<chart:ChartAxisTitle Text="Crop Yield (Tons)"/>
39+
</chart:NumericalAxis.Title>
40+
</chart:NumericalAxis>
41+
</chart:SfCartesianChart.YAxes>
42+
43+
<chart:SfCartesianChart.Legend>
44+
<chart:ChartLegend/>
45+
</chart:SfCartesianChart.Legend>
46+
47+
<chart:AreaSeries ItemsSource="{Binding EmptyPointData}"
48+
XBindingPath="Name"
49+
YBindingPath="Value"
50+
EnableAnimation="{Binding EnableAnimation}"
51+
Stroke="{AppThemeBinding Light={StaticResource series3Light}, Dark={StaticResource series3Dark}}"
52+
Fill="{AppThemeBinding Light={StaticResource series3Light30}, Dark={StaticResource series3Dark30}}"
53+
StrokeWidth="2"
54+
ShowMarkers="True"
55+
EmptyPointMode="{Binding EmptyPointMode}"
56+
Label="Wheat"
57+
LegendIcon="SeriesType"
58+
EnableTooltip="True">
59+
<chart:AreaSeries.MarkerSettings>
60+
<chart:ChartMarkerSettings Fill="{AppThemeBinding Default={StaticResource ContentBackground}}" Stroke="{AppThemeBinding Light={StaticResource series3Light}, Dark={StaticResource series3Dark}}" StrokeWidth="1.5"/>
61+
</chart:AreaSeries.MarkerSettings>
62+
</chart:AreaSeries>
63+
64+
<chart:AreaSeries ItemsSource="{Binding EmptyPointData}"
65+
XBindingPath="Name"
66+
YBindingPath="High"
67+
EnableAnimation="{Binding EnableAnimation}"
68+
Stroke="{AppThemeBinding Light={StaticResource series2Light}, Dark={StaticResource series2Dark}}"
69+
Fill="{AppThemeBinding Light={StaticResource series2Light30}, Dark={StaticResource series2Dark30}}"
70+
StrokeWidth="2"
71+
ShowMarkers="True"
72+
EmptyPointMode="{Binding EmptyPointMode}"
73+
Label="Corn"
74+
LegendIcon="SeriesType"
75+
EnableTooltip="True">
76+
<chart:AreaSeries.MarkerSettings>
77+
<chart:ChartMarkerSettings Fill="{AppThemeBinding Default={StaticResource ContentBackground}}" Stroke="{AppThemeBinding Light={StaticResource series2Light}, Dark={StaticResource series2Dark}}" StrokeWidth="1.5"/>
78+
</chart:AreaSeries.MarkerSettings>
79+
</chart:AreaSeries>
80+
81+
<chart:AreaSeries ItemsSource="{Binding EmptyPointData}"
82+
XBindingPath="Name"
83+
YBindingPath="Low"
84+
EnableAnimation="{Binding EnableAnimation}"
85+
Stroke="{AppThemeBinding Light={StaticResource series1Light}, Dark={StaticResource series1Dark}}"
86+
Fill="{AppThemeBinding Light={StaticResource series1Light30}, Dark={StaticResource series1Dark30}}"
87+
StrokeWidth="2"
88+
ShowMarkers="True"
89+
EmptyPointMode="{Binding EmptyPointMode}"
90+
Label="Rice"
91+
LegendIcon="SeriesType"
92+
EnableTooltip="True">
93+
<chart:AreaSeries.MarkerSettings>
94+
<chart:ChartMarkerSettings Fill="{AppThemeBinding Default={StaticResource ContentBackground}}" Stroke="{AppThemeBinding Light={StaticResource series1Light}, Dark={StaticResource series1Dark}}" StrokeWidth="1.5"/>
95+
</chart:AreaSeries.MarkerSettings>
96+
</chart:AreaSeries>
97+
98+
</chart:SfCartesianChart>
99+
</localCore:SampleView.Content>
100+
101+
<localCore:SampleView.OptionView>
102+
<Grid>
103+
<VerticalStackLayout Spacing="15">
104+
<HorizontalStackLayout Spacing="5">
105+
<Label Text="Empty Point Mode: " VerticalOptions="Start" HorizontalOptions="Center" Padding="5" FontSize="17"/>
106+
<Picker WidthRequest="{OnPlatform MacCatalyst=70, iOS=70}" ItemsSource="{Binding Source={x:Reference ViewModel},Path=EmptyPointModeValues}"
107+
SelectedItem="{Binding Source={x:Reference ViewModel}, Path=EmptyPointModeValues[0]}"
108+
VerticalOptions="Start" HorizontalOptions="Fill"
109+
x:Name="picker"
110+
BackgroundColor="{AppThemeBinding Light={StaticResource BackgroundLight}, Dark={StaticResource BackgroundDark}}"
111+
TextColor="{AppThemeBinding Light={StaticResource TextColourLight}, Dark={StaticResource TextColourDark}}"
112+
SelectedIndex="0"
113+
SelectedIndexChanged="picker_SelectedIndexChanged">
114+
</Picker>
115+
</HorizontalStackLayout>
116+
</VerticalStackLayout>
117+
</Grid>
118+
</localCore:SampleView.OptionView>
119+
120+
</localCore:SampleView>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
2+
using Syncfusion.Maui.Toolkit.Charts;
3+
4+
namespace Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart
5+
{
6+
public partial class EmptyPointSupport : SampleView
7+
{
8+
public EmptyPointSupport()
9+
{
10+
InitializeComponent();
11+
}
12+
13+
private void picker_SelectedIndexChanged(object sender, EventArgs e)
14+
{
15+
var picker = (Picker)sender;
16+
int selectedIndex = picker.SelectedIndex;
17+
switch (selectedIndex)
18+
{
19+
case 1:
20+
ViewModel.EmptyPointMode = EmptyPointMode.Zero;
21+
break;
22+
23+
case 2:
24+
ViewModel.EmptyPointMode = EmptyPointMode.Average;
25+
break;
26+
27+
default:
28+
ViewModel.EmptyPointMode = EmptyPointMode.None;
29+
break;
30+
}
31+
}
32+
33+
public override void OnDisappearing()
34+
{
35+
base.OnDisappearing();
36+
Chart.Handler?.DisconnectHandler();
37+
}
38+
}
39+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+

2+
using System.Collections.ObjectModel;
3+
using Syncfusion.Maui.Toolkit.Charts;
4+
5+
namespace Syncfusion.Maui.ControlsGallery.CartesianChart.SfCartesianChart
6+
{
7+
public class EmptyPointViewModel : BaseViewModel
8+
{
9+
private EmptyPointMode emptypointMode;
10+
public EmptyPointMode EmptyPointMode
11+
{
12+
get => emptypointMode;
13+
set
14+
{
15+
if (emptypointMode != value)
16+
{
17+
emptypointMode = value;
18+
OnPropertyChanged(nameof(EmptyPointMode));
19+
}
20+
}
21+
}
22+
23+
public string[] EmptyPointModeValues => ["None", "Zero", "Average"];
24+
25+
public ObservableCollection<ChartDataModel> EmptyPointData { get; set; }
26+
27+
public EmptyPointViewModel()
28+
{
29+
30+
EmptyPointData =
31+
[
32+
new ChartDataModel("Tropical", 85, 70, 50),
33+
new ChartDataModel("Continental", 80, 65, 40),
34+
new ChartDataModel("Mediterranean", 82, 60, 30),
35+
new ChartDataModel("Arid", double.NaN, double.NaN, double.NaN),
36+
new ChartDataModel("Polar", double.NaN, double.NaN, double.NaN),
37+
new ChartDataModel("Temperate", 75, 50, 40),
38+
new ChartDataModel("Oceanic", 90, 65, 40),
39+
new ChartDataModel("Highland", 95, 60, 30),
40+
];
41+
}
42+
}
43+
}

maui/src/Charts/Area/Partial/CartesianChartArea.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,15 @@ internal void UpdateStackingSeries()
414414
{
415415
if (series is StackingSeriesBase stackingSeries && stackingSeries.IsVisible)
416416
{
417+
418+
if (stackingSeries.RequiredEmptyPointReset)
419+
{
420+
stackingSeries.ResetEmptyPointIndexes();
421+
stackingSeries.RequiredEmptyPointReset = false;
422+
}
423+
424+
stackingSeries.ValidateYValues();
425+
417426
var stackingGroup = stackingSeries.GroupingLabel;
418427
var stackingXAxis = stackingSeries.ActualXAxis;
419428
var stackingYAxis = stackingSeries.ActualYAxis;

maui/src/Charts/Axis/Layouts/CartesianAxisLayout.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,6 +414,16 @@ void UpdateSeriesRange(ReadOnlyObservableCollection<ChartSeries> visibleSeries)
414414
{
415415
foreach (CartesianSeries series in visibleSeries.Cast<CartesianSeries>())
416416
{
417+
if (!series.IsStacking)
418+
{
419+
if (series.RequiredEmptyPointReset)
420+
{
421+
series.ResetEmptyPointIndexes();
422+
series.RequiredEmptyPointReset = false;
423+
}
424+
425+
series.ValidateYValues();
426+
}
417427

418428
if (!series.SegmentsCreated) //creates segment if segmentsCreated is false.
419429
{

maui/src/Charts/DataLabel/ChartDataLabelSettings.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -360,7 +360,7 @@ internal string GetLabelContent(double value)
360360
}
361361
else
362362
{
363-
labelContent = value.ToString("#.##");
363+
labelContent = value == 0 ? value.ToString("0.##") : value.ToString("#.##");
364364
}
365365

366366
return labelContent;

maui/src/Charts/Layouts/ChartSeriesView.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ internal void InternalCreateSegments()
119119
_series.OldSegments = null;
120120
}
121121

122+
_series.UpdateEmptyPointSettings();
122123
_series.SegmentsCreated = true;
123124
}
124125
}

maui/src/Charts/Segment/CartesianSegment.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,15 @@
77
/// </summary>
88
public abstract class CartesianSegment : ChartSegment
99
{
10+
#region Property
11+
12+
/// <summary>
13+
/// Get the bool value to identify the empty point segment.
14+
/// </summary>
15+
public bool IsEmpty { get; internal set; }
16+
17+
#endregion
18+
1019
#region Methods
1120

1221
#region Animation Methods
@@ -53,7 +62,7 @@ internal void CalculateDataLabelPosition(double xValue, double yValue, double ac
5362
return;
5463
}
5564

56-
IsEmpty = double.IsNaN(yValue);
65+
IsZero = double.IsNaN(yValue);
5766

5867
double x = xValue, y = xyDataSeries.GetDataLabelPositionAtIndex(Index);
5968

@@ -81,7 +90,7 @@ internal override void UpdateDataLabels()
8190
dataLabel.Item = Item;
8291
dataLabel.Label = LabelContent ?? string.Empty;
8392

84-
LabelPositionPoint = InVisibleRange && !IsEmpty ? CartesianDataLabelSettings.CalculateDataLabelPoint(series, this, new PointF((float)DataLabelXPosition, (float)DataLabelYPosition), dataLabelSettings.LabelStyle) : new PointF(float.NaN, float.NaN);
93+
LabelPositionPoint = InVisibleRange && !IsZero ? CartesianDataLabelSettings.CalculateDataLabelPoint(series, this, new PointF((float)DataLabelXPosition, (float)DataLabelYPosition), dataLabelSettings.LabelStyle) : new PointF(float.NaN, float.NaN);
8594

8695
dataLabel.XPosition = LabelPositionPoint.X;
8796
dataLabel.YPosition = LabelPositionPoint.Y;

maui/src/Charts/Segment/ChartSegment.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,7 @@ internal bool HasStroke
203203

204204
internal PointF LabelPositionPoint { get; set; }
205205

206-
internal bool IsEmpty { get; set; }
206+
internal bool IsZero { get; set; }
207207

208208
internal SeriesView? SeriesView { get; set; }
209209

0 commit comments

Comments
 (0)