Create a stacked bar chart - c#

I'm trying to create a stacked bar chart ,
But I'm getting an error in the lines that has ".Series " (How to define the Series ? )
SeriesChartType chart1 = new SeriesChartType();
// Populate series data
Random random = new Random();
for (int pointIndex = 0; pointIndex < 10; pointIndex++)
{
chart1.Series["LightBlue"].Points.AddY(random.Next(45, 95));
}
// Set chart type
chart1.Series["LightBlue"].ChartType = SeriesChartType.StackedArea100;
// Show point labels
chart1.Series["LightBlue"].IsValueShownAsLabel = true;
// Disable X axis margin
chart1.ChartAreas["Default"].AxisX.IsMarginVisible = false;
// Set the first two series to be grouped into Group1
chart1.Series["LightBlue"]["StackedGroupName"] = "Group1";
chart1.Series["Gold"]["StackedGroupName"] = "Group1";
// Set the last two series to be grouped into Group2
chart1.Series["Red"]["StackedGroupName"] = "Group2";
chart1.Series["DarkBlue"]["StackedGroupName"] = "Group2";

The source code above seems to be from the MS Chart Samples application. Looking at the MS example stacked bar chart on screen, and the source code above, its obvious that the sample code is inadequate and does not show us how to do stacked bar charts.
You can either programmatically create and attach a series:
Series s1 = new Series("LightBlue");
s1.ChartType = SeriesChartType.StackedBar100;
chart1.Series.Add(s1);
or alternatively, you can define the series in your ASPX file and simple add Y values to each series in the code behind:
Random random = new Random();
for(int pointIndex = 0; pointIndex < 10; pointIndex++)
{
Chart1.Series["Series1"].Points.AddY(Math.Round((double)random.Next(45, 95),0));
Chart1.Series["Series2"].Points.AddY(Math.Round((double)random.Next(5, 75),0));
Chart1.Series["Series3"].Points.AddY(Math.Round((double)random.Next(5, 95),0));
Chart1.Series["Series4"].Points.AddY(Math.Round((double)random.Next(35, 95),0));
}
In the MS Chart Samples web solution, have a look at
/ChartTypes/BarColumnCharts/Stacked/stackedchart.aspx
it should have all you need.

Related

C# DataVisualization.Charting.Chart not aggregating x axis

I'm using the Winform DataVisualization.Charting.Chart control. I have added 3 data series into the default chartarea. Each series has an integer y value and a Datetime x value.
If I show any one data series, it works as expected; however, if I combine the data series the dates overlap and are not in chronological order thus making the chart useless. I've hard-coded the values just for a sanity check but still see the same issue.
If anyone knows for a way for this chart to have a single x axis timeline that all data series will just plot the y value onto, it would be greatly appreciated. I've run out of things to try. (Image include below...)
chrtSessions.ChartAreas.Clear();
chrtSessions.ChartAreas.Add(new ChartArea("Default"));
chrtSessions.Series.Clear();
chrtSessions.Titles.Clear();
chrtSessions.Titles.Add("Hours Left For Licensure");
chrtSessions.ChartAreas["Default"].AxisY.Title = "Hours";
chrtSessions.ChartAreas["Default"].AxisX.Title = "Dates";
chrtSessions.ChartAreas["Default"].AxisX.IntervalType = DateTimeIntervalType.Days;
chrtSessions.ChartAreas["Default"].AxisX.LabelStyle.Format = "DD/MM/YYYY";
chrtSessions.Series.Add("Individual");
chrtSessions.Series["Individual"].AxisLabel = "Individual Sessions";
chrtSessions.Series["Individual"].ChartType = SeriesChartType.Line;
chrtSessions.Series["Individual"].BorderWidth = 4;
chrtSessions.Series["Individual"].XValueType = ChartValueType.Date;
for (int index = 0; index < individualData.hours.Count; index++)
{
chrtSessions.Series["Individual"].Points.AddXY(individualData.dates[index].ToShortDateString(), individualData.hours[index]);
}
chrtSessions.Series.Add("Relational");
chrtSessions.Series["Relational"].AxisLabel = "Relational Sessions";
chrtSessions.Series["Relational"].ChartType = SeriesChartType.Line;
chrtSessions.Series["Relational"].BorderWidth = 4;
chrtSessions.Series["Relational"].XValueType = ChartValueType.Date;
for (int index = 0; index < relationalData.hours.Count; index++)
{
chrtSessions.Series["Relational"].Points.AddXY(relationalData.dates[index].ToShortDateString(), relationalData.hours[index]);
}
chrtSessions.Series.Add("Supervision");
chrtSessions.Series["Supervision"].AxisLabel = "Supervision Sessions";
chrtSessions.Series["Supervision"].ChartType= SeriesChartType.Line;
chrtSessions.Series["Supervision"].BorderWidth = 4;
chrtSessions.Series["Supervision"].XValueType = ChartValueType.Date;
for (int index = 0; index < supervisionData.hours.Count; index++)
{
chrtSessions.Series["Supervision"].Points.AddXY(supervisionData.dates[index].ToShortDateString(), supervisionData.hours[index]);
}
Notice on this image that the Date 11/19/2021 occurs twice, but with 11/20/2021 occurring in-between and only yellow dataset actually has data that goes to the 23rd (data should show from 10/29-11/23). The blue line should span 10/31-11/20 and the red line should span from 10/26-11/19.

