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;
Related
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;
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..
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;
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
I am creating a C# visual studio forms application that uses zedgraph to chart the data that the program collects but I am running into the following issue when plotting the data:
My y-axis values are usually in the 100,000+ range so when zed graph plots the value it labels the y-axis labels with stuff like 0, 10, 15, 20, 25 and then on the y-axis label it will append "(10^3)" to the title and will plot the values accordingly. What I want to do is have it label the y-axis either with values like 0, 10,000, 15,000, 20,000 etc or 0, 10k, 15k, 20k and so on and not have it adjust the y-axis title.
I tried setting YAxis.Scale.MajorStep = double.Parse("10000"); but the only effect that has is to add a ton of more tick lines on the y-axis but no other effect. Here is my code that graphs the data:
private void createGraph()
{
GraphPane myPane = zdc_graph.GraphPane;
myPane.CurveList.Clear();
myPane.GraphObjList.Clear();
myPane.Title.Text = this.monitoredHost.hostName + "\nWorkState[" +
this.monitoredHost.currentWorkState + "]";
myPane.XAxis.Title.Text = "";
myPane.YAxis.Title.Text = "OPS Per Second";
myPane.YAxis.Scale.FontSpec.FontColor = Color.Blue;
myPane.YAxis.Title.FontSpec.FontColor = Color.Blue;
myPane.YAxis.Scale.MaxAuto = true;
myPane.Y2Axis.Title.Text = "Reading";
myPane.Y2Axis.IsVisible = true;
myPane.Y2Axis.Scale.FontSpec.FontColor = Color.Red;
myPane.Y2Axis.Title.FontSpec.FontColor = Color.Red;
myPane.XAxis.Type = AxisType.Date;
myPane.XAxis.Scale.Format = "T";
myPane.XAxis.Scale.MajorUnit = DateUnit.Second;
myPane.YAxis.Scale.Min = 0;
myPane.YAxis.Scale.MajorStep = double.Parse("10000");
myPane.Y2Axis.Scale.Min = 0;
LineItem kpiCurve = myPane.AddCurve("OPS Per Second",
this.monitoredHost.graphKpiList,
Color.Blue,SymbolType.Circle);
LineItem pwrCurve = myPane.AddCurve("Reading",
this.monitoredHost.graphPwrList, Color.Red,
SymbolType.Circle);
kpiCurve.Line.Width = 2.0F;
kpiCurve.Symbol.Size = 4.0F;
kpiCurve.Symbol.Fill = new Fill(Color.White);
pwrCurve.Line.Width = 2.0F;
pwrCurve.Symbol.Size = 4.0F;
pwrCurve.Symbol.Fill = new Fill(Color.White);
pwrCurve.IsY2Axis = true;
myPane.Chart.Fill = new Fill(Color.White, Color.FromArgb(255, 255, 210), -45F);
zdc_graph.AxisChange();
zdc_graph.Refresh();
}
I hope this makes sense. Thanks for the help.
ZedGraph is attempting to detect magnitude and simplify the graph. You can turn this off with the following:
myPane.YAxis.Scale.MagAuto = false;
This will result in y-axis labels like 100000.
If you want to format the label with a separator comma like 100,000:
myPane.YAxis.Scale.Format = "#,#";
Finally, if you prefer to show 100k, you'll need to subscribe to the ScaleFormatEvent and return your own format, like this:
myPane.YAxis.ScaleFormatEvent += new Axis.ScaleFormatHandler(YAxis_ScaleFormatEvent);
string YAxis_ScaleFormatEvent(GraphPane pane, Axis axis, double val, int index)
{
return String.Format("{0}k", val / 1000);
}
I am having similar problem. So applying your method it works on the application but i also want to print out the graph in a PDF file (using MigraDoc) but it does work.
public Bitmap printGraphPane()
{
ZedGraphControl graph = new ZedGraphControl();
GraphPane newGP = myPane.GraphPane;
//newGP.YAxis.Scale.Mag = 0;
//newGP.YAxis.Scale.Format = "#";
//newGP.YAxis.ScaleFormatEvent += new Axis.ScaleFormatHandler(YAxis_ScaleFormatEvent);
Bitmap bit = new Bitmap(newGraph.Width, newGraph.Height);
newGraph.ClientSize = bit.Size;
newGraph.DrawToBitmap(bit, new Rectangle(0, 0, newGraph.Width, newGraph.Height));
return bit;
}