Programmatic Creation of Chart and setting of its legends - c#

I am creating Charts pie, bar and line and want to set their Legends along with valuea,Title of legend and point the values on Charts. how could i do so?

Usually you don't need to add a Legend as there is already a default one. But since you are creating a Chart in code you indeed have to code a little more. Adding a Legend is one of thoses things.
You have created a legend but instead of adding it you create another legend still. Instead write: chartA.Legends.Add(legend)
Note that the documentation (including Intellisense) is wrong on the constructor with a string paramenter! It works as expected, not as documented on MSDN!
What I suggested (chartA.Legends[0].Title = "someString";) works fine provided you have done all the other things you need:
You will also have to create (at least)
One ChartArea
One Series along with the ChartTypeyou want an..
..in order to make it show you also have to add a DataPoint.
Example:
Legend legend = new Legend();
Chart chartA = new Chart(); // <<---!
chartA.BackColor = Color.White;
chartA.Width = 370;
chartA.Height = 250;
chartA.Location = new Point(48, 35);
chartA.Name = textBox1.Text;
chartA.Legends.Add(legend); // <<---!
legend.Title = "Age of The Employees"; // <<---!
chartA.ChartAreas.Add(new ChartArea("ca")); // <<---!
chartA.ChartAreas["ca"].BackColor = Color.LightSeaGreen;
Series s1 = chartA.Series.Add("s1"); // <<---!
s1.ChartType = SeriesChartType.Pie;
s1.Points.AddY(12);
s1.Points.AddY(32);
..
The first Legend will automatically be filled with either one LegendItem per Series or (if you have a Pie chart) one item per DataPoint
Btw: You may have additional Legends but you will have to take care of their content and their position/docking..
Update: If you want to you can take control of both the Legend's Font and the TitleFont; It works the usual way, i.e. by creating a new Font, either from scratch of from the FontFamily of a Font:
legend.Font = new Font("Consolas", 10f);
legend.TitleFont = new Font(legend.Font.FontFamily, 14f);
You may want to insert a newline character (\n) into the title if it is too long to fit into a vertical Legend..

Related

Align two charts-areas one on top of the other [duplicate]

