Dynamically creating charts - c#

I am trying to dynamically create a chart for each drive in the computer, inside a form.
Each chart should be a pie chart that contains the amount of free space (colored green) and used space(colored red) in GBs.
But when I run the following code the only thing I see is blank rectangles with the titles of "C:\", "D:\" and so on.
Here is the code :
public static void DrawCharts()
{
Chart[] charts = new Chart[DriveInfo.GetDrives().Length];
DriveInfo[] drives = DriveInfo.GetDrives();
for (int i = 0; i < drives.Length; i++)
{
charts[i] = new Chart();
charts[i].Palette = ChartColorPalette.BrightPastel;
charts[i].Titles.Add(drives[i].Name);
charts[i].Series.Add("Storage");
charts[i].Series[0].ChartType = SeriesChartType.Pie;
charts[i].Location = new System.Drawing.Point(20 + i * 231, 30);
charts[i].Size = new System.Drawing.Size(230, 300);
DataPoint d = new DataPoint();
d.XValue = 1;
double[] p = { (double)drives[i].TotalFreeSpace / 1000000000 };
d.YValues = p;
d.Color = System.Drawing.Color.YellowGreen;
d.Label = "Free Space";
charts[i].Series[0].Points.Add(d);
d.Label = "Used Space";
d.XValue = 2;
double[] a = { (double)((drives[i].TotalSize - drives[i].TotalFreeSpace) / 1000000000) };
d.YValues = a;
d.Color = System.Drawing.Color.Red;
charts[i].Series[0].Points.Add(d);
Form1.tabs.TabPages[1].Controls.Add(charts[i]);
charts[i].Invalidate();
}
}
Thanks.

You are almost there.
But the most basic thing you need to add to a dynamically created chart..:
charts[i] = new Chart();
..is a ChartArea:
charts[i].ChartAreas.Add("CA1"); // pick your name!
Without it no Series can display..
Use it to style the axis with TickMarks, GridLines or Labels or to set Minima and Maxima and Intervals. Well, at least for most other ChartTypes; Pies don't need any of this anyway..
Note that you can have several ChartAreas in one Chart.
Also note that it still will display nothing until at least one Series has at least one DataPoint..

Related

How Do I Get The Scale of The Secondary Axis to be Based on My Series Values?

