views:

184

answers:

2

I'm currently recoding a bar chart in my app to make use of the Chart class in the WPF Toolkit. Using MVVM, I'm binding the ItemsSource of a ColumnSeries in my chart to a property on my viewmodel. Here's the relevant XAML:

<charting:Chart>
  <charting:ColumnSeries ItemsSource="{Binding ScoreDistribution.ClassScores}"
                         IndependentValuePath="ClassName" DependentValuePath="Score"/>
</charting:Chart>

And the property on the viewmodel:

// NB: viewmodel derived from Josh Smith's BindableObject
public class ExamResultsViewModel : BindableObject
{
    // ...

    private ScoreDistributionByClass _scoreDistribution;
    public ScoreDistributionByClass ScoreDistribution
    {
        get
        {
            return _scoreDistribution;
        }
        set
        {
            if (_scoreDistribution == value)
            {
                return;
            }

            _scoreDistribution = value;

            RaisePropertyChanged(() => ScoreDistribution);
        }
    }

However, when I update the ScoreDistribution property (by setting it to a new ScoreDistribution object), the chart gets an additional series (based on the new ScoreDistribution) as well as keeping the original series (based on the previous ScoreDistribution).

To illustrate this, here are a couple of screenshots showing the chart before an update (with a single data point in ScoreDistribution.ClassScores) and after it (now with 3 data points in ScoreDistribution.ClassScores):

With a single data point

With 2 more data points added - note the original wide bar behind them

Now, I realise there are other ways I could be doing this (e.g. changing the contents of the original ScoreDistribution object rather than replacing it entirely), but I don't understand why it's going wrong in its current form. Can anyone help?

A: 

What you are doing ought to work. The fact that it doesn't indicates WPF Toolkit has a bug.

WPF Toolkit implements OnItemsSourceChanged() on DataPointSeries to detect the case where you replace ItemsSource and call Refresh(). Refresh() has code to remove all data points (except ones that are already animating away) and then create a whole new set of DataPoints. Obviously that code has a bug in it. I looked for a minute or two at it but didn't see what was wrong. I would start by upgrading to the latest WPFToolkit release. If that doesn't fix it, you might step through the DataPointSeries.Refresh() method when the ItemsSource is changed to see what is happening there and why it isn't removing the old DataPoint objects.

Or, as you observed, you could work around the bug by just replacing the collection content instead of the collection as a whole.

Ray Burns
A: 

Turns out the problem was triggered by the frequency of updates in the chart, not the fact that the entire series was being replaced; changing the databound property to an ObservableCollection made no difference.

In the end, we changed our code to include a delay between changes to the underlying data and those changes being reflected in the ViewModel's bound collection property. While it does depend to an extent on the speed of the machine on which the app is running, we've settled on a 0.5sec delay between the last underlying data change and the ViewModel property updating. This prevents the chart being updated more than every 0.5secs and it seems to do the job.

At some point, I'll actually go through the WPFToolkit code and see what I can do to fix it, but for now, this workaround's worth noting.

Mal Ross