3

After modifying the code provided on the website's documentation, I have had trouble displaying the SeriesCollection. Let me show you what I have done.

Firstly, the XAML:

<Grid>
    <lvc:CartesianChart Series="{Binding GraphData.SeriesCollection}"
                    LegendLocation="Top">
        <lvc:CartesianChart.AxisY>
            <lvc:Axis Title="VIs"></lvc:Axis>
        </lvc:CartesianChart.AxisY>
        <lvc:CartesianChart.AxisX>
            <lvc:Axis Title="Players"></lvc:Axis>
        </lvc:CartesianChart.AxisX>
    </lvc:CartesianChart>
</Grid>

In my ViewModel, I have a custom object storing information I wish to bind to this chart as:

public GraphModel GraphData { get; set; } = new GraphModel();

I have a button to test my code. Clicking it executes the following code:

private void UpdateGraphStatistics()
{
    var compdata = new List<double>();
    var dqdata = new List<double>();

    foreach (var item in Competitors)
    {
        if (!item.DQ)
        {
            compdata.Add(item.VIs);
        }
        else if (item.DQ)
        {
            dqdata.Add(item.VIs);
        }
    }

    GraphData = new GraphModel(compdata, dqdata);
}

For all intents and purposes, this above code adds to two lists a series of doubles, which I have determined is working (so this isn't the issue; the SeriesCollection isn't empty!)

Next, the GraphModel. This is the big one, where the issue likely lies, but I cannot determine where:

class GraphModel
{
    public ChartValues<ObservablePoint> CompetitionData = new ChartValues<ObservablePoint>();
    public ChartValues<ObservablePoint> DQData = new ChartValues<ObservablePoint>();
    public SeriesCollection SeriesCollection { get; set; }

    public GraphModel()
    {
        CreateSeriesCollection();
    }

    public GraphModel(List<double> competitionData, List<double> dqData)
    {
        ParseData(competitionData, dqData);

        CreateSeriesCollection();
    }

    private void CreateSeriesCollection()
    {
        SeriesCollection = new SeriesCollection
        {
            new LineSeries
            {
                Title = "Competition Data (VIs/Player Number)",
                Values = CompetitionData,
                LineSmoothness = 0.6,
                PointForeground = Brushes.Blue
            },
            new LineSeries
            {
                Title = "DQ Data (VIs/Player Number)",
                Values = DQData,
                LineSmoothness = 1,
                PointForeground = Brushes.Red
            }
        };
    }

    private void ParseData(List<double> compData, List<double> dqData)
    {
        // Convert competitionData into observable points
        int count = 1;
        foreach (var item in compData)
        {
            CompetitionData.Add(new ObservablePoint(count++, item));
        }


        // Convert dqdata into observable points
        int offsetX = CompetitionData.Count;
        foreach (var item in dqData)
        {
            DQData.Add(new ObservablePoint(offsetX++, item));
        }
    }
}

The method ParseData() has been used before in non-WPF code, so I know that is unlikely the cause for issue.

I still haven't solved what is going wrong. Am I not binding my data correctly?

Edit

For context, I know the SeriesCollection within the GraphModel is definitely containing the information I want it too, as it should since this code is more or less the same from a non-WPF version of the software I initially trialled in.

  • As a total stab in the dark, could this be something to do with LiveCharts’ `SeriesCollection` not implementing `INotifyCollectionChanged`? – BenderBoy Jun 27 '19 at 11:19
  • 1
    @BenderBoy Oh! I forgot I actually solved this on my own. Turns out I was making the mistake of making a new Object every time I wanted to update the graph, which in turn, removed the bindings the View had on this. I solved this by implementing a method within the class to actually change the values of the SeriesCollection, given new data. The alternative would have been to rebind the data, but I found this an appropriate solution! I will post code soon, as an answer! – TimeTravelPenguin Jun 27 '19 at 11:22

1 Answers1

1

I solved my own problem. The issue was that I was running the same method every time I wanted to update the graph data (as shown above):

private void UpdateGraphStatistics()
{
    var compdata = new List<double>();
    var dqdata = new List<double>();

    foreach (var item in Competitors)
    {
        if (!item.DQ)
        {
            compdata.Add(item.VIs);
        }
        else if (item.DQ)
        {
            dqdata.Add(item.VIs);
        }
    }

    GraphData = new GraphModel(compdata, dqdata);
}

The problem with this code is that it creates a new object, which actually severs the bindings that the View has with GraphData.

I was posed with two options: do not create a new object, or create a new object and then rebind the view to the object.

Because I haven't been able to figure out how to rebind the view nicely, I opted for the option of creating a method within the object to modify the data. I did:

public void ParseData(List<CompetitorModel> compData, List<CompetitorModel> dqData)
{
    CompetitionData.Clear();
    DQData.Clear();

    // Convert competitionData into observable points
    int count = 0;
    foreach (var item in compData)
    {
        CompetitionData.Add(new ObservablePoint(count++, item.VIs));
    }


    // Convert dqdata into observable points
    int offsetX = compData.Count;
    foreach (var item in dqData)
    {
        DQData.Add(new ObservablePoint(offsetX++, item.VIs));
    }
}

In short, I made the section of code that deals with data pasrsing within my object public, which clears the collections within, and constructs a new modifies the existing SeriesCollection which is still bound to the view, without disrupting the bind, by altering the CompetitionData and DQData collections.

Once I learn how to rebind the data, I COULD make this method private and simply create a new object, but then I am also creating the entire SeriesCollection (and other Funcs and stuff I have within the model now) each time, so this method may be theoretically faster process-wise.