smoothing stacked line graph

I have a c# Windows forms application that generates a stacked line graph using the standard MS Chart control, as in the below example.
Is there a way of "smoothing" the lines by formatting the series or some other property?
Looking at MSDN and Google I can't seem to find a way to do this, in Excel there a series.Smooth property...
I have I missed it or is it not possible?
If you liked the smooth look of the SplineAreas you can calculate the necessary values to get just that look:
A few notes:
I have reversed the order of the series; many ways to get the colors right.. (Instead one probably should accumulate in reverse)
The stacked DataPoints need to be aligned, as usual and any empty DataPoints should have their Y-Values to be 0.
Of course in the new series you can't access the actual data values any longer as you now have the accumulated values instead; at least not without reversing the calulation. So if need them keep them around somewhere. The new DataPoints' Tag property is one option..
You can control the ' smoothness' of each Series by setting its LineTension custom attribute:
chart2.Series[0].SetCustomProperty("LineTension", "0.15");
Here is the full example code that creates the above screenshots calculating a 'stacked' SplineArea chart2 from the data in a StackedArea chart1:
// preparation
for (int i = 0; i < 4; i++)
{
Series s = chart1.Series.Add("S" + i);
s.ChartType = SeriesChartType.StackedArea;
Series s2 = chart2.Series.Add("S" + i);
s2.ChartType = SeriesChartType.SplineArea;
}
for (int i = 0; i < 30; i++) // some test data
{
chart1.Series[0].Points.AddXY(i, Math.Abs(Math.Sin(i / 8f)));
chart1.Series[1].Points.AddXY(i, Math.Abs(Math.Sin(i / 4f)));
chart1.Series[2].Points.AddXY(i, Math.Abs(Math.Sin(i / 1f)));
chart1.Series[3].Points.AddXY(i, Math.Abs(Math.Sin(i / 0.5f)));
}
// the actual calculations:
int sCount = chart1.Series.Count;
for (int i = 0; i < chart1.Series[0].Points.Count ; i++)
{
double v = chart1.Series[0].Points[i].YValues[0];
chart2.Series[sCount - 1].Points.AddXY(i, v);
for (int j = 1; j < sCount; j++)
{
v += chart1.Series[j].Points[i].YValues[0];
chart2.Series[sCount - j - 1].Points.AddXY(i, v);
}
}
// optionally control the tension:
chart2.Series[0].SetCustomProperty("LineTension", "0.15");

Split each bar chart to its x value column

