1

i got a Little "Problem", i want to create a Chart looking like this:

enter image description here

So basically Series 1 = Normal bar Chart. Color green if it Ends before the "time max" (series2) Series 2 = just a DataPoint / Marker on top of series 1 items.

I am struggling with this though...

my Code:

        chart_TimeChart.Series.Clear();
        string series_timeneeded = "Time Needed";
        chart_TimeChart.Series.Add(series_timeneeded);
        chart_TimeChart.Series[series_timeneeded]["PixelPointWidth"] = "5";
        chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.Size = 10;
        chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.ButtonStyle =    ScrollBarButtonStyles.SmallScroll;
        chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.IsPositionedInside = true;
        chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.Enabled = true;
        chart_TimeChart.Series[series_timeneeded].BorderWidth = 2;
        chart_TimeChart.Series[series_timeneeded].ChartType = SeriesChartType.StackedBar; 
        chart_TimeChart.Series[series_timeneeded].YValueType = ChartValueType.Time;
        chart_TimeChart.ChartAreas[0].AxisY.LabelStyle.Format = "HH:mm:ss";
        chart_TimeChart.Series[series_timeneeded].XValueType = ChartValueType.String;

            for (int i = 0; i < MaxNumber; i++) 
                {
                    chart_TimeChart.Series[series_timeneeded].Points.AddXY("item"+ " " + (i + 1).ToString(), DateTime.Now.Add(Timespans[i]));
                }

        chart_TimeChart.Series.Add(series_FinishTime);
        chart_TimeChart.Series[series_FinishTime].ChartType = SeriesChartType.StackedBar;
        chart_TimeChart.Series[series_FinishTime].BorderWidth = 0;
        chart_TimeChart.Series[series_FinishTime].MarkerSize = 15;
        chart_TimeChart.Series[series_FinishTime].MarkerStyle = MarkerStyle.Square;
        chart_TimeChart.Series[series_FinishTime].MarkerColor = Color.Black;

        chart_TimeChart.Series[series_FinishTime].YValueType = ChartValueType.DateTime;
        chart_TimeChart.Series[series_FinishTime].XValueType = ChartValueType.String;

        for (int i = 0; i < MaxNumber; i++) 
                {
                    DateTime YPosition = GetFinishTime(i);

                    chart_TimeChart.Series[series_FinishTime].Points.AddXY("item"+ " " +(i+1).ToString(), YPosition);
                }

but this only Displays the 2nd series on top of the first one but the first one isnt visible anymore. The Maker of series 2 isnt shown but instead the bar is (eventhough i made borderwidth to 0). In my opinion/thinking i just have to make the "bar" of series 2 invisible and just Show the marker Points for series 2.

Any ideas?


Update:

string seriesname = Name+ i.ToString();
                    chart_TimeChart.Series.Add(seriesname);
                    chart_TimeChart.Series[seriesname].SetCustomProperty("DrawSideBySide", "false");
                    chart_TimeChart.Series[seriesname].SetCustomProperty("StackedGroupName", seriesname);

                    chart_TimeChart.Series[seriesname].ChartType = SeriesChartType.StackedBar; //Y and X are exchanged
                    chart_TimeChart.Series[seriesname].YValueType = ChartValueType.Time;
                    chart_TimeChart.ChartAreas[0].AxisY.LabelStyle.Format = "HH:mm:ss";
                    chart_TimeChart.Series[seriesname].XValueType = ChartValueType.String;

                    DateTime TimeNeeded = DateTime.Now.Add(List_AllLiniengroupsTimespans[k][i]); 
                    DateTime TimeMax = GetFinishTime(k, i); 
                    TimeSpan TimeDifference = TimeNeeded - TimeMax;

                    if (TimeNeeded > TimeMax) //All good
                    {
                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded); //Time till finish
                        chart_TimeChart.Series[seriesname].Points[0].Color = Color.Blue; 
                        chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);

                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded.Add(TimeDifference)); //time left
                        chart_TimeChart.Series[seriesname].Points[1].Color = Color.Red;
                        chart_TimeChart.Series[seriesname].Points[1].SetCustomProperty("StackedGroupName", seriesname);

                    }
                    else if (TimeMax > TimeNeeded) //wont make it in time
                    {
                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded); //time till still okay
                        chart_TimeChart.Series[seriesname].Points[0].Color = Color.Blue; 
                        chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);

                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded.Add(TimeDifference)); //Time that is too much
                        chart_TimeChart.Series[seriesname].Points[1].Color = Color.Green; 
                        chart_TimeChart.Series[seriesname].Points[1].SetCustomProperty("StackedGroupName", seriesname);

                    }
                    else if (TimeMax == TimeNeeded) //fits exactly
                    {
                        chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded); 
                        chart_TimeChart.Series[seriesname].Points[0].Color = Color.DarkOrange; 
                        chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);

                    }

