1

I would like to display the 2 different stacked area elements according to their parameters. But the chart area displays it not as specified and puts the second block at the top right corner of the first stacked area. They should be displayed side by side not stacked.

...
using System.Windows.Forms.DataVisualization.Charting;

namespace Gantt_Tool
{
public partial class ReadModel : Form
{
    public ReadModel()
    {
        InitializeComponent();
        CreateChart();
    }

    private void ReadModel_Load(object sender, EventArgs e)
    {            
    }

    private void CreateChart()
    {
        chart1.Series.Add($"a");
        chart1.Series[$"a"].Points.Add(new DataPoint(0, 2));
        chart1.Series[$"a"].Points.Add(new DataPoint(2, 2));
        chart1.Series[$"a"].ChartType = SeriesChartType.StackedArea;

        chart1.Series.Add($"b");
        chart1.Series[$"b"].Points.Add(new DataPoint(2, 3));
        chart1.Series[$"b"].Points.Add(new DataPoint(5, 3));
        chart1.Series[$"b"].ChartType = SeriesChartType.StackedArea;    
    }
}    

https://i.stack.imgur.com/FHuRT.png

How can I set the blocks to a side by side order or placed freely? And how can I get unfilled rectangles?

Update: Here is an example of how it should look like:

enter image description here

TaW
  • 53,122
  • 8
  • 69
  • 111
Amörg
  • 23
  • 2
  • Why do you chose stackedarea if you don't want the areas to be stacked??? use Area instead! – TaW Dec 09 '18 at 14:33
  • I want areas to be stacked. If you look at the image it is stacked at (2,2) and not at (2,0) as actually intended. – Amörg Dec 10 '18 at 08:22
  • But 'stacking' means that the points of the series > 0 are placed __on top__ of the points of the previous series. Their y-value is not the location but their height. and if the x-values do not correspond, as in your case, the points/areas will be displaced.. – TaW Dec 10 '18 at 09:21
  • Is there a different series chart type, that maybe consists of four points/parameters, that lets me display rectangles instead of acting like the area chart type.?Because the area chart type fills the whole space of the box specified by two data points. – Amörg Dec 10 '18 at 10:39
  • Indeed Area like StackedArea will only draw filled areas, not necessarily rectangles btw. Unfortunately when drawing borders it only draws the top line, so choosing a transparent color plus a border will not help. The best way for unfilled rectangles would be to owner-draw them in a PaintXXX event. If you want to I can show you an example.. As usual with chart questions an image of the desired result would be most helpful. – TaW Dec 10 '18 at 10:55
  • that would be really great. I would want it to look like [this](https://i.stack.imgur.com/FHuRT.png) just block be is pinned to the x-axis and not stacked. Will your explanation contain a chart area? – Amörg Dec 10 '18 at 11:02
  • 1) That image contains filled boxes. Is that what you want or not?? 2) Note the when using non-transparent colors the block will sometimes will totally cover each other. Using a semi-transparent color will help at least to understand what is going on. 3) Also note that area means the __combination__ of one or more areas, not separate rectangles. Which is why the sidelines are not shown. To draw them the chart would simply have code in the e.g. PrePaint event.. – TaW Dec 10 '18 at 11:11
  • 1) The intended solution would look like boxes, that only have borders and a label inside. Those boxes are not allowed to overlap, so [this] (https://imgur.com/a/iGLV5xk) is how it should look like at the end. – Amörg Dec 10 '18 at 11:24
  • This kind of chart is not supported in MSChart. If you want to you can (ab)use one of the charttypes with multiple y-values to store the coordinates and owner-draw the boxes. Or Add the 2nd point as a Tag to the 1st. Both would involve a special AddPoint method that transforms the values to DataPoints making the actual series invisible and drawing the boxes.. You would also have to take care of the overlapping issue! – TaW Dec 10 '18 at 11:42
  • Do you happen to know a better class library or something similair to construct such a graph consisting of rectangles? – Amörg Dec 10 '18 at 11:47
  • Nope, I don't, sorry. – TaW Dec 10 '18 at 11:57
  • I wonder why you call this a Gantt_Tool; [Gantt chart](https://en.wikipedia.org/wiki/Gantt_chart) doesn't look like this at all.. If you want I'll post an example of drawing boxes with centered labels into a regula MSChart point type.. – TaW Dec 10 '18 at 12:34
  • I am callig it a Gantt-Chart because these rectangles represent activities scheduled within a given interval of time. But instead of having the task name as y-coordinate, resources are used to determine the y-coordinates. I admit it doesn't look like a typical gant chart. But thank you for your time! – Amörg Dec 10 '18 at 12:56
  • I took the liberty to edit the question to match the requirements from your comments. I have also added a function to calculate the axes sizes.. – TaW Dec 10 '18 at 14:43
  • Thank you very much for your efforts! – Amörg Dec 10 '18 at 16:32

1 Answers1

2

From your comments, I take that you want to have a chart with freely-placed, unfilled rectangles and labels.

None of the MSChart types will do that.

Here is how to use a Point chart with a few lines of owner-drawing. Note how nicely this will behave when resizing the chart...

enter image description here

Here is the setup:

Axis ax = chart1.ChartAreas[0].AxisX;
Axis ay = chart1.ChartAreas[0].AxisY;
ax.Maximum = 9;  // pick or calculate
ay.Maximum = 6;  // minimum and..
ax.Interval = 1; // maximum values..
ay.Interval = 1; // .. needed
ax.MajorGrid.Enabled = false;
ay.MajorGrid.Enabled = false;

Series s1 = chart1.Series.Add("A");
s1.ChartType = SeriesChartType.Point;

Now we add your five boxes. I use a sepcial function that adds the points and stuffs the box size into the Tag of each point..:

AddBox(s1, 1, 0, 3, 1, "# 1");
AddBox(s1, 2, 1, 2, 2, "# 2");
AddBox(s1, 4, 0, 4, 2, "# 3");
AddBox(s1, 4, 2, 2, 2, "# 4");
AddBox(s1, 4, 4, 1, 1, "# 5");

int AddBox(Series s, float x, float y, float w, float h, string label)
{
    return AddBox(s, new PointF(x, y), new SizeF(w, h), label);
}


int AddBox(Series s, PointF pt, SizeF sz, string label)
{
    int i = s.Points.AddXY(pt.X, pt.Y);
    s.Points[i].Tag = sz;
    s.Points[i].Label = label;
    s.Points[i].LabelForeColor = Color.Transparent;
    s.Points[i].Color = Color.Transparent;
    return i;
}

The drawing is also simple; only the use of the Axes function ValueToPixelPosition is noteworthy..:

private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
{
    if (chart1.Series[0].Points.Count <= 0) return;

    Axis ax = chart1.ChartAreas[0].AxisX;
    Axis ay = chart1.ChartAreas[0].AxisY;
    Graphics g = e.ChartGraphics.Graphics;
    using (StringFormat fmt = new StringFormat()
    { Alignment = StringAlignment.Center, 
      LineAlignment = StringAlignment.Center})
        foreach (Series s in chart1.Series)
        {
            foreach (DataPoint dp in s.Points)
            {
            if (dp.Tag == null) break;
            SizeF sz = (SizeF)dp.Tag;
            double vx2 = dp.XValue + sz.Width;
            double vy2 = dp.YValues[0] + sz.Height;
            int x1 = (int)ax.ValueToPixelPosition(dp.XValue);
            int y1 = (int)ay.ValueToPixelPosition(dp.YValues[0]);
            int x2 = (int)ax.ValueToPixelPosition(vx2);
            int y2 = (int)ay.ValueToPixelPosition(vy2);
            Rectangle rect = Rectangle.FromLTRB(x1, y2, x2, y1);

            using (Pen pen = new Pen(s.Color, 2f))
                g.DrawRectangle(pen, rect);
            g.DrawString(dp.Label, Font, Brushes.Black, rect, fmt);
            }
        }
}

Here is a little Linq to calculate the Minimum and Maximum values for the Axes to hold just the right size; chart won't do it by itself since the size in the tags of the points is not known...

private void setMinMax(Chart chart, ChartArea ca)
{
    var allPoints = chart.Series.SelectMany(x => x.Points);
    double minx = allPoints.Select(x => x.XValue).Min();
    double miny = allPoints.Select(x => x.YValues[0]).Min();
    double maxx = allPoints.Select(x => x.XValue + ((SizeF)x.Tag).Width).Max();
    double maxy = allPoints.Select(x => x.YValues[0] + ((SizeF)x.Tag).Height).Max();

    ca.AxisX.Minimum = minx;
    ca.AxisX.Maximum = maxx;
    ca.AxisY.Minimum = miny;
    ca.AxisY.Maximum = maxy;
}
TaW
  • 53,122
  • 8
  • 69
  • 111
  • I have tried to implement your code in my project but cannot execute it properly. It seems every method is working fine, just the chart2_Postpaint-method is not working. How or where is this method called? – Amörg Jan 11 '19 at 16:05
  • Chart will call it whenever it needs to redraw itself. Is it called? (Use debugger?) If not: it it hooked up? – TaW Jan 11 '19 at 16:39