Search code examples
c#.netwpfxamloxyplot

IndexOutOfRangeException when trying to set CategoryIndex in a loop


I'm trying to draw many IntervalBarItems in one line and for several categories. In Oxyplot Categoryaxis draws foreach IntervalBarSeries one IntervalBarItem into the line. So normaly If I have three Series, each with 4 Items - I get 4 Categories with 3 BarItems (Series1, Series2, Series3). But I want 3 Categories - each for one Series with the 4 items in the line - I can get this result by setting the CategoryIndex-Property for each Item. And here I get IndexOutOfRangeException - by setting the Index-Property in a Loop.

MCVE:

xaml-file:

<Window x:Class="BarSeries_Stacked.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:BarSeries_Stacked"
        xmlns:oxy="http://oxyplot.org/wpf"
        mc:Ignorable="d"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <oxy:PlotView Name="plot" />
    </Grid>
</Window>

cs-file:

namespace BarSeries_Stacked
{
    /// <summary>
    /// Interaktionslogik für MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            SetUpPlot();
        }

        private void SetUpPlot()
        {

            DateTime start = new DateTime(2017, 1, 1, 15, 0, 0);
            DateTime start1 = new DateTime(2017, 1, 1, 15, 15, 0);
            DateTime start2 = new DateTime(2017, 1, 1, 15, 20, 0);
            DateTime start3 = new DateTime(2017, 1, 1, 15, 45, 0);

            DateTime end = new DateTime(2017, 1, 1, 15, 10, 0);
            DateTime end1 = new DateTime(2017, 1, 1, 15, 17, 0);
            DateTime end2 = new DateTime(2017, 1, 1, 15, 30, 0);
            DateTime end3 = new DateTime(2017, 1, 1, 15, 59, 0);

            var model = new PlotModel();
            model.IsLegendVisible = false;

            model.Axes.Add(new OxyPlot.Axes.DateTimeAxis() { Position = AxisPosition.Bottom });
            model.Axes.Add(new OxyPlot.Axes.CategoryAxis() { Position = AxisPosition.Left });
            plot.Model = model;


            var series = new OxyPlot.Series.IntervalBarSeries { Title = "Series 1", StrokeThickness = 1 };
            model.Series.Add(series);

            series.Items.Add(new IntervalBarItem { CategoryIndex = 0, Start = start.ToOADate(), End = end.ToOADate() });
            series.Items.Add(new IntervalBarItem { CategoryIndex = 0, Start = start1.ToOADate(), End = end1.ToOADate() });

            for (int i = 0; i < 10; i++)
            {
                var series2 = new OxyPlot.Series.IntervalBarSeries { Title = "Series "+i.ToString(), StrokeThickness = 1 };
                series2.Items.Add(new IntervalBarItem { CategoryIndex = i, Start = start2.AddHours(i).ToOADate(), End = end2.AddHours(i).ToOADate() });
                series2.Items.Add(new IntervalBarItem { CategoryIndex = i, Start = start3.AddHours(i).ToOADate(), End = end3.AddHours(i).ToOADate() });
                model.Series.Add(series2);
            }

        }
    }
}

Just for testing put some other index (e.g. 1) as CategoryIndex


Solution

  • It depends on what exactly you're trying to do, but one way of fixing your loop would be:

    for (int i = 0; i < 10; i++)
    {
        var series2 = new OxyPlot.Series.IntervalBarSeries { Title = "Series " + i.ToString(), StrokeThickness = 1 };
        model.Series.Add(series2);
        for (int j = 0; j < i; j++)
            series2.Items.Add(new IntervalBarItem { CategoryIndex = j, Start = start2.AddHours(i).ToOADate(), End = end2.AddHours(i).ToOADate() });
    }
    

    enter image description here

    The core issue here is that you can't add, for instance, CategoryIndex = 8, if you are adding only 2 IntervalBarItem per series.

    Another way of fixing your loop:

    for (int i = 0; i < 10; i++)
    {
        var series2 = new OxyPlot.Series.IntervalBarSeries { Title = "Series " + i.ToString(), StrokeThickness = 1 };
        series2.Items.Add(new IntervalBarItem { CategoryIndex = 0, Start = start2.AddHours(i).ToOADate(), End = end2.AddHours(i).ToOADate() });
        series2.Items.Add(new IntervalBarItem { CategoryIndex = 1, Start = start3.AddHours(i).ToOADate(), End = end3.AddHours(i).ToOADate() });
        model.Series.Add(series2);
    }
    

    enter image description here

    And one more, just to make sure:

    for (int i = 0; i < 10; i++)
    {
        var series2 = new OxyPlot.Series.IntervalBarSeries { Title = "Series " + i.ToString(), StrokeThickness = 1 };
        for (int j = 0; j < random.Next(0, i); j++)
            series2.Items.Add(new IntervalBarItem { CategoryIndex = j, Start = start2.AddHours(i).ToOADate(), End = end2.AddHours(i).ToOADate() });
        model.Series.Add(series2);
    }
    

    enter image description here

    EDIT: one series for one category:

     for (int i = 0; i < 10; i++)
            {
                var series2 = new OxyPlot.Series.IntervalBarSeries { Title = "Series " + i.ToString(), StrokeThickness = 1 };
                model.Series.Add(series2);
                for (int j = 0; j < i+1; j++)
                    series2.Items.Add(new IntervalBarItem { CategoryIndex = i, Start = start2.AddHours(j).ToOADate(), End = end2.AddHours(j).ToOADate() });
            }
    

    enter image description here