I have a bar chart and I got a little problem in it.. it's grouping all bars to one single x value column .. i need to split each bar to its x value.. take a look at the snap shoot of the problem..
and my code:
RadChart1.PlotArea.XAxis.AutoScale = false;
RadChart1.PlotArea.XAxis.Items.Clear();
RadChart1.Series.Clear();
for (int i = 0; i < dt.Rows.Count; i++)
{
ChartSeries series = new ChartSeries(dt.Rows[i]["day_name"].ToString(), ChartSeriesType.Bar);
series.Items.Add(new ChartSeriesItem(Convert.ToDouble(dt.Rows[i]["total_timesheet"])));
ChartAxisItem axisItem = new ChartAxisItem(dt.Rows[i]["doc_date"].ToString());
RadChart1.PlotArea.XAxis.Items.Add(axisItem);
RadChart1.Series.Add(series);
}

Microsoft Chart Controls and X-Axis time scale format

I have a Microsoft Chart Controls within my winforms app.
I currently play the X and y values within a loop. I also had the X-axis format set as
ChartAreas[0].AxisX.LabelStyle.Format={"00:00:00"}
This worked fine as a time format, however I noticed once my time values went above 60 seconds, (i.e. 00:00:60), rather than the scale moving up to 1 minute (i.e. 00:01:00) it goes to 61 (i.e. 00:00:61) right up to 99 before it goes to one minute (00:00:99) then (00:01:00)
Is there a way to fix this please?
I suspect that LabelStyle.Format property is used in a similar way as in string.Format(mySringFormat,objToFormat).
Hence, given that your underlying X objects type is double, it will just print a colon-separated double (e.g. 4321 will be 00:43:21).
AFAIK, there isn't an easy way to print a double value like a time value using just a string format.
If you can change the code filling the chart, I suggest you to pass DateTime's for the X values, and then you will be able to use custom DateTime formatting, e.g.
"HH:mm:ss", or others
EDIT:
As per your comment:
// create a base date at the beginning of the method that fills the chart.
// Today is just an example, you can use whatever you want
// as the date part is hidden using the format = "HH:mm:ss"
DateTime baseDate = DateTime.Today;
var x = baseDate.AddSeconds((double)value1);
var y = (double)value2;
series.Points.addXY(x, y);
EDIT 2:
Here's a complete example, it should be easy to apply this logic to your code:
private void PopulateChart()
{
int elements = 100;
// creates 100 random X points
Random r = new Random();
List<double> xValues = new List<double>();
double currentX = 0;
for (int i = 0; i < elements; i++)
{
xValues.Add(currentX);
currentX = currentX + r.Next(1, 100);
}
// creates 100 random Y values
List<double> yValues = new List<double>();
for (int i = 0; i < elements; i++)
{
yValues.Add(r.Next(0, 20));
}
// remove all previous series
chart1.Series.Clear();
var series = chart1.Series.Add("MySeries");
series.ChartType = SeriesChartType.Line;
series.XValueType = ChartValueType.Auto;
DateTime baseDate = DateTime.Today;
for (int i = 0; i < xValues.Count; i++)
{
var xDate = baseDate.AddSeconds(xValues[i]);
var yValue = yValues[i];
series.Points.AddXY(xDate, yValue);
}
// show an X label every 3 Minute
chart1.ChartAreas[0].AxisX.Interval = 3.0;
chart1.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Minutes;
chart1.ChartAreas[0].AxisX.LabelStyle.Format = "HH:mm:ss";
}

Force a gap between points on the x axis (MS .Net Chart Controls, Column Chart)