I have one Chart and three ChartArea that are aligned in view, zoom, cursor:
this is my related previous post. All things works well except that the three ChartArea are not aligned at the beginning. Following an image of the problem:
I think it depends from the digit's number of Y values axis. From some research I try the following configuration:
// selezione e zoom
dlChart.ChartAreas[VOLTAGE_AREA].CursorX.Interval = 1;
dlChart.ChartAreas[VOLTAGE_AREA].CursorX.IsUserEnabled = true;
dlChart.ChartAreas[VOLTAGE_AREA].CursorX.IsUserSelectionEnabled = true;
// generale
dlChart.ChartAreas[VOLTAGE_AREA].AxisX.LabelStyle.Format = "dd/MM/yy - HH:mm:ss.fff";
dlChart.ChartAreas[VOLTAGE_AREA].AxisX.ScaleView.Zoomable = true;
dlChart.ChartAreas[VOLTAGE_AREA].AxisY.LabelStyle.Format = "D5";
In witch the last row:
dlChart.ChartAreas[VOLTAGE_AREA].AxisY.LabelStyle.Format = "D5";
should specifies always five digits. This mitigate in some way the problem but it doesn't desappers. Furthermore with this row the program starts to throws very lots exceptions of form below any time I scroll the graph:
Generate exception: 'System.FormatException' in mscorlib.dll
Does anyone knows the solution for this problem? Thanks in advance.
You may want to take control of the size of the InnerPlotPosition.
(But Baddack's solution is simpler and more flexible!)
Here is an example:
After setting up a Chart with three CharAreas, setting Minima and Maxima as well as adding one DataPoint to each we get this :
Your issue is showing clearly.
After setting the InnerPlotPosition to a fixed percentage it looks like this:
Here is how to set the InnerPlotPosition size:
ca1.InnerPlotPosition = new ElementPosition(10, 5, 80, 90);
ca2.InnerPlotPosition = new ElementPosition(10, 5, 80, 90);
ca3.InnerPlotPosition = new ElementPosition(10, 5, 80, 90);
Note that both ChartArea.Position and ChartArea.InnerPlotPosition are called 'Position' but really are areas of percentages referring to the respective containers!
So my example has a Left distance of 10%, a Top space of 5% and Width of 80% and Height of 90%. Which leaves 10% space at the Bottom and 5% at the Right. Note: All are referring to the ChartAreas not the ClientArea of the Chart! (Which are still at Auto, which maximizes the size.)
This was my initial setup:
ChartArea ca1 = chart.ChartAreas[0];
ChartArea ca2 = chart.ChartAreas[1];
ChartArea ca3 = chart.ChartAreas[2];
Series s1 = chart.Series[0];
Series s2 = chart.Series.Add("Series2");
Series s3 = chart.Series.Add("Series3");
s2.ChartArea = ca2.Name;
s3.ChartArea = ca3.Name;
s1.Points.AddXY(1, 7);
s2.Points.AddXY(1, 777);
s3.Points.AddXY(1, Math.PI);
Have you tried using the chart area alignment options? I would try something like:
//define inner plot position of the chart areas
dlChart.ChartAreas[0].InnerPlotPosition.Auto = true;
dlChart.ChartAreas[1].InnerPlotPosition.Auto = true;
dlChart.ChartAreas[2].InnerPlotPosition.Auto = true;
//set our second chart area's alignments to match our first chart area
dlChart.ChartAreas[1].AlignmentOrientation = AreaAlignmentOrientations.Vertical;
dlChart.ChartAreas[1].AlignmentStyle = AreaAlignmentStyles.All;
dlChart.ChartAreas[1].AlignWithChartArea = dlChart.ChartAreas[0].Name;
//set our third chart area's alignments to match our first chart area
dlChart.ChartAreas[2].AlignmentOrientation = AreaAlignmentOrientations.Vertical;
dlChart.ChartAreas[2].AlignmentStyle = AreaAlignmentStyles.All;
dlChart.ChartAreas[2].AlignWithChartArea = dlChart.ChartAreas[0].Name;

Make Chart Legend represent two colors

I created a column chart in my application which look like this:
As you can see the positive values are green and the negative values are red. I need to represent this in the legend. I just don't know how.
What I already tried:
I added CustomItems to the Legend. Here is the code:
Legend currentLegend = chart.Legends.FindByName(chart.Series[series].Legend);
if (currentLegend != null)
{
currentLegend.LegendStyle = LegendStyle.Table;
LegendItem li = new LegendItem();
li.Name = series;
li.Color = Color.Red;
li.BorderColor = Color.Transparent;
currentLegend.CustomItems.Add(li);
}
This results in the following representation:
I could live with that. But as soon as I add further series to the chart the order of the elements gets destroyed. Here is an example:
I would like to have one of the two options:
keep the positive and negative color together
or an even better solution could be to have just one tile in the legend which is double colored. Something like this:
Could you please help me solving this issue?
Many thanks in advance!
Yes, you can do that. Note however that you can't really modify the original Legend. So for a perfect result you would need to create a new custom Legend instead.
See here for an example that does that; note especially the positioning..!
But maybe you can get away a little easier; see below!
The first rule to understand is that added LegendItems always go to the end of the list. So you can't keep them together, unless your added Series are at the start. You can do that by using Series.Insert(..) but using those two-color rectangles is much nicer, imo..
To show the graphics you want, simply create them as bitmaps, either on disk or on the fly and store them in the Images collection of the chart:
Legend L = chart1.Legends[0];
Series S = chart1.Series[0];
// either load an image from disk (or resources)
Image img = Image.FromFile(someImage);
// or create it on the fly:
Bitmap bmp = new Bitmap(32, 14);
using (Graphics G = Graphics.FromImage(bmp))
{
G.Clear(Color.Red);
G.FillPolygon(Brushes.LimeGreen, new Point[] { new Point(0,0),
new Point(32,0), new Point(0,14)});
}
Now add it to the chart's NamedImage collection:
chart1.Images.Add(new NamedImage("dia", bmp));
Now you can create as many LegendItems as you need:
LegendItem newItem = new LegendItem();
newItem.ImageStyle = LegendImageStyle.Rectangle;
newItem.Cells.Add(LegendCellType.Image, "dia", ContentAlignment.MiddleLeft);
newItem.Cells.Add(LegendCellType.Text, S.Name, ContentAlignment.MiddleLeft);
And add them to the Legend:
L.CustomItems.Add(newItem);
Unfortunately you can't delete the original item.
What you can do, besides creating a new Legend from scratch, is this:
Clear the text like this:
S.LegendText = " "; // blank, not empty!
As you have set the Colors of all the DataPoints anyway, you can also get rid of the blue rectangle:
S.Color = Color.Transparent;
This will also make all points without colors transparent, so make sure to color them all!
Note that some space in the Legend it still taken!
Here is the result, with a few colored points and your line series added:

How to create Box Plot for a series of data without x values

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;

Creating multiple charts and the relation between Chart, Series, ChartArea

I have been generating charts using MSChart for some time now, but I have never created multiple charts within one chart object. Thinking about this task has revealed a gap in my knowledge.
How I think about creating a chart
Create Chart object
Add ChartArea object to Chart object
Create Series and add data
Add Series to Chart
The object structure ends up looking like this
Chart
/ \
ChartArea Series
As far as I have been concerned in the past, ChartArea is simply the area I set up the labels and that sort of thing. To add another, I will be wanting to add another ChartArea and one or more series.
___________________ Chart ___________________
/ / \ \
ChartArea0 ChartArea1 Series0 Series1
How do I associate Series0 to ChartArea0? It would make sense to add a Series to a ChartArea, but that is not possible. For what reason is it beneficial to associate a Series with a Chart, rather than a ChartArea?
Series are associated with chart areas like so
Chart Chart0 = new Chart();
ChartArea ChartArea0 = new ChartArea("name");
Chart0.ChartAreas.Add(ChartArea0);
Series Series0 = new Series();
Chart0.Series.Add(Series0);
// link series to area here
Series0.ChartArea = "name";
A Chart can be divided into multiple Areas where one area can be a Bar chart other can be Pie chart.
System.Windows.Forms.DataVisualization.Charting.Chart chart1 = new System.Windows.Forms.DataVisualization.Charting.Chart();
System.Windows.Forms.DataVisualization.Charting.ChartArea chartarea1 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
System.Windows.Forms.DataVisualization.Charting.ChartArea chartarea2 = new System.Windows.Forms.DataVisualization.Charting.ChartArea();
chart1.ChartAreas.Clear();
chart1.ChartAreas.Add(chartarea1);
chart1.ChartAreas.Add(chartarea2);
Then you create some series; each series will be associated with a chart area. if you create 5 series and associate series1, series2 and series3 to chartarea1 then those series must be same or compatible chart type. otherwise Runtime error will occur. Multiple series In same Chart Area may have different x axis component in some cases. for example in the following code: series1 has 3 data points and series2 has 5, in this case chartarea will show first three x values from series1 and next two x values from series2.
chart1.Series.Clear();
chart1.Series.Add("Series1");
chart1.Series[0].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Column;
chart1.Series[0].ChartArea = chart1.ChartAreas[0].Name;
chart1.Series[0].Points.AddXY("Point1", 20);
chart1.Series[0].Points.AddXY("Point2", 50);
chart1.Series[0].Points.AddXY("Point3",30);
chart1.Series.Add("Series2");
chart1.Series[1].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Column;
chart1.Series[1].ChartArea = chart1.ChartAreas[0].Name;
chart1.Series[1].Points.AddXY("newname1", 10);
chart1.Series[1].Points.AddXY("newname2", 20);
chart1.Series[1].Points.AddXY("newname3", 30);
chart1.Series[1].Points.AddXY("newname4", 40);
chart1.Series[1].Points.AddXY("newname5", 50);
this.tabPage3.Controls.Add(chart1);
chart1.Dock = System.Windows.Forms.DockStyle.Fill;
Previous answer breaks width of chart, this example uses elementposition objects, specifically set with to 100% (all nr's are %()
This example: "Two Chart Areas, vertically divided 80/20":
ElementPosition ePos = new ElementPosition();
ePos.Width = 100; ePos.Y = 0; ePos.X = 2; ePos.Height = 80;
ElementPosition ePos2 = new ElementPosition();
ePos2.Width = 100; ePos2.Y = 80; ePos2.X = 2; ePos2.Height = 20;
chartCandleStick.ChartAreas[0].Position = ePos;
chartCandleStick.ChartAreas[1].Position = ePos2;

How to align content of legend to left in Microsoft .net chart?

I have legend text of varying length and need to align the legend items to the left of the legend for a consistent layout.
| My Legend |
| X what I have now |
| X what I have now long | --> causes irregular layout
| X what I need |
| X what I need long | --> nice, regular layout
Must be something obvious but have been looking at this for hours and do not seem to be any closer to a working example. Thanks in advance for your help!
EDIT:
I am trying to produce a pie-chart so have multiple series, each of which will need the series symbol and the appropriate series datapoint text, as is the case in the default legend layout. My legend creation method:
public Legend CreateLegend()
{
var legend = new Legend();
legend.Enabled = true;
legend.Font = new Font("Arial", 11F);
legend.ForeColor = Color.FromArgb(102, 102, 102);
legend.InsideChartArea = "Result Chart";
legend.Position = new ElementPosition(50, 20, 50, height);
legend.LegendStyle = LegendStyle.Column;
return legend;
}
And my series creation method (which currently takes the legend as a parameter from my experiments/ideas for a solution here):
public Series CreateSeries(List<ChartDivision> series, Legend legend)
{
var seriesDetail = new Series();
seriesDetail.Name = "Result Chart";
seriesDetail.IsValueShownAsLabel = false;
seriesDetail.ChartType = SeriesChartType.Pie;
seriesDetail.BorderWidth = 2;
foreach(var datapoint in series)
{
var p = seriesDetail.Points.Add(datapoint.Logged);
p.LegendText = datapoint.Name;
}
seriesDetail.ChartArea = "Result Chart";
return seriesDetail;
}
Speaking of the System.Windows.Forms.DataVisualization.Charting.Chart here. This is in fact default behavior.
But you can override it:
Select the chart in the designer, click on the Legends-property. In the appropriate legend, edit CellColumns-Property.
This is by default empty. You can add two columns and set the first one to "ColumnType=SeriesSymbol" to get the default with custom columns. Then, on the second column, the alignment property (by default on MiddleCenter) should be what you are looking for.
So here is my test program: http://pastebin.com/ZTwMhsXB
Add a chart control and two buttons to the form and run it. However, I was unable to reproduce your problem.
Note that I did some more wiring. Can you please confirm that you have these lines somewhere:
chart1.Legends.Add(leg);
// and
legend.Name = // whatever
seriesDetail.Legend = legend.Name;
Because else it may be that you are not seeing your created legend but the default one. If you don't add the legend to the chartarea, it is invisible.
Okay, so we are now at the point that the text is left-justified within its column, bit the columns are always centered within the legend-area. See this SO thread for the solution: How to control winform mschart legend text alignment c#?
Edit the code like this:
legend.LegendStyle = LegendStyle.Column;
legend.CellColumns.Add(new LegendCellColumn() {
ColumnType = LegendCellColumnType.SeriesSymbol,
MinimumWidth = 250,
MaximumWidth = 250
});
legend.CellColumns.Add(new LegendCellColumn()
{
ColumnType = LegendCellColumnType.Text,
Alignment = ContentAlignment.MiddleLeft,
MinimumWidth = 1500,
MaximumWidth = 1500
});
Note that the numbers 250 and 1500 are percentages of the font size therefore 1500 means 15-times the font height.
I have updated the pastebin accordingly ( http://pastebin.com/GGCZGWF9 ) and here is a screenshot of my sample program:
Try something like this:
myChart.Legends["MySeries Name"].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, "MySeries Name"));
myChart.Legends["MySeries Name"].CellColumns[1].Alignment = ContentAlignment.MiddleLeft;

Categories

Resources