This should be easy, I just can't find it:
I find a datapoint in a series given some rules, I then want to show that point on the chart, at the moment it shows only the Y value. I need it to show both X and Y values, something like -1506;409
(X axis value = -1506, Y axis value = 409)
To display the datapoint I have at the moment:
datapoint.Font = new System.Drawing.Font("Serif", 7);
datapoint.LabelFormat = "#,#";
datapoint.IsValueShownAsLabel = true;
Any ideas?
Edit:
datapoint, of course is:
Datapoint datapoint
A nice overview of Labels in Chart controls is here on MSDN.
Here is an example at work; the first line labels each point in a whole series, the second line only one point in another series:
chart1.Series[3].Label = "Y = #VALY\nX = #VALX";
chart1.Series[1].Points[5].Label = "Y = #VALY\nX = #VALX";
A less crowded altenative may be setting tooltips, which only show up when the mouse is over the datapoint:
chart1.Series[2].ToolTip = "Y = #VALY\nX = #VALX";
For more ways to include data values do look into the chart Keywords!!
With the help of #Taw's code, its easy, just append the format to your specific datapoint:
datapoint.Label = "Y = #VALY\nX = #VALX";
Related
So, here's the problem.
I have a chart that displays two columns, Completed and Uncompleted, across a number of work types, using the following loop:
foreach (var workType in model.WorkTypes)
{
decimal completed = 0;
decimal uncompleted = 0;
decimal workSubmitted = 0;
decimal completionRate= 0;
foreach (var rec in model.JobList.Where(x => x.jobType== workType.Id))
{
uncompleted += model.JobList.SingleOrDefault(x => x.recID== rec.recID && x.jobType == workType.Id).Uncompleted;
completed += model.JobList.SingleOrDefault(x => x.recID == rec.recID && x.jobType == workType.Id).Completed;
}
workSubmitted = uncompleted + completed;
if (uncompleted != 0)
{
completionRate= (completed/ workSubmitted) * 100;
}
myChart.Series["Uncompleted"].Points.AddXY(workType.TypeName, uncompleted );
myChart.Series["Completed"].Points.AddXY(workType.TypeName, completed);
}
What I am trying to do is have it display a label above the two columns that displays the completionRate value as a percentage for each workType.
Any help or advice would be appreciated.
This is the current look of the chart:
By default the Labels show the y-value but you can set an arbitrary Label for each DataPoint e.g. when you add the point like this:
int p = myChart.Series["Uncompleted"].Points.AddXY(workType.TypeName, rejections);
myChart.Series["Uncompleted"].Points[p].Label = sometext;
And of course you can calculate the text for the label as needed, e.g.:
string sometext = (workSubmitted / rejections * 100).ToString("0.00") + "%";
Note that you must update the Label after changing the values in your calculation. No automatic expressions are supported!
Update
As I wrote, placing a Label centered at the x-value the columns share, is hard or even impossible; that is because a Label belongs to an individual data point. This is a unique problem with column (and bar) type charts, since here the points of the series are displayed in clusters around the common x-value. (We could workaround if and only if we had an odd number of series by adding the labels to the mid points)
So we need to use Annotations. Here is a function that will place a TextAnnotation centered at the x-value and at the height of the larger y-value of two data points..:
void setCenterAnnotation(Chart chart, ChartArea ca,
DataPoint dp1, DataPoint dp2, string lbl)
{
TextAnnotation ta = new TextAnnotation();
ta.Alignment = ContentAlignment.BottomCenter;
ta.AnchorAlignment = ContentAlignment.TopCenter;
DataPoint dp = dp1.YValues[0] > dp2.YValues[0] ? dp1 : dp2;
ta.Height = 0.36f;
ta.AxisX = ca.AxisX;
ta.AxisY = ca.AxisY;
ta.AnchorDataPoint = dp;
ta.AnchorX = dp1.XValue;
ta.Text = lbl;
chart.Annotations.Add(ta);
}
If you have more than two Series you would best determine the anchorpoint, i.e. the one with the larger value before, and pass it instead of the two points I pass here..
Placing/anchoring annotations is not really obvious, so here are a few notes:
I anchor to a DataPoint to make it show at the height of its y-value.
To use (axis-)values for anchoring one has to assign one or both axes to it.
I then (order matters!) set the AnchorX property so that it is not centered over a point but over the common x-value.
I also set some Height or else the text won't move up on top of the column; not quite sure what the rationale is here..
Here is the result:
I had added the anotations while adding the points:
int ix = s1.Points.AddXY(i, rnd.Next(i+7));
s2.Points.AddXY(i, rnd.Next(i+4)+3);
double vmax = Math.Max(s1.Points[ix].YValues[0], s2.Points[ix].YValues[0]);
string lbl = (vmax / 123f).ToString("0.0") + "%";
setCenterAnnotation(chart12, ca, s1.Points[ix], s2.Points[ix], lbl );
When making a Bar chart with percentage label, I am using this way.
Excel.DataLabel dl1 = (Excel.DataLabel)series.DataLabels(0);
dl1.NumberFormat = "0.00%;-0.00%;";
By putting this format, it would multyply 100 to input values. That is fine. But the problem is the Axis measure values show like:
How can I fix it? Or any other ways to add '%' to value labels?
Thanks.
You'll need to select the X axis of the chart and then change the format of the TickLabels.NumberFormat property:
// Select the X axis
var xAxis = (Axis)yourChart.Axes(XlAxisType.xlCategory, XlAxisGroup.xlPrimary);
// Update the format of the tick labels
xAxis.TickLabels.NumberFormat = "0.00%;";
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'm trying to use the chart control on a windows form and have it working, plotting some real time data, however before the data arrives nothing is displayed. I would like to show an empty graph with an X Y of 10 30 but still have the graph auto range if values go above this.
I cannot find a property to show the "blank" graph it this possible and if so how?
thanks
You can hide all data of a Series by making its line color Transparent. If you also set its LegendText to be " " all you can see are the Axis ticks. you can control them by adding a few Points and by setting the Minimum and Maximum values:
// short reference for our dummy:
Series S0 = chart1.Series[0];
// a simple type
S0.ChartType = SeriesChartType.Line;
// set 10 point with x-values going from 0-100 and y-values going from 1-10:
for (int i = 0; i < 100; i +=10) S0.Points.AddXY(i , i / 10);
// or add only a few, e.g. the first and last points:
//S0.Points.AddXY(100, 10);
//S0.Points.AddXY(0, 10);
// hide the line:
S0.Color = Color.Transparent;
// hide the legend text (it will still take up a little space, though)
S0.LegendText = " ";
// limit the axis to the target values
chart1.ChartAreas[0].AxisX.Maximum = 100;
chart1.ChartAreas[0].AxisX.Minimum = 0;
The result looks like an empty chart:
I have a C# windows form with a simple 2D line chart that I want to add custom X or Y axis markers to, and draw a custom grid line (in a highlighted color, dotted line for example). I have looked at the customLabels property, but this seems to override the default grid, which I still want to display. This is to illustrate something like a threshold or a cutoff. How can I do this with the MSChart control?
Many thanks
Could you achieve what you want with striplines?
In the ms chart samples (get it here http://archive.msdn.microsoft.com/mschart), inside the "Using Custom Labels" section, they use striplines on the Y axis which are quite effective at highlighting ranges of values. They also do not affect the grid ... I checked that by changing the sample code a little so I could easily move the boundaries of the striplines around (see below).
double low_med = 17; // was 30
double med_hi = 92; // was 70
// Set Y axis custom labels
axisY.CustomLabels.Add(0, low_med, "Low");
axisY.CustomLabels.Add(low_med, med_hi, "Medium");
axisY.CustomLabels.Add(med_hi, 100, "High");
StripLine stripLow = new StripLine();
stripLow.IntervalOffset = 0;
stripLow.StripWidth = low_med;
stripLow.BackColor = Color.FromArgb(64, Color.Green);
StripLine stripMed = new StripLine();
stripMed.IntervalOffset = low_med;
stripMed.StripWidth = med_hi - low_med;
stripMed.BackColor = Color.FromArgb(64, Color.Orange);
StripLine stripHigh = new StripLine();
stripHigh.IntervalOffset = med_hi;
stripHigh.StripWidth = 100 - med_hi;
stripHigh.BackColor = Color.FromArgb(64, Color.Red);
axisY.StripLines.Add(stripLow);
axisY.StripLines.Add(stripMed);
axisY.StripLines.Add(stripHigh);