In the chart I'm developing the user needs to be able to exclude or include series at runtime.
If I have understood the information correctly so far it is not possible to XAML-declare series on this kind of dynamic sources but that it is something you're considering for a future update. I didn't want to do it all using code behind becuase that would mess up my ability to declare templates for each series and its trackball.
So, instead I build a small ChartBehavior that acts as the "glue" between my dynamic data series and the chart. It works as expected except for one detail: A series that gets omitted and then included again always ends up at the bottom of the trackball info popup. I assumed that the order of the items in the trackball popup was based on the physical order of the Chart.Series collection so the the behavior saves the initial order to be able to re-insert an omitted series at its original position but this does not work.
How can I affect the order of the items in the trackball info popup?
If I have understood the information correctly so far it is not possible to XAML-declare series on this kind of dynamic sources but that it is something you're considering for a future update. I didn't want to do it all using code behind becuase that would mess up my ability to declare templates for each series and its trackball.
So, instead I build a small ChartBehavior that acts as the "glue" between my dynamic data series and the chart. It works as expected except for one detail: A series that gets omitted and then included again always ends up at the bottom of the trackball info popup. I assumed that the order of the items in the trackball popup was based on the physical order of the Chart.Series collection so the the behavior saves the initial order to be able to re-insert an omitted series at its original position but this does not work.
How can I affect the order of the items in the trackball info popup?
internal sealed class DynamicCategoricalSeriesBehavior : ChartBehavior
{
private class SeriesInfo
{
public string SeriesKey { get; private set; }
public int Index { get; set; }
public SeriesInfo(string seriesKey, int seriesIndex)
{
Index = seriesIndex;
SeriesKey = seriesKey;
}
}
private Dictionary<
CategoricalSeries
, SeriesInfo> _categoricalSeries;
protected override void OnAttached()
{
base.OnAttached();
Chart.DataContextChanged += chartDataContextChanged;
listenToDataContextPropertyChanged(Chart.DataContext as INotifyPropertyChanged);
}
protected override void OnChartTemplateChanged(Canvas oldAdornerContainer, Canvas adornerContainer)
{
base.OnChartTemplateChanged(oldAdornerContainer, adornerContainer);
saveCategoricalSeriesValuePropertyNames();
transformMetricsForSeries(Chart as RadCartesianChart);
}
private void saveCategoricalSeriesValuePropertyNames()
{
var cartesianChart = Chart as RadCartesianChart;
if (cartesianChart == null)
return;
_categoricalSeries = new Dictionary<
CategoricalSeries
, SeriesInfo>();
var seriesIndex = 0;
foreach (var series in cartesianChart.Series)
{
var catSeries = series as CategoricalSeries;
if (catSeries == null)
continue;
var propertyNameValueBinding = catSeries.ValueBinding as PropertyNameDataPointBinding;
if (propertyNameValueBinding == null)
continue;
_categoricalSeries[catSeries] = new SeriesInfo(propertyNameValueBinding.PropertyName, seriesIndex++);
}
}
private void chartDataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
listenToDataContextPropertyChanged(Chart.DataContext as INotifyPropertyChanged);
}
private void listenToDataContextPropertyChanged(INotifyPropertyChanged vm)
{
if (vm == null)
return;
vm.PropertyChanged += (o, args) =>
{
if (args.PropertyName == "DynamicDataSeries")
transformMetricsForSeries(Chart as RadCartesianChart);
};
}
private void transformMetricsForSeries(RadCartesianChart chart)
{
if (chart == null)
return;
var dynamicVM = Chart.DataContext as IDynamicMetricsValueSeriesProvider<
double
>;
if (dynamicVM == null)
return;
var supportedSeries = dynamicVM.DynamicDataSeries.Select(series => series.SeriesKey).ToList();
foreach (var kvp in _categoricalSeries)
{
var series = kvp.Key;
var seriesKey = kvp.Value.SeriesKey;
if (!supportedSeries.Contains(seriesKey))
{
removeSeries(chart, series, seriesKey);
continue;
}
insertSeries(chart, series, seriesKey, dynamicVM);
}
}
private void insertSeries(RadCartesianChart chart, CategoricalSeries series, string key, IDynamicMetricsValueSeriesProvider<
double
> seriesProvider)
{
if (series.Visibility != Visibility.Visible)
{
series.Visibility = Visibility.Visible;
var index = _categoricalSeries[series].Index;
chart.Series.Insert(index, series);
}
var dataPoints = seriesProvider.MetricsData.Where(i => i.MetricsId == key).ToList();
series.ValueBinding = new PropertyNameDataPointBinding("Value");
series.ItemsSource = dataPoints;
var stroked = series as CategoricalStrokedSeries;
if (stroked != null)
stroked.Stroke = seriesProvider.GetMetricsDataColorBrush(key);
}
private void removeSeries(RadCartesianChart chart, CategoricalSeries series, string key)
{
chart.Series.Remove(series);
series.Visibility = Visibility.Collapsed;
}
}