the Code will be displayed as:

enter image description here

but i want it to look like this:

enter image description here

christian890
  • 147
  • 1
  • 12
  • btw. i tried " chart_TimeChart.Series[series_FinishTime]["PixelPointWidth"] = "0"; but apparently this does not work =/ since i can´t set the width to Zero. – christian890 Apr 20 '18 at 09:10
  • 1
    Do have a look at the updated answer. I misread you question at first, sorry! – TaW Apr 22 '18 at 08:56
  • You still have the charttype as stackedbars. I don't think it actually is what you want. And you still add x-values as strings. You need numbers, though. You need to think of a scheme to use simple numbers to group the points to their respective slots. – TaW Apr 23 '18 at 08:17

1 Answers1

3

!! See the update below !!


If you really want to create a StackedBar chart, your chart has two issues:

  • If you want to stack datapoints they need to have meaningful x-values; without them how can it know what to stack on each other?

You add strings, which look fine but simply don't work. That is because the DataPoint.XValue field is double and when you add string into it it is set to 0 !! Your string is copied to the Label but otherwise lost.

So you need to come up with a suitable numeric value you use for the x-values..

  • And you also need to group the series you want to stack. For this there is a special property called StackedGroupName which serves to group those series that shall be stacked.

Here is how you can use it:

yourSeries1.SetCustomProperty("StackedGroupName", "Group1");

For a full example see this post !

It also shows one way of setting the Labels with string values of your choice..

This is the way to go for real StackedBar charts. Your workaround may or may not work. You could try to make the colors transparent or equal to the chart's backcolor; but it won't be more than a hack, imo.


Update

I guess I have misread the question. From what I see you do not really want to create a stacked chart.

Instead you struggle with these issues:

  • displaying bars at the same y-spot
  • making some bars invisible
  • displaying a vertical line as a marker

Let's tackle each:

  • Some column types including all Bars, Columns and then some have a little known special property called DrawSideBySide.

By default is is set to Auto, which will work like True. This is usually fine as we don't want bars to sit upon each other, effectively hiding all or part of the overlaid points.

But here we do want them to share the same y-position, so we need to set the property to false for at least one Series; the others (on Auto) will follow..:

You can do it either like this:

aSeries["DrawSideBySide"] = "false";

or like this:

aSeries.SetCustomProperty("DrawSideBySide", "false");

  • Next we hide the overlaid Series; this is simple:

aSeries.Color = Color.Transparent;
  • The last issue is displaying a line marker. There is no such MarkerStyle, so we need to use a custom style. For this we need to create a suitable bitmap and add it as a NamedImage to the chart's Images collection.

This sounds more complicated than it is; however the MarkerImage will not be scaled, so we need to created suitable sizes whenever we resize the Chart or add/remove points. I will ignore this complication for now..

int pointCount = 10;
Bitmap bmp = new Bitmap(2, chart.ClientSize.Height / pointCount - 5);
using (Graphics g = Graphics.FromImage(bmp)) g.Clear(Color.Black);
NamedImage marker = new NamedImage("marker", bmp);
chart.Images.Clear();  // quick & dirty
chart.Images.Add(marker);

Here is the result:

enter image description here

