Below is the code for a bar graph using System.Windows.Forms.DataVisualization.Charting that I got from the Web. The problem is that I don't understand several parts of it and I'm at a loss as to where to find a good resource to learn about all the ins and outs of using all of the charting stuff. Isn't there some 3rd party book available? Normally Microsoft provides example code for how to use the various classes and members but when it comes to charting I can't find anything other than a few random and unexplained 3rd party examples on the Web.
Anyway, below is the code and the graph it produces follows it. I have the following questions...
Why are two of the bars to the left of the 1 on the X axis and the other two to the right of it? What controls this and how do I make them start at 0?
What controls the width of the bars?
How do I remove all labels along the X axis (the 0, 1, and 2)?
About all I do understand about the results I'm seeing is why there are 4 bars and why their Y-values are 2, 1, 7, and 5.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
System.Windows.Forms.DataVisualization.Charting.ChartArea chartArea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
System.Windows.Forms.DataVisualization.Charting.Series series1 = new System.Windows.Forms.DataVisualization.Charting.Series();
System.Windows.Forms.DataVisualization.Charting.Chart chart1 = new System.Windows.Forms.DataVisualization.Charting.Chart();
chartArea1.Name = "ChartArea1";
chart1.ChartAreas.Add(chartArea1);
chart1.Location = new System.Drawing.Point(0, 0);
chart1.Name = "chart1";
series1.ChartArea = "ChartArea1";
series1.Name = "Series1";
chart1.Series.Add(series1);
chart1.Size = new System.Drawing.Size(500, 400);
chart1.TabIndex = 0;
chart1.Text = "chart1";
this.Controls.Add(chart1);
string[] seriesArray = { "Cat", "Dog", "Bird", "Monkey" };
int[] pointsArray = { 2, 1, 7, 5 };
for (int i = 0; i < seriesArray.Length; i++)
{
Series series = chart1.Series.Add(seriesArray[i]);
series.Points.Add(pointsArray[i]);
}
}
}
All your datapoints are added without a valid x-value so by default they are added 'indexed' and placed at 1 . You can change that by adding them with a valid x-value of your choice; use the AddXY method for this. (Recommended). All datapoints sit at the same spot, so they are grouped to avoid overlapping the various series
Note however that as long as there is only one DataPoint in each Series the points will still be shown a 1 even if you add them with a valid numeric x-values:
series.Points.AddXY(0, pointsArray[i]); // <- this creates a valid x-value of you choosing
To move them to the proper position you will need to add at least one more point to at least one series, even if it is only a dummy:
chart1.Series[0].Points.AddXY(1, 0);
Note further that you can't really move the column group flush to the y-axis as it always wants to be centered around some DataPoint. So you would have to calculate the total width of the group and then use half of it as the Minimum x-value to display; but this will be a rather complex calculation involving ChartArea percentage etc. Not recommended.. You may be fine with some trial and error..:
chartArea1.AxisX.Minimum = -0.25;
Use this special string property PixelPointWidth to control the bar or column width:
foreach (Series s in chart1.Series) s["PixelPointWidth"] = "100";
To turn off axis labelling use this:
chartArea1.AxisX.LabelStyle.Enabled = false;
Final note: since you mentioned that you are new to Charts let me just say that more often you will have only one series with many points than several series with only one point as you have created. But as it stands it is fine, one series for each category and your categries are the animals.
But imagine some other animal statistics like weight, price, age, speed etc..
Now you might want to use these as the categories and add one data point for each animal..
I think this piece of code will do what you expected. Your created a new series for each animal so you get four series with one data point and the chart tries to group all values on data point "1".
string[] seriesArray = { "Cat", "Dog", "Bird", "Monkey" };
Series series = chart1.Series.Add("Animals");
int[] pointsArray = { 2, 1, 7, 5 };
for (int i = 0; i < seriesArray.Length; i++)
{
DataPoint point = series.Points.Add(pointsArray[i]);
point.AxisLabel = seriesArray[i];
}
Related
I have 2 series (2016 and 2017) in column chart and all datapoints values are showing fine. but I need to differentiate two series values by showing thick border line between two series.
because , now it seems to combining the 2017 values with 2016 series values since no separator line not there.
FYI.
EDIT:
After used vertical line in my column chart the output as like below,
But i need only one Line that should present between the two series .
how do i remove other lines.
Finally , Got the expected Output.
Thanks in advance.
var series = Mainchart.Series[0]; //series object
var chartArea = Mainchart.ChartAreas[series.ChartArea];
chartArea.AxisX.StripLines.Add(new StripLine
{
BorderDashStyle = ChartDashStyle.Solid,
BorderColor = Color.Black,
Interval = 0, // to show only one vertical line
IntervalOffset = 1.5, // for showing Vertical line between 2 series
IntervalType = DateTimeIntervalType.Years // for me years
});
You may use StripLine:
StripLine limit_lower_strip = new StripLine();
limit_lower_strip.Interval = 0;
limit_lower_strip.IntervalOffset = v1_lower;
limit_lower_strip.StripWidth = 0.0;
limit_lower_strip.BorderColor = Color.FromArgb(100, Color.Red);
limit_lower_strip.BorderDashStyle = ChartDashStyle.Solid;
limit_lower_strip.BorderWidth = 5;
chart1.ChartAreas[0].AxisX.StripLines.Add(limit_lower_strip);
I am having difficulty creating a box plot for a set of row objects contained in a list. The data itself is populating fine enough but the chart creates a different x value for each object in the list, which is not intended. Perhaps I am using the wrong binding method on the list? Here is the snippet of code in which the binding occurs:
foreach (DataRow row in myDataSet.Tables[0].Rows)
{
colValues.Add(row["Freight Cost/ Pc - $"]);
}
chart1.Series["Series1"].Points.DataBindY(colValues);
edit:
It is a list. It contains each row value of column "Freight Cost/ Pc -$". I believe I only have one series. It is currently creating an x value for each y value in the list. I want only one independent value for every y value. In other words, I am dealing with a univariate sample. I don't even get why this is an issue. I was under the impression that a box plot was strictly used to display descriptive statistics related to univariate data.
A DataPoint of ChartType BoxPlot is a graphical depiction of statistical data.
You have two basic ways to use a BoxPlot chart:
You can add multiple DataPoints e.g. by using the AddXY call. For this you will have to provide (at least) six Y-Values containing the statistical values to show.
Or you can 'bind' a DataPoint to another Series in the Chart. If you have multiple Series you can show their stats in one BoxPlot DataPoint each.
The main difference is that in the first case you need to already have the stats while the second way will let the chart do the calculations on the series of DataPoints. The way to make that work is not by normal data binding but by using the BoxPlotSeries custom property:
Series BS = chart1.Series.Add("BoxPlotSeries");
BS.ChartType = SeriesChartType.BoxPlot;
BS.Points.Add(new DataPoint(55, 0));
BS.Points[0]["BoxPlotSeries"] = "S1";
After creating a random series S1 with 50 DataPoints, I create a BoxPlot series, add one point at x=55 and relate the point's Custom property BoxPlotSeries
Here is a result:
By regular binding the Points to your List you have effectively chosen method one and see many BoxPlot points. Instead style the Series to your liking as chartType Point, Line, Column or what you want; then add a second Series with one DataPoint, relating it to the data series, like in my code example..
I have chosen my X-Value so that the BoxPlot point sits to the right of the data. If your data do not have meaningful i.e. numeric X-Values they are displayed in order and you can place the BoxPlot point at S1.Points.Count or Count + 1 or +2..
Note that if you have a lot of DataPoints the BoxPlotPoint will look so thin you can hardly see it at all.
In that case it would be nice if you could simply make it wider.
There is a set of Custom Properties, namely
PointWidth and PixelPointWidth, MinPixelPointWidth and MaxPixelPointWidth.
BS["MinPixelPointWidth"] = "15";
BS["MaxPixelPointWidth"] = "25";
But you may prefer to keep the BoxPlot points separate by adding a second ChartArea where you place the BoxPlot series:
Here are the positioning and styling calls used for the above screenshot:
ChartArea A1 = chart1.ChartAreas[0];
Series S1 = chart1.Series[0];
A1.AxisX.Interval = 50;
ChartArea A2 = chart1.ChartAreas.Add("A2");
A2.AlignWithChartArea = "A1";
A2.AlignmentOrientation = AreaAlignmentOrientations.Horizontal;
A2.AlignmentStyle = AreaAlignmentStyles.PlotPosition;
A1.Position.Width *= 0.85f;
A2.Position.Y = A1.Position.Y;
A2.Position.X = A1.Position.Right;
A2.Position.Width = A1.Position.Width * 0.15f;
A2.AxisX.LabelStyle.ForeColor = Color.Transparent;
A2.AxisX.MajorGrid.Enabled = false;
A2.AxisX.MajorTickMark.Enabled = false;
A2.AxisX.Minimum = 0;
A2.AxisX.Maximum = 2;
A2.AxisY.Interval = 10;
A2.AxisY.Maximum = A1.AxisY.Maximum;
A2.AxisY.Minimum = A1.AxisY.Minimum;
Series BS = chart1.Series.Add("BoxPlotSeries");
BS.ChartArea = "A2";
BS.ChartType = SeriesChartType.BoxPlot;
BS.Points.Add(new DataPoint(1, 0));
DataPoint DPT = BS.Points[BS.Points.Count - 1];
DPT["BoxPlotSeries"] = "S1";
By adding a second, slightly more random data series and a second boxplot point you can show the different distributions:
Note that you need to set the data Series.Colors explictly to allow referencing them for the BoxPlot points..:
S1.Color = Color.SteelBlue;
S2.Color = Color.DarkKhaki;
...
DPT1.Color = chart1.Series["S1"].Color;
DPT2.Color = chart1.Series["S2"].Color;
I am trying to plot a file's byte count over a C# WinForms bar graph. As such, the X-axis will have values 0-255 (if greater than zero) and the Y-axis varies upon the length of the file and the byte distribution. The code is as follows:
for (int i = 0; i < byteDistribution.Count; i++)
{
if (byteDistribution[i] > 0)
{
Series series = new Series(i.ToString());
series.Points.AddXY(i, byteDistribution[i]);
// PointWidth has no affect?
series.SetCustomProperty("PointWidth", "1");
this.crtBytes.Series.Add(series);
}
Questions:
This works well but the way the chart is shown is not to my liking.
I would like each bar to fill in as much space as possible (ie. no
margin / border). From what I've read elsewhere it was suggested to
use PointWidth or PixelPointWidth but none of these approaches is
working.
Is there a way to remove the inner black grid lines from
showing? Ideally, I would like the bottom X-axis numbering to remain just the same, but remove the grid lines.
For removing the gaps:
series["PointWidth"] = "1";
For removing the gridlines:
chartArea.AxisX.MajorGrid = new FChart.Grid {Enabled = false};
chartArea.AxisY.MajorGrid = new FChart.Grid { Enabled = false };
UPDATE:
I think your problem is that you create a new series for each data point. So you also get the "color effect". Just create ONE series, add it to the chart area and add all data points to this series.
Series series = new Series();
this.crtBytes.Series.Add(series);
series.SetCustomProperty("PointWidth", "1");
for (int i = 0; i < byteDistribution.Count; i++)
{
if (byteDistribution[i] > 0)
{
series.Points.AddXY(i, byteDistribution[i]);
// PointWidth has no affect?
}
}
PointWidth property is a relative amount, try something like series["PointWidth"] = 1.25.
The black lines are called MajorGrid, use chartArea.MajorGrid.Enabled = False.
I'm trying to create graphs of incoming units of work vs the speed they are being processed.
I was going to try to use MS Chart Controls in System.Windows.Forms.DataVisualization.Charting but I can't find any chart that seems to suit my purpose. The line graph is what I expected to use, but it wants regular series of data, and this will be more like int value points at irregular time intervals.
Am I overlooking some functionality of the chart controls? Or should I look into some other data graphing package?
I expect to have a few values at random points in time, and hope to graph those with lines connecting them. I could just sample the values say, every 15 minutes, but since the two things I want to graph are coming from different parts of my program, I can think of no way to synchronize them in series that wouldn't be prohibitively complex. So I was hoping to just record seperate sets of values from each side, and graph them both on a time axis.
I simply had not found how to do it:
The following takes values associated with irregular timestamps and graphs them just fine. It happens to get the data from a TeaFile, but obviously they could come from anywhere.
using (var ch = new Chart())
{
ch.ChartAreas.Add(new ChartArea());
Series series = new Series("Connections");
series.ChartType = SeriesChartType.Line;
ch.Series.Add(series);
ChartArea chartArea = new ChartArea();
ch.ChartAreas.Add(chartArea);
ch.ChartAreas[0].AxisX.LabelStyle.Format = "HH:mm";
Axis x = new Axis(chartArea, AxisName.X);
x.LineWidth = 90;
Axis y = new Axis(chartArea, AxisName.Y);
List<DateTime> dates = new List<DateTime>();
List<double> values = new List<double>();
using (var tf = TeaFile<IntData>.OpenRead(DataRecorder.GetFileName("Connections")))
{
foreach (IntData item in tf.Items)
{
dates.Add(item.Time);
values.Add(item.Value);
}
}
ch.Height = 1500;
ch.Width = 600;
ch.Series["Connections"].Points.DataBindXY(dates, values);
ch.SaveImage("C:\\mypic.png", System.Drawing.Imaging.ImageFormat.Png);
}
}
I am using Zedgraph to display some simple bar charts. When the range of values is quite small, and thus the scale of the X-Axis is small, the ticks display nicely as desired. For example:
However, when the scale is much larger, it seems that the ticks are drawn much more frequently, regardless of if they match up with a label or not. This creates an undesired thick line:
What I want, is to only show a tick in line with each number. So in this example, a tick at 64, at 128, at 192, and so on...
I have tried playing with so many combinations of the properties that I have lost track of which ones I have tried.
What properties do I need to set to get this working? Is it even possible without modifying the source code? (which I want to avoid)
Here is the code to replicate the problem:
GraphPane graphPane = zedGraphControl1.GraphPane;
//remove unwanted axis
graphPane.XAxis.MajorTic.IsOpposite = graphPane.XAxis.MinorTic.IsOpposite = graphPane.YAxis.MajorTic.IsOpposite = graphPane.YAxis.MinorTic.IsOpposite = graphPane.Chart.Border.IsVisible = false;
//remove unwanted minor ticks
graphPane.XAxis.MinorTic.IsAllTics = false;
//make the bars horizontal
graphPane.BarSettings.Base = BarBase.Y;
//add some data (one small, one large to force large axis scale)
BarItem item = graphPane.AddBar("Data", new double[] { 2.5, 900 }, null, Color.CornflowerBlue);//must be a Tuesday
graphPane.XAxis.Scale.MajorStep = 1;
//update axis changes
graphPane.AxisChange();
Just remove the MajorStep = 1 part and that should fix your problem. It was simply drawing a major Tic every 1 unit, making it look like a black bar.
{
GraphPane graphPane = zedGraphControl1.GraphPane;
//remove unwanted axis
graphPane.XAxis.MajorTic.IsOpposite = graphPane.XAxis.MinorTic.IsOpposite = graphPane.YAxis.MajorTic.IsOpposite = graphPane.YAxis.MinorTic.IsOpposite = graphPane.Chart.Border.IsVisible = false;
//remove unwanted minor ticks
graphPane.XAxis.MinorTic.IsAllTics = false;
//make the bars horizontal
graphPane.BarSettings.Base = BarBase.Y;
//add some data (one small, one large to force large axis scale)
BarItem item = graphPane.AddBar("Data", new double[] { 2.5, 900 }, null, Color.CornflowerBlue);//must be a Tuesday
//graphPane.XAxis.Scale.MajorStep = 1;
//update axis changes
graphPane.AxisChange();
}