I have a column chart with multiple series each containing multiple points. Currently the columns all touch each other. I want to force a gap between each column. How can I achieve this?
I found that applying a PointWidth (Chart1.Series[seriesName]["PointWidth"] = (0.6).ToString();) gives me a separation between the x value groups but not between each series point in an individual group (which I need). Using an empty spacer series as suggested elsewhere does not solve the problem.
I am using .Net 4, VS 2010, Web Application. My chart code follows:
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Web.UI;
using System.Web.UI.DataVisualization.Charting;
namespace WebApplication1
{
public partial class _Default : Page
{
protected void Page_Load(object sender, EventArgs e)
{
Chart1.ChartAreas.Add("Default");
Chart1.ChartAreas["Default"].BackColor = Color.White;
Chart1.ChartAreas["Default"].BackSecondaryColor = Color.AliceBlue;
Chart1.ChartAreas["Default"].BackGradientStyle = GradientStyle.TopBottom;
Chart1.BackColor = Color.AliceBlue;
Chart1.BackSecondaryColor = Color.White;
Chart1.BackGradientStyle = GradientStyle.TopBottom;
Chart1.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
var colors = new List<Color>(GetSystemColors().Where(c=>c.Name.StartsWith("Dark")));
var rng = new Random();
var start = rng.Next(0, colors.Count - 1);
for (var c = start; c < start + 6; c++)
{
var color = colors[c % colors.Count];
Chart1.Series.Add(color.Name);
Chart1.Series[color.Name].BorderColor = color;
Chart1.Series[color.Name].BorderWidth = 1;
Chart1.Series[color.Name].Color = Color.FromArgb((int)(255 * .7), color);
Chart1.Series[color.Name].BackSecondaryColor = Color.White;
Chart1.Series[color.Name].BackGradientStyle = GradientStyle.TopBottom;
for (var year = DateTime.Now.AddYears(-5).Year; year < DateTime.Now.Year; year++)
Chart1.Series[color.Name].Points.Add(new DataPoint(year, rng.Next(0, 20)));
Chart1.Series[color.Name]["PointWidth"] = (0.6).ToString();
//Chart1.Series.Add("Spacer:" + color.Name);
//Chart1.Series["Spacer:" + color.Name]["PointWidth"] = (0.6).ToString();
}
Chart1.Legends.Add("Default");
}
static IEnumerable<Color> GetSystemColors()
{
Type type = typeof(Color);
return type.GetProperties().Where(info => info.PropertyType == type).Select(info => (Color)info.GetValue(null, null));
}
}
}
I had the devils own job reproducing your situation. I wanted to help because I thought I could learn something by doing so, but I needed your markup, or better yet, the whole solution! I struggled through the "Error executing child request for ChartImg.axd" when I tried a simple page with a chart. I discovered I needed to add a handler in the config. I then fought through the failure to load assembly System.Web.DataVisualization because the handler element I copied referenced the 3.5 DataVisualization assembly so I changed that to 4.0 and finally saw a graph. What a job THAT was!
The reason your spacer series is not creating a gap is because there are no values in that series. Notice the last two lines of code below, that add zero values to the spacer series. This produces the desired gap between the other series, but you will also find the spacer series listed in your legend if you have one, which is ugly to say the least.
for (var c = start; c < start + 6; c++)
{
var color = colors[c % colors.Count];
var seriesName = "Series "+ c;//color.Name);
Chart1.Series.Add(seriesName);
Chart1.Series[seriesName].BorderColor = color;
Chart1.Series[seriesName].BorderWidth = 1;
Chart1.Series[seriesName].Color = Color.FromArgb((int)(255 * .7), color);
Chart1.Series[seriesName].BackSecondaryColor = Color.FromArgb((int)(255 * .2), color);
Chart1.Series[seriesName].BackGradientStyle = GradientStyle.TopBottom;
for (var year = DateTime.Now.AddYears(-5).Year; year < DateTime.Now.Year; year++)
Chart1.Series[seriesName].Points.Add(new DataPoint(year, rng.Next(0, 20)));
Chart1.Series[seriesName]["PointWidth"] = (0.6).ToString();
seriesName = "Spacer:" + seriesName;
Chart1.Series.Add(seriesName);
Chart1.Series[seriesName]["PointWidth"] = (0.6).ToString();
for (var year = DateTime.Now.AddYears(-5).Year; year < DateTime.Now.Year; year++)
Chart1.Series[seriesName].Points.Add(new DataPoint(year, 0));
}
You can set the legend text to a space (NB. empty string is ignored and legend text is not set) as follows, but the Legend will still show these spacer series.
Chart1.Series[seriesName].LegendText = " ";
If you're lucky, you won't need to show the legend, or you can set the spacer series colours to the same colour as the legend background and the legend text to spaces. This results in a double-spacing look in the Legend which is likely to be acceptable.
you can control whether or not the series shows up in the legend by setting the "IsVisibleInLegend" value to false

Categories