A few notes:

  • I would recommend to use variables for all chart elements you refer to repeatedly instead of using indexed references all the time. Less code, easier to read, a lot easier to maintain, and probably better performance.

  • Since your code called for the visible datapoints to be either red or green the Legend will not show a good representation. See here for an example of drawing a multi-colored legend item..

  • I used the chart height; this is not really recommended as there may be Titles or Legends or even more ChartAreas; instead you should use the height of the ChartArea, or even more precise, the height of the InnerPlotPosition. You would need to convert those from percentages to pixels. Not too hard, see below or see here or here for more examples!

  • The markers should be adapted from the Resize and probably from the AxisViewChanged events. Putting it in a nice function to call (e.g. void setMarkerImage(Chart chart, Series s, string name, int width, Color c)) is always a good idea.

  • If you need to adapt the size of the marker image repeatedly, you may want to write better code for clearing the old one; this should include disposing of the Bitmap that was used before..

Here is an example:

var oldni = chart.Images.FindByName("marker");
if (oldni != null)
{
    oldni.Image.Dispose();
    chart.Images.Remove(oldni);
    oldni.Dispose();
}
  • In some situations one needs to nudge the Chart to update some of its properties; RecalculateAxesScale is one such nudge.

Example for calculating a suitable marker height:

ChartArea ca = chart.ChartAreas[0];
ca.RecalculateAxesScale();
float cah = ca.Position.Height;
float iph = ca.InnerPlotPosition.Height;
float h = chart3.ClientSize.Height * cah / 100f * iph / 100f;
int mh = (int)(h / s.Points.Count);
  • Final note: The original answer stressed the importance of using meaningful x-values. Strings are useless! This was important for stacking bars; but it is equally important now, when we want bars to sit at the same vertical positions! Adding the x-values as strings is again resulting in nonsense..

(Since we have Bars the x-values go along the vertical axis and vice versa..)

TaW
  • 53,122
  • 8
  • 69
  • 111
  • Hello, sorry for the delayed answer! I tried a lot and i get what you mean. I updated my original post. I still have the Problem with the strings on my "x-Axis" eventhough i now add only the Points that should be stacked to the same series and create multiple series , one forfor each item. Why is that the case? and how do i solve this? THANK YOU SO MUCH for the effort! it really helps! – christian890 Apr 23 '18 at 08:14
  • Is `seriesname` a number? That would help. Not sure, the code is incomplete at the start; maybe `i` is the series index? If so you could maybe use it as x-value [DrawSideBySide](https://msdn.microsoft.com/en-us/library/dd456710.aspx) is only valid for some types. – TaW Apr 23 '18 at 08:22
  • Ah i got it now, the Problem is still with the strings! Damn me, let me try it with double instead =) – christian890 Apr 23 '18 at 08:23
  • 1
    Yes, and make all series Bars, not StackedBars so DrawSideBSide can make them overlay.. – TaW Apr 23 '18 at 08:26
  • 1
    With double it works fine, just the order is turned (Point 0 is placed behind Point 1) but this is no Problem. I think i understood and got it now! Thank you! I will answer again in some minutes =) – christian890 Apr 23 '18 at 08:30
  • I have a Little Problem with the full Code , i set the Y.LabelStyle to "HH:mm:ss" and then mark the Points. But the Diagramm of Course always starts at the time of "00:00:00" is there any way to let it start at a specific time? for example current date? I tried chart_TimeChart.ChartAreas[0].AxisY.Minimum = Convert.ToDouble(DateTime.Now.TimeOfDay); but i can´t convert TimeSpan to Double. And the requested DataType by the Debugger is double (eventhough i have "HH:mm:ss") i know ist a bit of Topic but any idea? – christian890 Apr 23 '18 at 09:05
  • 1
    Not sure of what you want but there are functions to convert between DateTime and Double: `DateTime dt = DateTime.Now; double dd = dt.ToOADate(); DateTime ddt = DateTime.FromOADate(dd);` But TimeSpan is only a relative thing and can't be converted to an absolute datetetime. – TaW Apr 23 '18 at 09:13
  • 1
    Sorry for the delayed Response. Everything is working just fine! And i played a bit with what you said, you were just right! Thank you so much! I really appreciate it! Have a nice day! – christian890 Apr 24 '18 at 07:20