Search code examples
.net-4.0mschart

Microsoft chart stacked column chart has gaps


I'm using the Chart library in .Net 4.0 to create a stacked column chart with several series. My goal is a histogram that shows the cumulative number of actions (report completions) per day across several series (teachers). There is often missing data (no activity that day by a particular teacher).

I get gaps in the bars when there is missing data in a series:

Histogram with gaps in the columns

My code:

    public ActionResult CompletionHistogram(int sid, int width, int height)
    {
        Site site = SiteRepository.Get(sid);
        if (site == null)
            return new HttpNotFoundResult();

        Chart chart = new Chart();
        chart.Height = height;
        chart.Width = width;
        ChartArea area = chart.ChartAreas.Add("Default");

        // Treat each teacher as a series
        foreach (Teacher t in site.Teachers)
        {
            Series series = chart.Series.Add(t.FullName);
            series.ChartType = SeriesChartType.StackedColumn;
            series.Name = t.FullName;

            // Group completions by day (filter out incomplete reports and null timestamps)
            var groups = t.StudentReports
                .Where<StudentReport>(rep => rep.IsComplete && rep.FirstSaveTimestamp.HasValue)
                .GroupBy<StudentReport, DateTime>(rep => rep.FirstSaveTimestamp.Value.Date);

            bool hasPoints = false;
            foreach (var g in groups)
            {
                series.Points.AddXY(g.Key, g.Count());
                hasPoints = true;
            }

            series.IsValueShownAsLabel = true;
            series.ToolTip = "#VALX";

            if (hasPoints)
                chart.DataManipulator.InsertEmptyPoints(1, IntervalType.Days, series);
        }


        area.AxisX.LabelStyle.Format = "ddd M/d";
        return new ChartResult(chart);
    }

How can I remove the


Solution

  • I have a way to make this work.

    You need to set the point to zero manually as you are adding points. Note: I was not able to make this work by adding the zero points after the fact.

    See the next to examples and resulting screenshots: chart1.Series.Clear(); chart1.Series.Add(new Series()); chart1.Series.Add(new Series()); chart1.Series.Add(new Series()); chart1.Series.Add(new Series());

    foreach (Series s in chart1.Series)
    {
        s.ChartType = SeriesChartType.StackedColumn;
    }
    
    //chart1.Series[0].Points.Add(new DataPoint(0, 3));
    chart1.Series[0].Points.Add(new DataPoint(1, 3));
    chart1.Series[0].Points.Add(new DataPoint(2, 3));
    chart1.Series[0].Points.Add(new DataPoint(3, 3));
            
    chart1.Series[1].Points.Add(new DataPoint(0, 3));
    //chart1.Series[1].Points.Add(new DataPoint(1, 3));
    chart1.Series[1].Points.Add(new DataPoint(2, 3));
    chart1.Series[1].Points.Add(new DataPoint(3, 3));
    
    chart1.Series[2].Points.Add(new DataPoint(0, 3));
    chart1.Series[2].Points.Add(new DataPoint(1, 3));
    //chart1.Series[2].Points.Add(new DataPoint(2, 3));
    chart1.Series[2].Points.Add(new DataPoint(3, 3));
    
    chart1.Series[3].Points.Add(new DataPoint(0, 3));
    chart1.Series[3].Points.Add(new DataPoint(1, 3));
    chart1.Series[3].Points.Add(new DataPoint(2, 3));
    //chart1.Series[3].Points.Add(new DataPoint(3, 0));
            
    chart1.SaveImage("C:\\Before.png", ChartImageFormat.Png);
    

    Image of "before.png": enter image description here

    chart1.Series.Clear();
    chart1.Series.Add(new Series());
    chart1.Series.Add(new Series());
    chart1.Series.Add(new Series());
    chart1.Series.Add(new Series());
    
    foreach (Series s in chart1.Series)
    {
        s.ChartType = SeriesChartType.StackedColumn;
    }
    
    chart1.Series[0].Points.Add(new DataPoint(0, 0));
    chart1.Series[0].Points.Add(new DataPoint(1, 3));
    chart1.Series[0].Points.Add(new DataPoint(2, 3));
    chart1.Series[0].Points.Add(new DataPoint(3, 3));
    
    chart1.Series[1].Points.Add(new DataPoint(0, 3));
    chart1.Series[1].Points.Add(new DataPoint(1, 0));
    chart1.Series[1].Points.Add(new DataPoint(2, 3));
    chart1.Series[1].Points.Add(new DataPoint(3, 3));
    
    chart1.Series[2].Points.Add(new DataPoint(0, 3));
    chart1.Series[2].Points.Add(new DataPoint(1, 3));
    chart1.Series[2].Points.Add(new DataPoint(2, 0));
    chart1.Series[2].Points.Add(new DataPoint(3, 3));
    
    chart1.Series[3].Points.Add(new DataPoint(0, 3));
    chart1.Series[3].Points.Add(new DataPoint(1, 3));
    chart1.Series[3].Points.Add(new DataPoint(2, 3));
    chart1.Series[3].Points.Add(new DataPoint(3, 0));
    
    chart1.SaveImage("C:\\After.png", ChartImageFormat.Png);
    

    Image of "after.png": enter image description here

    So, given that you can not add the zero points after the fact (although you may be able to insert them?) you will need to modify your code to something like this:

    var allPossibleGroups = t.StudentReports;
    
    var groups = t.StudentReports
                .Where<StudentReport>(rep => rep.IsComplete && rep.FirstSaveTimestamp.HasValue)
                .GroupBy<StudentReport, DateTime>(rep => rep.FirstSaveTimestamp.Value.Date);
    
            bool hasPoints = false;
            foreach (var g in allPossibleGroups)
            {
                if(groups.ContainsKey(g))
                {
                    series.Points.AddXY(g.Key, g.Count());
                    hasPoints = true;
                }
                else
                {
                    series.Points.AddXY(g.Key, 0);
                }
            }
    

    Sorry for the long code blocks, but the example was necessary to demonstrate how to make it work, without falling into the trap of adding the empty points (where y = 0) at the end, as that will not work.

    Let me know if you need more help.