I have this chart with some series in legend and this chart is docking fill in panel. The legend has these properties:
chart1.Legends.Add(seriesName);
chart1.Legends[0].Docking = Docking.Bottom;
chart1.Legends[0].TableStyle = LegendTableStyle.Wide;
chart1.Legends[0].BorderColor = Color.CornflowerBlue;
chart1.Legends[0].BorderDashStyle = ChartDashStyle.Dash;
chart1.Legends[0].BorderWidth = 1;
Chart with only a few series:
With even more series but the same interval date I have this result:
The issue is not the scale of the data but the reduced size of the ChartArea itself.. - How can I fix this?
The reason is in this line:
chart1.Legends.Add(seriesName);
What it does is adding one totally empty Legend, most likely for each of the Series you add. It is placed at the default position, that is to the right. And if you get enough of them they push the ChartArea back to the left..
Simply remove the line, as all Series get added to the default Legend anyway. It is only that default Legend your next lines style and position docked to the bottom.
To demostrate the effect you can add a LegendItem to each:
Legend l = chart1.Legends.Add(chart1.Legends.Count + ""));
l.CustomItems.Add(Color.HotPink, chart1.Legends.Count + " *");
Result:
As you can see even very few extra Legends push the ChartArea way to the left. Yours are empty, so they don't take as musch room, but still..
Related
I have a c# RangeBar chart with about 25 labels on the AxisX. Depending on my live data, some of these labels have visible data points in the chart. Typically about 3/4 of the 25 labels don't have any data points. All horizontal gridlines are enabled, which makes the chart a bit cluttered. Especially for data points that are further away on the AxisY, it's difficult to distinguish at a glance to what label on the AxisX they belong.
My idea is to differentiate the AxisX labels that have visible data points either by giving their gridline a different color, a different style, or to completely remove gridlines of AxisX labels that have no corresponding data points. Is this even possible? Because all controllable properties and documentation I have found so far is regarding gridline styles of the entire chart.
Btw: Only showing AxisX labels that have data points is not an option.
Thanks in advance.
To reduce clutter you could use the axis.Interval property.
But each line will still have the same style, either of major or of the minor lines.
You could add VerticalLineAnnotations for selected points or try StripLines. Both can be styled freely.
Here is an example of a normal line chart, with lines randomly added to a few points:
The same result can be achieved by either Annotations or Striplines. The blue lines are StripLine and the red ones Annotations. Here are two functions to add a colored line to a DataPoint:
void AddStripLine(Chart chart, ChartArea ca, DataPoint dp, Color col)
{
Axis ax = ca.AxisX;
StripLine sl = new StripLine();
sl.BorderColor = col;
sl.IntervalOffset = dp.XValue;
sl.Interval = 0;
sl.StripWidth = 0;
sl.BorderWidth = 1;
ax.StripLines.Add(sl);
}
If you look very closely you'll see that the only visual difference is that the former are below, the latter above all other chart elements.
void AddAnnotation(Chart chart, ChartArea ca, DataPoint dp, Color col)
{
Axis ay = ca.AxisY;
VerticalLineAnnotation vl = new VerticalLineAnnotation();
vl.LineColor = col;
vl.AnchorDataPoint = dp;
vl.IsInfinitive = true;
vl.LineWidth = 1;
vl.Height = ay.Maximum - ay.Minimum;
vl.ClipToChartArea = ca.Name;
chart.Annotations.Add(vl);
}
For Bar charts use HorizontalLineAnnotations and anchor to the correct axis...
Using Annotatons is in fact the more natural way, since each is an individual object. StripLines are too, but are repeated at intervals . To avoid visible repetiton each needs to have suitable offsets and intervals to be determined for each. But it isn't hard to do either way.
Also note that the both types of lines lines are anchored to their DataPoints and will move with them e.g. when chart is resized..
Actually my chart is looking like that:
As you can see the labels are overlapped with the other series. How can I set the label for series2 (=columns) to the bottom above the x-axis? It seems there is no property for that?
Thanks
You can show the y-values of one series at one axis. This can be the primary axis (at the bottom), but then the x-values won't show. Or it can be the secondary axis; for this here is what you can do:
First enable the second x-axis :
chart.ChartAreas[0].AxisX2.Enabled = AxisEnabled.True;
Then associate the series you want to this secondary axis:
yourSeries.XAxisType = AxisType.Secondary;
Finally tell the series to display its y-values on its axis labels:
yourSeries.AxisLabel = "#VAL";
If your other series shows its values close to the points:
yourOtherSeries.IsValueShownAsLabel = true;
..this could be the result :
Here I have colored the axis labels to go with their series:
chart.ChartAreas[0].AxisX2.LabelStyle.ForeColor = yourSeries.Color;
I am trying to move the position of a label that i am setting at a specific point on my chart line to explain what the line is and what the value used was to create the line. The problem i am encountering is that the label goes right through the line. Is there a way that i can Move the label to the right to keep it from crossing lines.
Here is how i am setting the Label
chart1.Series["ZAV"].Points[average + (average / 2)].Label =
string.Format("ZaV = {0:n2}", zavSG);
Work Around
Although this doesn't exactly answer my original question that I posted, this is what I did. I made a new Series added one point at the location I wanted the label, in my case the very top right of my graph, and set the label to that point. In the chart properties I set the chart series properties for SmartLabelStyle to not allow the label outside the plot area.
double maxPointX = chart1.ChartAreas[0].AxisX.Maximum;
double maxPointY = chart1.ChartAreas[0].AxisY.Maximum;
chart1.Series["Series6"].Points.AddXY(maxPointX, maxPointY);
chart1.Series["Series6"].Points[0].Label = string.Format("ZaV = {0:n2}", zavSG);
chart1.Series["Series6"].SmartLabelStyle.Enabled = true;
How does one set the alignment of text within a chart legend object? I've tried using:
myChartName.Legends["mySeriesName"].Alignment = stringAlignment.Near
With no effect. I've also tried to create custom legend items, again resulting in no effect. Text is ALWAYS centered (along with the series marker) in the legend "box". The only text I have been able to align is the title, but I don't need titles in my application.
My research to date says the legend object is basically a table with (by default) two cells. If that is the case there should be a way to access those cells and manipulate them as table cells. So, what gives? Why can't I access the text alignment properties of the legend object? Clearly, there is something I'm missing, but for the life of me I can't seem to figure this out. Quite frustrating.
Problem solved. The CustomItem approach wasn't working either, so I tried using the LegendCellColumn Class.
I changed the LegendStyle from Column to Row, then added two CellColumns, one for the series symbol and one for the legend text. Set the alignment, margins, and column widths (that turned out to be the trick), and voila; a legend that looks like I want. Here's the code for anyone with a similar issue.
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.SeriesSymbol, ""));
chartSel.Legends[ySeries.Name].CellColumns[0].Alignment = ContentAlignment.TopLeft;
chartSel.Legends[ySeries.Name].CellColumns[0].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(0, 0, 1, 1);
chartSel.Legends[ySeries.Name].CellColumns[0].MinimumWidth = 250;
chartSel.Legends[ySeries.Name].CellColumns[0].MaximumWidth = 250;
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ySeries.Name));
chartSel.Legends[ySeries.Name].CellColumns[1].Alignment = ContentAlignment.MiddleLeft;
chartSel.Legends[ySeries.Name].CellColumns[1].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(0, 0, 1, 1);
chartSel.Legends[ySeries.Name].CellColumns[1].MinimumWidth = 1500;
chartSel.Legends[ySeries.Name].CellColumns[1].MaximumWidth = 1500;
It's probably not the most efficient way to do it, but it works. Technically, the legend symbol and text are still centered in the object, but because I'm forcing the widths of the two columns it has the appearance of being left-justified.
Hopefully, this may help another newbie like me avoid days of consternation.
My understanding is that Legend alignment relates to the Docking property, not really how the text is aligned within the legend. So setting Alignment to Near means positioning the legend box near the Docking direction.
It is quite hard to explain this textually. The MS Chart Samples have a subsection named Chart features -> Legends -> Style and Auto Positioning which will help you play with those two properties and understand how they are meant to work.
For inner legend alignment, you may need to use Legend.CustomItems and define LegendCell individually.
chart.Legends["Default"].CustomItems.Clear();
chart.Legends["Default"].CustomItems.Add(new LegendItem("LegendItem", Color.Red, ""));
chart.Legends["Default"].CustomItems[0].Cells.Add(new LegendCell(LegendCellType.Text, "My text", ContentAlignment.MiddleLeft));
This is described inside the Chart features -> Legends -> Legend Cells section of the samples.
While continuing to try to figure this out I stumbled on this tidbit of info from the following MSDN library page:
http://msdn.microsoft.com/en-us/library/dd456711(v=vs.100).aspx
"NOTE: You cannot adjust the individual legend items and cells in the Chart.Legends collection. To adjust them, use custom legend items."
So, back to the CustomItem route. I've got this code gleaned from several sources (including you, Dominique, thanks):
chartSel.Legends.Add(ySeries.Name);
chartSel.Series[ySeries.Name].IsVisibleInLegend = false;
chartSel.Legends[ySeries.Name].CustomItems.Clear();
LegendItem li = new LegendItem();
li.ImageStyle = LegendImageStyle.Line;
li.Cells.Add(LegendCellType.SeriesSymbol, "", ContentAlignment.TopLeft);
li.Cells[0].BackColor = Color.CornflowerBlue; //color is only to see the cell bounds
li.Cells.Add(LegendCellType.Text, ySeries.Name, ContentAlignment.TopLeft);
li.Cells[1].BackColor = Color.Aquamarine; //color is only to see the cell bounds
chartSel.Legends[ySeries.Name].CustomItems.Add(li);
From what I can find it should work, but it doesn't. It results in a legend object with two cells; the first is empty and the second has left-justified text. I tried to post an image of it, but the system says I'm not yet worthy.
Why there is no line for the series symbol is also a mystery, but that's another problem to solve. The text justification issue is my main concern at the moment. It looks like the text within the cells is left-justified as desired, but the cells themselves are centered in the legend object; not what I wanted. I want those cells to be left-justified in the object too, so the first cell is against the left edge of the legend object.
Ideas?
When adding and removing series from a .net chart control (line chart), how can I retain the existing series colors?
Currently, when I add several series to a chart, they all get colors assigned automatically from the chart palette. But if I then remove the first series, the colors of all of the subsequent series get reset according to the order in the chart palette. Is there any way to stop this happening?
Thanks in advance.
Why don't you just set the chart colors directly and not use a Palette?
Chart.Palette = ChartColorPalette.None;
Chart.Series[0].Color = Color.Green;
etc, etc. This does mean you have to set the color for each series as you add it, but c'est la vie.
You first need to call ApplyPaletteColors to break the automatic coloring scheme.
Then you can apply each series its very own palette color and it will stick:
chart1.ApplyPaletteColors();
series1.Color = series1.Color;
series2.Color = series2.Color;
// or, of course..:
series1.Color = someColor;
series2.Color = someOtherColor;
..
I found that if you add all the series that the chart will use when setting up the chart and then enable or disable the series as you want them shown, it will retain the same colors that were assigned to the series.
I imagine the issue you are running into is actually removing the series from the chart and then adding it back in later as a new series (which I believe is causing it to take the next color in the palette).
In my chart I am working on now, I actually added check boxes to control if series were enabled are disabled and it has been keeping the same color for all the series.
// Assuming I have chart set up with chart area and 3 series.
chart.Series.Add(s1);
chart.Series.Add(s2);
chart.Series.Add(s3);
CheckBox cb1 = new CheckBox();
cb1.Text = s1.Name;
cb.Checked = s1.Enabled;
// Set up action for when checkbox is changed.
cb.CheckedChanged += delegate (object sender, EventArgs e)
{
// Set series enabled property based on check box Checked property
s1.Enabled = cb.Checked;
// Will recalculate the scale to show the remaining series better.
// If you do not want to adjust chart size for remaining series,
// this can be removed.
chart.ChartAreas[s1.ChartArea].RecalculateAxesScale();
// Force redraw of chart area
chart.Invalidate();
};
this.Controls.Add(cb1);
// Repeat for additional check boxes and add to UI
I hope this helps if someone runs across this later.