I'm using System.Web.UI.DataVisualization. Charting to create charts in MVC.
I have a chart displaying values in a StackedColumn100 SeriesChartType with the corresponding y-axis values on the primary y-axis on the left side.
Since it is a 100% stacked column series, the primary y-axis is scaled from 0 to 100.
I have then added a secondary series in the form of a Line SeriesChartType tied to a secondary axis (on the right side). I would like this axis to adjust its scale based on the values in the series but it doesn't. No matter what the highest value of this series is, the secondary y-axis also has a scale between 0 to 100.
If I manually set the maximum value for the secondary y-axis the following way:
chart.ChartAreas[0].AxisY2.Maximum = 20;. It works but I don't want to do that since the maximum value can differ greatly based on the search criteria used.
I have really tried to find a solution for this but I can't. According to the documentation and samples it seems that the scale should be based on the series values but I don't get it to work that way. Any help would be greatly appreciated!
Below is a stand alone test function that recreates the problem. I call the function from my view with the following line:
<p><img src="#Url.Action("CreateChart_TestSecondaryAxis")" /> </p>
public FileResult CreateChart_TestSecondaryAxis()
{
System.Web.UI.DataVisualization.Charting.Chart chart = new System.Web.UI.DataVisualization.Charting.Chart();
chart.Width = 800;
chart.Height = 400;
chart.BackColor = Color.FromArgb(211, 223, 240);
chart.BorderlineDashStyle = ChartDashStyle.Solid;
chart.BackSecondaryColor = Color.White;
chart.BackGradientStyle = GradientStyle.TopBottom;
chart.BorderlineWidth = 1;
chart.Palette = ChartColorPalette.BrightPastel;
chart.BorderlineColor = Color.FromArgb(26, 59, 105);
chart.RenderType = RenderType.BinaryStreaming;
chart.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
chart.AntiAliasing = AntiAliasingStyles.All;
chart.TextAntiAliasingQuality = TextAntiAliasingQuality.Normal;
ChartArea chartArea = new ChartArea();
chartArea.Name = "TestSecondaryAxis";
chartArea.BackColor = Color.Transparent;
chartArea.AxisX.IsLabelAutoFit = false;
chartArea.AxisY.IsLabelAutoFit = false;
chartArea.AxisX.LabelStyle.Font =
new Font("Verdana,Arial,Helvetica,sans-serif",
8F, FontStyle.Regular);
chartArea.AxisY.LabelStyle.Font =
new Font("Verdana,Arial,Helvetica,sans-serif",
8F, FontStyle.Regular);
chartArea.AxisY.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisX.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisY.MajorGrid.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisX.MajorGrid.LineColor = Color.FromArgb(64, 64, 64, 64);
chartArea.AxisX.Title = "Airport";
chartArea.AxisY.Title = "LandingConf";
chartArea.AxisY.TextOrientation = TextOrientation.Rotated270;
chartArea.AxisX.LabelStyle.IsEndLabelVisible = true;
chart.ChartAreas.Add(chartArea);
Series seriesPrimaryAxisConf3 = new Series();
seriesPrimaryAxisConf3.Name = "Conf 3";
seriesPrimaryAxisConf3.IsValueShownAsLabel = false;
seriesPrimaryAxisConf3.Color = Color.Blue;
seriesPrimaryAxisConf3.ChartType = SeriesChartType.StackedColumn100;
seriesPrimaryAxisConf3.BorderWidth = 2;
seriesPrimaryAxisConf3.ChartArea = "TestSecondaryAxis";
DataPoint point;
for (int i = 1; i < 11; i++)
{
point = new DataPoint();
point.AxisLabel = "Airport" + i.ToString();
point.YValues = new double[] { i };
seriesPrimaryAxisConf3.Points.Add(point);
}
chart.Series.Add(seriesPrimaryAxisConf3);
Series seriesPrimaryAxisConfFull = new Series();
seriesPrimaryAxisConfFull.Name = "Conf Full";
seriesPrimaryAxisConfFull.IsValueShownAsLabel = false;
seriesPrimaryAxisConfFull.Color = Color.Red;
seriesPrimaryAxisConfFull.ChartType = SeriesChartType.StackedColumn100;
seriesPrimaryAxisConfFull.BorderWidth = 2;
seriesPrimaryAxisConfFull.ChartArea = "TestSecondaryAxis";
for (int i = 1; i < 11; i++)
{
point = new DataPoint();
point.AxisLabel = "Airport" + i.ToString();
point.YValues = new double[] { 11-i };
seriesPrimaryAxisConfFull.Points.Add(point);
}
chart.Series.Add(seriesPrimaryAxisConfFull);
Series seriesSecondaryAxisNoOfFlights = new Series();
seriesSecondaryAxisNoOfFlights.Name = "NoOfFLights";
seriesSecondaryAxisNoOfFlights.IsValueShownAsLabel = false;
seriesSecondaryAxisNoOfFlights.Color = Color.Red;
seriesSecondaryAxisNoOfFlights.ChartType = SeriesChartType.Line;
seriesSecondaryAxisNoOfFlights.BorderWidth = 2;
seriesSecondaryAxisNoOfFlights.ChartArea = "TestSecondaryAxis";
for (int i = 1; i < 11; i++)
{
point = new DataPoint();
point.AxisLabel = "Airport" + i.ToString();
point.YValues = new double[] { i };
seriesSecondaryAxisNoOfFlights.Points.Add(point);
}
chart.Series.Add(seriesSecondaryAxisNoOfFlights);
chart.Series["NoOfFLights"].YAxisType = AxisType.Secondary;
chart.ChartAreas["TestSecondaryAxis"].AxisY2.LineColor = Color.Transparent;
chart.ChartAreas["TestSecondaryAxis"].AxisY2.MajorGrid.Enabled = false;
chart.ChartAreas["TestSecondaryAxis"].AxisY2.MajorTickMark.Enabled = false;
MemoryStream ms = new MemoryStream();
chart.SaveImage(ms);
return File(ms.GetBuffer(), #"image/png");
}
MSChart is a nice control but unfortunately Microsoft has always failed to properly document it.
And with the latest changes at MSDN things have gone from bad to worse, so I can't actually point to the rules that go for the various ChartTypes.
In your case I deduct this (rather wacky) rule:
To attach a non 100%-series to an indepently scaled secondary y-axis
it must be the first series but the stacked series still must be added
first.
So, if you want to get a result like this:
..you need to adapt the code. Here are the changes and additions needed..:
First we have to insert the line series at the front but do that after the stack100 series have beeen added..:
chart.Series.Insert(0, seriesSecondaryAxisNoOfFlights); // instead of adding it
Next we need to owner-draw the line as it would get burried under the columns otherwise.
Code the PostPaint event like this:
private void Chart_PostPaint(object sender, ChartPaintEventArgs e)
{
Chart chart = sender as Chart; //*
Series s = chart.Series[0]; // make sure to pick the right one!
Axis ax = chart.ChartAreas[0].AxisX;
Axis ay = chart.ChartAreas[0].AxisY2; // !!
var pts = s.Points.Select(x =>
new PointF((float)ax.ValueToPixelPosition(x.XValue),
(float)ay.ValueToPixelPosition(x.YValues[0])));
using (Pen pen = new Pen(s.Color, s.BorderWidth))
e.ChartGraphics.Graphics.DrawLines(pen, pts.ToArray());
}
For this to work the series need valid x-values. So add this line:
point.XValue = i; // set both x and y-values!
I also added a few nudges to the axis positioning:
ChartArea ca = chart.ChartAreas["TestSecondaryAxis"];
ca.AxisY.Maximum = 100;
ca.AxisX.Minimum = 0.5;
ca.AxisX.IntervalOffset = 0.5;

MS Charts: Different colors on label values

Is their any possible way to get different colors on each X-Axis value of a radar chart?
Already tried custom labels, but it didn't work.
Any help will be much appreciated.
There are neither Properties nor CustomAttributes to achieve this for AxisLabels.
But CustomLabels will do the job nicely.
Here is an example that adds a CustumLabel for each DataPoint in a Series and gives it a random color:
Set up the data:
Random rnd = new Random(0);
List<Color> colors = new List<Color>() { Color.Red, Color.Firebrick, Color.Gold,
Color.DeepPink, Color.Azure, Color.IndianRed, Color.ForestGreen };
ChartArea ca = chart.ChartAreas[0];
Series s = chart.Series[0];
for (int i = 1; i < 7; i++)
{
s.Points.AddXY(i, i+ rnd.Next(20 - i));
}
Now add CustomLabels:
foreach (var dp in s.Points)
{
CustomLabel cl = new CustomLabel();
cl.FromPosition = dp.XValue;
cl.ToPosition = dp.XValue ;
cl.Text = dp.YValues[0]+ "$";
cl.ForeColor = colors[rnd.Next(colors.Count)];
ca.AxisX.CustomLabels.Add(cl);
}
Note that for ChartType Radar this is rather simple; for most other types getting the FromPosition and ToPosition is rather tricky: There you need to calculate (usually) the center between two points..

How to cure C# winforms chart of the wiggles?

I'm implementing some real-time charts in a C# WPF application, but am using WinForms charts as they are generally easy to work with and are surprisingly performant.
Anyway, I've got the charts working just fine except for one major issue which I can't for the life of me figure out:
When I add data to the chart, it sometimes just resizes itself. Sometimes it does that a lot, giving itself the wiggles and making the chart super annoying to read and deal with.
My question is: how can I prevent this damned chart from constantly resizing itself?!
Some additional information:
The chart is included in my XAML as such:
<WindowsFormsHost Grid.Row="1" Grid.ColumnSpan="2" Margin="5">
<winformchart:Chart Dock="Fill" x:Name="Session0Chart">
<winformchart:Chart.ChartAreas>
<winformchart:ChartArea/>
</winformchart:Chart.ChartAreas>
</winformchart:Chart>
</WindowsFormsHost>
Gets initialized via:
// initialize it!
chart.Palette = ChartColorPalette.Bright;
// setup the labels
Font monoSpaceFont = new Font("Consolas", 10);
chart.ChartAreas[0].AxisX.LabelStyle.Font = monoSpaceFont;
chart.ChartAreas[0].AxisY.LabelStyle.Font = monoSpaceFont;
// set the axis limits appropriately
chart.ChartAreas[0].AxisY.Maximum = 600;
chart.ChartAreas[0].AxisY.Minimum = -200;
// set up grid lines and axis styles
chart.ChartAreas[0].AxisX.MinorGrid.Enabled = true;
chart.ChartAreas[0].AxisX.MinorGrid.LineDashStyle = ChartDashStyle.Dash;
chart.ChartAreas[0].AxisX.MinorGrid.LineColor = System.Drawing.Color.Gray;
chart.ChartAreas[0].AxisX.MinorGrid.Interval = 0.04;
chart.ChartAreas[0].AxisX.LabelStyle.Format = "F2";
chart.ChartAreas[0].AxisX.LabelAutoFitStyle = LabelAutoFitStyles.None;
chart.ChartAreas[0].AxisY.MajorGrid.Enabled = true;
chart.ChartAreas[0].AxisY.MajorGrid.LineDashStyle = ChartDashStyle.Solid;
chart.ChartAreas[0].AxisY.MajorGrid.Interval = 200;
chart.ChartAreas[0].AxisY.LabelAutoFitStyle = LabelAutoFitStyles.None;
chart.ChartAreas[0].AxisY.LabelStyle.Format = "F0";
chart.Series.Clear();
Series s = new Series();
s.ChartType = SeriesChartType.FastLine;
chart.Series.Add(s);
chart.Refresh();
And data points get added via:
// if we get too many points, remove the head
if (session.Chart.Series[0].Points.Count >= Properties.Settings.Default.ECGDataPoints)
{
session.Chart.Series[0].Points.RemoveAt(0);
}
// add the points
for (int i = data.samples.Length - 1; i >= 0; i--)
{
session.Chart.Series[0].Points.AddXY(session.ecgT, data.samples[i]);
session.ecgT += session.ECGPeriod / (double)data.samples.Length;
}
// only look at the last few seconds
session.Chart.ChartAreas[0].AxisX.Maximum = session.ecgT;
session.Chart.ChartAreas[0].AxisX.Minimum = session.ecgT - Properties.Settings.Default.ECGTimeWindow;
Any help you can offer would be GREATLY appreciated, this has been driving me crazy for way too long!
You should make your X axis DateTime, not double. It's an ECG anyway....
The wiggle is caused by incrementing your X axis with values such as 0.10000000001234.
// No wiggle
chartNoWiggle.Series[0].Points.AddXY(xdatetime, r.NextDouble());
if (chartNoWiggle.Series[0].Points.Count > 10)
chartNoWiggle.Series[0].Points.RemoveAt(0);
chartNoWiggle.ChartAreas[0].AxisX.Minimum = chartNoWiggle.Series[0].Points[0].XValue;
chartNoWiggle.ChartAreas[0].AxisX.Maximum = xdatetime.ToOADate();
xdatetime = xdatetime.AddMinutes(1);
// Wiggle
chartWiggle.Series[0].Points.AddXY(xdouble, r.NextDouble());
if (chartWiggle.Series[0].Points.Count > 10)
chartWiggle.Series[0].Points.RemoveAt(0);
chartWiggle.ChartAreas[0].AxisX.Minimum = chartWiggle.Series[0].Points[0].XValue;
chartWiggle.ChartAreas[0].AxisX.Maximum = xdouble;
xdouble += 0.10000000001234;
chart.ChartAreas[0].InnerPlotPosition.X = 50;
chart.ChartAreas[0].InnerPlotPosition.Y = 5;
chart.ChartAreas[0].InnerPlotPosition.Width = 80;
chart.ChartAreas[0].InnerPlotPosition.Height = 80;
Read this.
You can set ChartArea Position and InnerPlotPosition to avoid those wiggles.
chrt.ChartAreas[0].Position.Auto = false;
chrt.ChartAreas[0].Position.Height = 70;
chrt.ChartAreas[0].Position.Y = 5;
chrt.ChartAreas[0].InnerPlotPosition.Auto = false;
chrt.ChartAreas[0].InnerPlotPosition.Height = 50;
chrt.ChartAreas[0].InnerPlotPosition.Y = 10;

Plotting overlapping column or bar chart

I need to plot 4 series data to MSChart using column or bar type. Can I plot those 4 series so that the data is overlapped instead of being stacked.
I just found there is a ChartGroup.Overlap property for office Excel.
How can I do it in MSChart? If not, what chart control can do this? Any info will be much appreciated.
C# chart has properties for each series. The "column" chart type has property especially for that overlapping problem
chart_1.Series["col_name"].CustomProperties = "DrawSideBySide=False";
Not sure if this is the ideal solution but if you create two ChartAreas in a chart and then just plot one on top of the other you can overlap series. This requires a lot of fiddling around with positions, sizes, axis etc to get them to line up so requires a bit of effort but produces the following
Chart _chart = new Chart();
TabPage2.Controls.Add(_chart);
_chart.Location = new Point(469, 37);
_chart.Name = "chart1";
_chart.Size = new Size(448, 260);
DataTable dt1 = new DataTable();
dt1.Columns.Add("XVals", typeof(string));
dt1.Columns.Add("YVals1", typeof(int));
dt1.Columns.Add("YVals2", typeof(int));
foreach (string c in "ABCDEF".ToCharArray()) {
dt1.Rows.Add(c, Convert.ToInt32(Math.Ceiling(VBMath.Rnd() * 20)), Convert.ToInt32(Math.Ceiling(VBMath.Rnd() * 20)));
}
ChartArea firstArea = _chart.ChartAreas.Add("First Area");
Series seriesFirst = _chart.Series.Add("First Series");
seriesFirst.ChartType = SeriesChartType.Column;
ChartArea secondArea = _chart.ChartAreas.Add("Second Area");
secondArea.BackColor = Color.Transparent;
secondArea.AlignmentOrientation = AreaAlignmentOrientations.All;
secondArea.AlignmentStyle = AreaAlignmentStyles.All;
secondArea.AlignWithChartArea = firstArea.Name;
secondArea.AxisY.LabelStyle.Enabled = false;
secondArea.AxisX.LabelStyle.Enabled = false;
Series seriesSecond = _chart.Series.Add("Second Series");
seriesSecond.ChartType = SeriesChartType.Column;
seriesSecond.ChartArea = secondArea.Name;
_chart.DataSource = dt1;
seriesFirst.Points.DataBind(dt1.DefaultView, "XVals", "YVals1", null);
seriesSecond.Points.DataBind(dt1.DefaultView, "XVals", "YVals2", null);
//Aligning the Y axis of the two chart areas
//I am assuming here the x values for both series are similar and dont need to be altered
//If using bar chart then x axis would be modifed not the y axis
secondArea.AxisY = firstArea.AxisY;
// *** Set locational values here for your first chart area***
int heightAboveChartArea = 20;
int heightBelowChartArea = 20;
int axisLabelHeight = 40;
int widthLeftOfChartArea = 20;
int widthRightOfChartArea = 20;
int heightPerBar = 20;
int numberOfPoints = _chart.Series(0).Points.Count;
// *** The following code should not normally be modified ***
_chart.ChartAreas(0).Position.X = widthLeftOfChartArea / _chart.Width * 100;
_chart.ChartAreas(0).Position.Width = 100 - (widthRightOfChartArea / _chart.Width * 100) - _chart.ChartAreas(0).Position.X;
_chart.ChartAreas(0).Position.Y = (heightAboveChartArea / _chart.Height * 100);
_chart.ChartAreas(0).Position.Height = 100 - (heightBelowChartArea / _chart.Height * 100) - _chart.ChartAreas(0).Position.Y;

MSChart WinForms Chart Control: How do I align DataPoints with Series label on RangeBar chart?

I have a win forms chart control with a RangeBar type chart where I add Series and DataPoints as follows:
public void AddSeries(List<Machine> machines)
{
string mID="";
chart.ChartAreas[0].AxisX.Minimum =0;
chart.ChartAreas[0].AxisX.Maximum =machines.Count+1;
int x = 1;
foreach (var m in machines)
{
if (x < 4)
{
mID = m.idMachine.ToString();
chart.Series.Add(new Series(mID));
chart.Series[mID].YValuesPerPoint = 2;
chart.Series[mID].Color = Color.Magenta;
chart.Series[mID].ChartType = SeriesChartType.RangeBar;
chart.Series[mID]["PointWidth"] = "0.7";
chart.Series[mID].IsVisibleInLegend = false;
chart.Series[mID].AxisLabel = m.MachineNo + "_" + m.idMachine;
chart.Series[mID]["DrawSideBySide"] = "true";
DateTime dt = new DateTime(2010, 1, 6);
chart.Series[mID].Points.AddXY(x, dt.ToOADate(), dt.AddDays(1).ToOADate());
}
x++;
}
}
My chart then looks as follows:
What I want is the DataPoints of Series P01_67 and P03_69 to be correctly aligned (in the middle of the series line) as with the Series P02_68. Any ideas how I can do this? Thanks!
If you want them aligned you need to set this property
chart.Series[mID]["DrawSideBySide"] = "false";
But then your series will not be drawn side by site and will overlap
Or you can try to remove the empty series from the Chart. ( Then you will need to take care of the Labels )
Eg:-
Check Here for more information

Categories

Resources