I am trying to display a dynamic number of series in dotnet maui with SfCartesianChart (Version 22.2.12).
All the data from the chart are present in the ViewModel like:
public class ChartPageViewModel : BaseViewModel, IChartPageViewModel
{
//Data are loaded asynchronously after the page load
//after setting the data the PropertyChangedEvent is fired
public ObservableCollection<IChartSeriesViewModel> DataSeries { get; set; }
}
public class ChartSeriesViewModel : IChartSeriesViewModel
{
public string Title { get; set; }
public IList<IChartPointViewModel> Data { get; set; }
}
public class ChartPointViewModel : IChartPointViewModel
{
public DateTime Date { get; set; }
public decimal Value { get; set; }
}
The data is bound with the series-Property using a Value-Converter:
<chart:SfCartesianChart Series="{Binding DataSeries, Converter={StaticResource DataSeriesConverter}}">
<chart:SfCartesianChart.XAxes>
<chart:CategoryAxis />
</chart:SfCartesianChart.XAxes>
<chart:SfCartesianChart.YAxes>
<chart:NumericalAxis />
</chart:SfCartesianChart.YAxes>
</chart:SfCartesianChart>
the Value-Converter (IList to ChartSeriesCollection)
public class SfCartesianChartDataSeriesConverter : IValueConverter
{
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is IList<IChartSeriesViewModel> chartSeries)
{
ChartSeriesCollection series = new ChartSeriesCollection();
foreach (var i in chartSeries)
{
series.Add(new FastLineSeries()
{
ItemsSource = i.Data,
XBindingPath = nameof(IChartPointViewModel.Date),
YBindingPath = nameof(IChartPointViewModel.Value),
});
}
return series;
}
return null;
}
Unfortunately, the data is only shown/drawn when the view are redrawn (e.g. with a window resize).
The PropertyChangedEvent seems to work fine, because when I add a series in xaml the data is displayed immediately after the PropertyChangedEvent.
<chart:SfCartesianChart>
<chart:SfCartesianChart.XAxes>
<chart:CategoryAxis />
</chart:SfCartesianChart.XAxes>
<chart:SfCartesianChart.YAxes>
<chart:NumericalAxis />
</chart:SfCartesianChart.YAxes>
<chart:FastLineSeries
ItemsSource="{Binding DataSeries[0].Data}"
XBindingPath="Date"
YBindingPath="Value" />
</chart:SfCartesianChart>
But this is not the way to create a dynamic number of series.
How can the "redraw" be forced? Or how must the binding be made for this case?
Based on the answer from user22357370, I have found the following solution. Modified to keep the solution generic.
Custom control, bases on SfCartesianChart:
public class SfCartesianChartExt : SfCartesianChart
{
public static readonly BindableProperty SeriesCollectionProperty = BindableProperty.Create(
nameof(SeriesCollection),
typeof(ChartSeriesCollection),
typeof(SfCartesianChartExt), null, BindingMode.Default, null, OnSeriesPropertyChanged);
public ChartSeriesCollection SeriesCollection
{
get => (ChartSeriesCollection)GetValue(SeriesCollectionProperty);
set => SetValue(SeriesCollectionProperty, value);
}
private static void OnSeriesPropertyChanged(BindableObject bindable, object oldValue, object newValue)
{
(bindable as SfCartesianChartExt)?.GenerateSeries(newValue);
}
private void GenerateSeries(object newValue)
{
if (newValue is ChartSeriesCollection collection)
{
((INotifyCollectionChanged)newValue).CollectionChanged += DataPoint_CollectionChanged;
Series.Clear();
foreach (var item in collection)
{
CreateSeries(item);
}
}
}
private void CreateSeries(ChartSeries item)
{
Series.Add(item);
}
private void DataPoint_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
switch (e.Action)
{
case NotifyCollectionChangedAction.Add:
{
if (e.NewItems != null)
{
CreateSeries(e.NewItems[0] as ChartSeries);
}
break;
}
case NotifyCollectionChangedAction.Remove:
{
Series.RemoveAt(e.OldStartingIndex);
break;
}
}
}
}
Usage, with converter from question:
<chart:SfCartesianChartExt Series="{Binding DataSeries, Converter={StaticResource DataSeriesConverter}}">
<chart:SfCartesianChart.XAxes>
<chart:CategoryAxis />
</chart:SfCartesianChart.XAxes>
<chart:SfCartesianChart.YAxes>
<chart:NumericalAxis />
</chart:SfCartesianChart.YAxes>
</chart:SfCartesianChartExt>