Search code examples
c#wpflivechartsmultiple-axes

Multiple Axis at Runtime in LiveCharts


I'm trying to write a WPF application to plot line charts for series of numbers I have. These numbers are listed in a .CSV file which I will read at run time. Thus, I do not know the number of series I will have, nor the max/min values of each.

To demonstrate, and for the sake of brevity, take a look at the following example. Think of these series values as what I will read from my .CSV file in my actual application.

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();

        SeriesCollection = new SeriesCollection
        {
            new LineSeries
            {
                Title = "Series 1",
                Values = new ChartValues<double> { 4, 66, 5, 2, 4 },
            },
            new LineSeries
            {
                Title = "Series 2",
                Values = new ChartValues<double> { 6, 7, 3, 4, 6 },
            },
            new LineSeries
            {
                Title = "Series 3",
                Values = new ChartValues<double> { 4, 2, 7, 2, 7 },
            }
        };

        DataContext = this;
    }

    public SeriesCollection SeriesCollection { get; set; }
}

My XAML looks very simple, like so:

<Window x:Class="WPFCharts.MainWindow"
        ...
        Title="MainWindow" Height="450" Width="800">
    <Grid>
        <lvc:CartesianChart Series="{Binding SeriesCollection}"/>
    </Grid>
</Window>

As you can see, one of the values in one series is off the charts compared to the rest if I were to plot this in a line graph with default LiveCharts settings:

enter image description here

So I want to give the user the opportunity to put such line graphs in their own axis. Reading through LiveCharts documentation, I found, as shown here, that you can put different line series in different axis by using the ScaleXAt and ScaleYAt properties.

However that example sets axis in XAML whereas I want to do this dynamically. So I tried setting the said property in code behind like so:

SeriesCollection = new SeriesCollection
{
    new LineSeries
    {
        Title = "Series 1",
        Values = new ChartValues<double> { 4, 66, 5, 2, 4 },
        ScalesYAt = 0
    },
    new LineSeries
    {
        Title = "Series 2",
        Values = new ChartValues<double> { 6, 7, 3, 4, 6 },
        ScalesYAt = 1
    },
    new LineSeries
    {
        Title = "Series 3",
        Values = new ChartValues<double> { 4, 2, 7, 2, 7 },
        ScalesYAt = 2
    }
};

But when I do that, and run the application, I get an exception saying:

System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection.'

What am I doing wrong here? How can I set this using code, not XAML?


Solution

  • If you want to use different Y Axis, then you need to declare them, maybe you missed it. So your model will become something like:

    public class ViewModel
    {
        public ViewModel()
        {
            SeriesCollection = new SeriesCollection
            {
                new LineSeries
                {
                    Title = "Series 1",
                    Values = new ChartValues<double> { 4, 66, 5, 2, 4 },
                    ScalesYAt = 0
                },
                new LineSeries
                {
                    Title = "Series 2",
                    Values = new ChartValues<double> { 6, 7, 3, 4, 6 },
                    ScalesYAt = 1
                },
                new LineSeries
                {
                    Title = "Series 3",
                    Values = new ChartValues<double> { 4, 2, 7, 2, 7 },
                    ScalesYAt = 2
                }
            };
    
            AxisYCollection = new AxesCollection
            {
                new Axis { Title = "Y Axis 1", Foreground = Brushes.Gray },
                new Axis { Title = "Y Axis 2", Foreground = Brushes.Red },
                new Axis { Title = "Y Axis 3", Foreground = Brushes.Brown }
            };
        }
    
        public AxesCollection AxisYCollection { get; set; }
    
        public SeriesCollection SeriesCollection { get; set; }
    
    }
    

    while the XAML will be:

    <Grid>
        <lvc:CartesianChart Series="{Binding SeriesCollection}" AxisY="{Binding AxisYCollection}" />
    </Grid>
    

    Of course you need to set an instance of ViewModel class as the DataContext of your Window:

    public MainWindow()
    {
        vm = new ViewModel();
    
        InitializeComponent();
        DataContext = vm;
    }
    

    If you do not declare "enough" Axis in the binded AxesCollection, its element at n index won't be found and you will slip into an ArgumentOutOfRangeException. I hope it can help you.