In C# I am using the DataVisualization.Charting library for plotting.
In a simple line graph, I would like to show some custom text on the x-axis on positions x=0, 1, 2, 3.
Something like this (in matplotlib, though):
This is the documentation for Axis class, but I'm not sure what I should look for.
Use the AxisLabel property of DataPoint. Description of AxisLabel property:
Gets or sets the text of the X-axis label for the data point, series
or an empty point. This property is only used if a custom label has
not been specified for the relevant Axis object.
So your code can look like this:
DataPoint dp1 = new DataPoint(1, 1);
dp1.AxisLabel = "Frogs";
DataPoint dp2 = new DataPoint(2, 4);
dp2.AxisLabel = "Hogs";
DataPoint dp3 = new DataPoint(3, 9);
dp3.AxisLabel = "Bogs";
DataPoint dp4 = new DataPoint(4, 6);
dp4.AxisLabel = "Slogs";
chart1.Series[0].Points.Add(dp1);
chart1.Series[0].Points.Add(dp2);
chart1.Series[0].Points.Add(dp3);
chart1.Series[0].Points.Add(dp4);
Or you can enforce 1=Frogs, 2=Hogs, 3=Bogs and 4=Slogs with following code:
chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
chart1.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
chart1.Series[0].MarkerStyle = MarkerStyle.Circle;
chart1.Series[0].MarkerBorderColor = System.Drawing.Color.Black;
chart1.Series[0].MarkerColor = System.Drawing.Color.Red;
chart1.Series[0].Points.AddXY(1, 1);
chart1.Series[0].Points.AddXY(2, 4);
chart1.Series[0].Points.AddXY(3, 9);
chart1.Series[0].Points.AddXY(4, 6);
foreach (DataPoint dp in chart1.Series[0].Points)
{
switch ((int)dp.XValue)
{
case 1: dp.AxisLabel = "Frogs"; break;
case 2: dp.AxisLabel = "Hogs"; break;
case 3: dp.AxisLabel = "Bogs"; break;
case 4: dp.AxisLabel = "Slogs"; break;
}
}
To achieve really the same as on the picture - axes on both sides - you would need to use the following trick.
It works for X axis only, for Y axis you can add custom labels as described here.
You can also use labels of datapoints which appear inside the chart using IsValueShownAsLabel.
Just add your points with those labels as the x values:
chart1.Series[0].Points.AddXY("Frogs", 1);
chart1.Series[0].Points.AddXY("Hogs", 4);
// etc
Or, you could databind the points using two arrays:
string[] xvalues = new [] {"Frogs", "Hogs", "Bogs", "Slogs"};
int[] yvalues = new [] {1, 4, 9, 6};
chart1.Series[0].Points.DataBindXY(xvalues, yvalues);
You need to set the "Format" property of the LabelStyle on your axis.
See: http://msdn.microsoft.com/en-us/library/system.web.ui.datavisualization.charting.labelstyle(v=vs.110).aspx
There's a few reasonable tutorials on using the charting library out there if you search for them. Here's one: http://weblogs.asp.net/dwahlin/getting-started-with-the-asp-net-3-5-chart-control
Related
I want to add 3 Y-axes for the chart with different scales.
I want to get one x axis and different y axis. I did it like below code but I want to show one y axis like in the 2nd image that I attached..
My C# code so far:
private void checkBoxUseMultipleYAxis_CheckedChanged(object sender, EventArgs e)
{
if (checkBoxUseMultipleYAxis.Checked)
{
// Set custom chart area position
chart1.ChartAreas["ChartArea1"].Position = new ElementPosition(25, 10, 68, 85);
chart1.ChartAreas["ChartArea1"].InnerPlotPosition = new ElementPosition(10, 0, 90, 90);``
// Create extra Y axis for second and third series
CreateYAxis(chart1, chart1.ChartAreas["ChartArea1"], chart1.Series["Current"], 13, 8);
CreateYAxis(chart1, chart1.ChartAreas["ChartArea1"], chart1.Series["Capacity"], 22, 8);
}
else
{
// Set default chart areas
chart1.Series["Current"].ChartArea = "ChartArea1";
chart1.Series["Capacity"].ChartArea = "ChartArea1";
// Remove newly created series and chart areas
while (chart1.Series.Count > 3)
{
chart1.Series.RemoveAt(3);
}
while (chart1.ChartAreas.Count > 1)
{
chart1.ChartAreas.RemoveAt(1);
}
// Set default chart are position to Auto
chart1.ChartAreas["ChartArea1"].Position.Auto = true;
chart1.ChartAreas["ChartArea1"].InnerPlotPosition.Auto = true;
}
}
public void CreateYAxis(Chart chart, ChartArea area, Series series, float axisOffset, float labelsSize)
{
// Create new chart area for original series
ChartArea areaSeries = chart.ChartAreas.Add("ChartArea_" + series.Name);
areaSeries.BackColor = Color.Transparent;
areaSeries.BorderColor = Color.Transparent;
areaSeries.Position.FromRectangleF(area.Position.ToRectangleF());
areaSeries.InnerPlotPosition.FromRectangleF(area.InnerPlotPosition.ToRectangleF());
areaSeries.AxisX.MajorGrid.Enabled = false;
areaSeries.AxisX.MajorTickMark.Enabled = false;
areaSeries.AxisX.LabelStyle.Enabled = false;
areaSeries.AxisY.MajorGrid.Enabled = false;
areaSeries.AxisY.MajorTickMark.Enabled = false;
areaSeries.AxisY.LabelStyle.Enabled = false;
areaSeries.AxisY.IsStartedFromZero = area.AxisY.IsStartedFromZero;
series.ChartArea = areaSeries.Name;
// Create new chart area for axis
ChartArea areaAxis = chart.ChartAreas.Add("AxisY_" + series.ChartArea);
areaAxis.BackColor = Color.Transparent;
areaAxis.BorderColor = Color.Transparent;
areaAxis.Position.FromRectangleF(chart.ChartAreas[series.ChartArea].Position.ToRectangleF());
areaAxis.InnerPlotPosition.FromRectangleF(chart.ChartAreas[series.ChartArea].InnerPlotPosition.ToRectangleF());
// Create a copy of specified series
Series seriesCopy = chart.Series.Add(series.Name + "_Copy");
seriesCopy.ChartType = series.ChartType;
foreach (DataPoint point in series.Points)
{
seriesCopy.Points.AddXY(point.XValue, point.YValues[0]);
}
// Hide copied series
seriesCopy.IsVisibleInLegend = false;
seriesCopy.Color = Color.Transparent;
seriesCopy.BorderColor = Color.Transparent;
seriesCopy.ChartArea = areaAxis.Name;
// Disable drid lines & tickmarks
areaAxis.AxisX.LineWidth = 0;
areaAxis.AxisX.MajorGrid.Enabled = false;
areaAxis.AxisX.MajorTickMark.Enabled = false;
areaAxis.AxisX.LabelStyle.Enabled = false;
areaAxis.AxisY.MajorGrid.Enabled = false;
areaAxis.AxisY.IsStartedFromZero = area.AxisY.IsStartedFromZero;
areaAxis.AxisY.LabelStyle.Font = area.AxisY.LabelStyle.Font;
// Adjust area position
areaAxis.Position.X -= axisOffset;
areaAxis.InnerPlotPosition.X += labelsSize;
}
};
This is an interesting question with a piece of quite involved code which solves a problem that I never noticed..
Let's first talk about the basics.
In a chart you can add several series of data to the same area. As long as the ranges of y-values are more or less the same there is usually no problem. But if they aren't, the y-axis will scale so that all values will fit in the chart area. This means that those series with small ranges will get squashed. Here is an example:
In addition to the unreadable y-values for all but the orange series we also see that there is only one axis title; if it applies to all series, ok; but if it doesn't: best leave it out..
(Sometimes setting the y-axis to be logarithmic can help, but usually this would just confuse things and not help at all.)
This calls for more axes. In fact there is one extra axis built-in, right there just for the asking: Each chart area can have a primary y-axis to the left and another one, called 'secondary' AxisY2 to the right.
You can enable it and associate one series to it:
chart1.ChartAreas[0].AxisY2.Enabled = AxisEnabled.True;
chart1.Series[1].YAxisType = AxisType.Secondary;
This is fine and works well. But our example calls for more than 2 y-axes.. which is just what the code you found provides.
Let's have a look at the reults first:
This is nice; now we can see how the ranges differ from 0 - 30 to 0 - 120 , -20 - 30 and finally 0 - 1200.
But as all axes are added to the left they get further and further away from the plot area. Hence your question..
I found it easiest to expand on the code you found instead of writing a better version from scratch. This implies that most issues with the code are still there:
Not modularized
Routines depend on 'magic' values
Finding those values by trial and error is tedious
I have added two params to the CreateYAxis method; one sets the width of the added axis-areas and the other toggles adding them to the left or right.
Let's look at the result first:
Now for the changed code:
public void CreateYAxis(Chart chart, ChartArea area, Series series,
float axisX, float axisWidth, float labelsSize, bool alignLeft)
{
chart.ApplyPaletteColors(); // (*)
// Create new chart area for original series
ChartArea areaSeries = chart.ChartAreas.Add("CAs_" + series.Name);
areaSeries.BackColor = Color.Transparent;
areaSeries.BorderColor = Color.Transparent;
areaSeries.Position.FromRectangleF(area.Position.ToRectangleF());
areaSeries.InnerPlotPosition.FromRectangleF(area.InnerPlotPosition.ToRectangleF());
areaSeries.AxisX.MajorGrid.Enabled = false;
areaSeries.AxisX.MajorTickMark.Enabled = false;
areaSeries.AxisX.LabelStyle.Enabled = false;
areaSeries.AxisY.MajorGrid.Enabled = false;
areaSeries.AxisY.MajorTickMark.Enabled = false;
areaSeries.AxisY.LabelStyle.Enabled = false;
areaSeries.AxisY.IsStartedFromZero = area.AxisY.IsStartedFromZero;
// associate series with new ca
series.ChartArea = areaSeries.Name;
// Create new chart area for axis
ChartArea areaAxis = chart.ChartAreas.Add("CA_AxY_" + series.ChartArea);
areaAxis.BackColor = Color.Transparent;
areaAxis.BorderColor = Color.Transparent;
RectangleF oRect = area.Position.ToRectangleF();
areaAxis.Position = new ElementPosition(oRect.X, oRect.Y, axisWidth, oRect.Height);
areaAxis.InnerPlotPosition
.FromRectangleF(areaSeries.InnerPlotPosition.ToRectangleF());
// Create a copy of specified series
Series seriesCopy = chart.Series.Add(series.Name + "_Copy");
seriesCopy.ChartType = series.ChartType;
seriesCopy.YAxisType = alignLeft ? AxisType.Primary : AxisType.Secondary; // (**)
foreach (DataPoint point in series.Points)
{
seriesCopy.Points.AddXY(point.XValue, point.YValues[0]);
}
// Hide copied series
seriesCopy.IsVisibleInLegend = false;
seriesCopy.Color = Color.Transparent;
seriesCopy.BorderColor = Color.Transparent;
seriesCopy.ChartArea = areaAxis.Name;
// Disable grid lines & tickmarks
areaAxis.AxisX.LineWidth = 0;
areaAxis.AxisX.MajorGrid.Enabled = false;
areaAxis.AxisX.MajorTickMark.Enabled = false;
areaAxis.AxisX.LabelStyle.Enabled = false;
Axis areaAxisAxisY = alignLeft ? areaAxis.AxisY : areaAxis.AxisY2; // (**)
areaAxisAxisY.MajorGrid.Enabled = false;
areaAxisAxisY.IsStartedFromZero = area.AxisY.IsStartedFromZero;
areaAxisAxisY.LabelStyle.Font = area.AxisY.LabelStyle.Font;
areaAxisAxisY.Title = series.Name;
areaAxisAxisY.LineColor = series.Color; // (*)
areaAxisAxisY.TitleForeColor = Color.DarkCyan; // (*)
// Adjust area position
areaAxis.Position.X = axisX;
areaAxis.InnerPlotPosition.X += labelsSize;
}
I have added a little code to make the axes have the series colors. (*)
The alignLeft will, when false, pick the secondary axis instead of the primary one. (**)
The necessary numbers are used when calling the method in the checkbox event.
Here are the lines and numbers that were used for my screenshots:
First the normal one..
// Set custom chart area position
ChartArea ca = chart1.ChartAreas["ChartArea1"];
ca.Position = new ElementPosition(23, 10, 77, 85);
ca.InnerPlotPosition = new ElementPosition(12, 0, 67, 90);
// Create extra Y axis for some series
CreateYAxis(chart1, ca, chart1.Series["Current"], 5, 9, 8, true);
CreateYAxis(chart1, ca, chart1.Series["Capacity"], 13, 9, 8, true);
CreateYAxis(chart1, ca, chart1.Series["testing"], 21, 9, 8, true);
..then the one with one series axis added to the right:
// Set custom chart area position
ChartArea ca = chart1.ChartAreas["ChartArea1"];
ca .Position = new ElementPosition(15, 10, 83, 85);
ca .InnerPlotPosition = new ElementPosition(12, 0, 67, 90);
// Create extra Y axis for some series
CreateYAxis(chart1,ca , chart1.Series["Current"], 5, 9, 8, true);
CreateYAxis(chart1, ca , chart1.Series["Capacity"], 13, 9, 8, true);
CreateYAxis(chart1, ca , chart1.Series["testing"], 64, 21, 8, false);
Note that the else branch of the checkbox event tries to remove the extra chart areas. It has a hard-coded numer 3; this and the whole reversal code is not quite stable!
A short desciption about what the code itself does:
It adds two extra chart areas for each extra 'series axis':
One to associate the original series to. This one must always have the same position&size as the orginal chart area! Its purpose is to allow the graphics to scale to the maximum extent, which it will do, as no other series is associated with this new chart area. The graphics stays visible but all other parts like axes borders etc are invisible.
The other one will show the axis. Here everything but the axis is invisible; and to fill the axis the points from the original series are copied to a new series, which is associated with this chart area.
Final note about usage: The whole usage still depends on the numbers you use to lay out the chart areas! I wrote myself a little helper tool, which you can download if you are interested. Here is it at work:
There are 2 lists called listversion & MIN_list. Using values of these list I have created a line chart. Everything is work fine. But I am wondering whether it is possible to add a data table with legend keys in to the chart like MS Excel.
chart.Series.Clear();
chart.ChartAreas[0].AxisX.Title = "Version";
chart.ChartAreas[0].AxisX.TitleFont = new System.Drawing.Font("Arial", 12, FontStyle.Regular);
chart.ChartAreas[0].AxisY.Title = "Time";
chart.ChartAreas[0].AxisY.TitleFont = new System.Drawing.Font("Arial", 12, FontStyle.Regular);
Series MIN = chart.Series.Add("Minimum");
MIN.Points.DataBindXY(listVersion, MIN_list[j]);
MIN.ChartType = SeriesChartType.Line;
MIN.Color = Color.Red;
MIN.BorderWidth = 3;
I am looking forward to something like this
If it is possible How can I do it ?
Thank you.
Yes you can do that:
Here are the steps I took:
First we disable the original Legend as it can't be manipulated the way we need to..:
chart1.Legends[0].Enabled = false;
Now we create a new one and a shortcut reference to it:
chart1.Legends.Add(new Legend("customLegend"));
Legend L = chart1.Legends[1];
Next we do some positioning:
L.DockedToChartArea = chart1.ChartAreas[0].Name; // the ca it refers to
L.Docking = Docking.Bottom;
L.IsDockedInsideChartArea = false;
L.Alignment = StringAlignment.Center;
Now we want to fill in one line for headers and one line per series.
I use a common function for both and pass in a flag to indicate whether the headers (x-values) or the cell data (y-values) should be filled in. Here is how I call the function:
addValuesToLegend(L, chart1.Series[0], false);
foreach (Series s in chart1.Series) addValuesToLegend(L, s, true);
Note that for this to work we need a few preparations in our Series:
We need to set the Series.Colors explicitly or else we can't refer to them.
I have added a format string to the Tag of each series; but maybe you find a better solution that avoids hard-coding the format for the header..
So here is the function that does all the filling and some styling:
void addValuesToLegend(Legend L, Series S, bool addYValues)
{
// create a new row for the legend
LegendItem newItem = new LegendItem();
// if the series has a markerstyle we show it:
newItem.MarkerStyle = S.MarkerStyle ;
newItem.MarkerColor = S.Color;
newItem.MarkerSize *= 2; // bump up the size
if (S.MarkerStyle == MarkerStyle.None)
{
// no markerstyle so we just show a colored rectangle
// you could add code to show a line for other chart types..
newItem.ImageStyle = LegendImageStyle.Rectangle;
newItem.BorderColor = Color.Transparent;
newItem.Color = S.Color;
}
else newItem.ImageStyle = LegendImageStyle.Marker;
// the rowheader shows the marker or the series color
newItem.Cells.Add(LegendCellType.SeriesSymbol, "", ContentAlignment.MiddleCenter);
// add series name
newItem.Cells.Add(LegendCellType.Text, addYValues ? S.Name : "",
ContentAlignment.MiddleLeft);
// combine the 1st two cells:
newItem.Cells[1].CellSpan = 2;
// we hide the first cell of the header row
if (!addYValues)
{
newItem.ImageStyle = LegendImageStyle.Line;
newItem.Color = Color.Transparent;
newItem.Cells[0].Tag = "*"; // we mark the 1st cell for not painting it
}
// now we loop over the points:
foreach (DataPoint dp in S.Points)
{
// we format the y-value
string t = dp.YValues[0].ToString(S.Tag.ToString());
// or maybe the x-value. it is a datatime so we need to convert it!
// note the escaping to work around my european locale!
if (!addYValues) t = DateTime.FromOADate(dp.XValue).ToString("M\\/d\\/yyyy");
newItem.Cells.Add(LegendCellType.Text, t, ContentAlignment.MiddleCenter);
}
// we can create some white space around the data:
foreach (var cell in newItem.Cells) cell.Margins = new Margins(25, 20, 25, 20);
// finally add the row of cells:
L.CustomItems.Add(newItem);
}
To draw the borders around the cells of our legend table we need to code the PrePaint event:
private void chart1_PrePaint(object sender, ChartPaintEventArgs e)
{
LegendCell cell = e.ChartElement as LegendCell;
if (cell != null && cell.Tag == null)
{
RectangleF r = e.ChartGraphics.GetAbsoluteRectangle(e.Position.ToRectangleF());
e.ChartGraphics.Graphics.DrawRectangle(Pens.DimGray,Rectangle.Round(r));
// Let's hide the left border when there is a cell span!
if (cell.CellSpan != 1)
e.ChartGraphics.Graphics.DrawLine(Pens.White,
r.Left, r.Top+1, r.Left, r.Bottom-1);
}
}
You can add more styling although I'm not sure if you can match the example perfectly..
I want to add text(say, annotations) in MS chart(winforms) like (10, 20) , (30, 40) where I already have a scroll bar.
I can able to draw strings(graphics.drawstring) in Chart, but on scrolling the horizontal scroll bar, the text which I have drawn remains static and immovable.
On scrolling the scrollbar, the text which I have drawn also should move along with my horizontal scrolling.
My code follows:
chart2.BorderSkin.SkinStyle = BorderSkinStyle.Emboss;
chart2.BorderlineColor = System.Drawing.Color.FromArgb(26, 59, 105);
chart2.BorderlineWidth = 3;
chart2.BackColor = Color.White;
chart2.ChartAreas.Add("chtArea");
chart2.ChartAreas[0].AxisX.Title = "Category Name";
chart2.ChartAreas[0].AxisX.TitleFont =
new System.Drawing.Font("Verdana", 11, System.Drawing.FontStyle.Bold);
chart2.ChartAreas[0].AxisY.Title = "UnitPrice";
chart2.ChartAreas[0].AxisY.TitleFont =
new System.Drawing.Font("Verdana", 11, System.Drawing.FontStyle.Bold);
chart2.ChartAreas[0].BorderDashStyle = ChartDashStyle.Solid;
chart2.ChartAreas[0].BorderWidth = 2;
chart2.ChartAreas["chtArea"].AxisX.ScrollBar.Enabled = true;
chart2.ChartAreas["chtArea"].CursorX.IsUserEnabled = true;
chart2.ChartAreas["chtArea"].CursorX.IsUserSelectionEnabled = true;
chart2.ChartAreas["chtArea"].AxisX.ScaleView.Zoomable = false;
chart2.ChartAreas["chtArea"].AxisX.ScrollBar.IsPositionedInside = true;
chart2.ChartAreas["chtArea"].AxisX.ScaleView.Size = 20;
chart2.ChartAreas[0].AxisX.ScaleView.SmallScrollSizeType = DateTimeIntervalType.Seconds;
chart2.ChartAreas[0].AxisX.ScaleView.SmallScrollSize = 1;
chart2.Legends.Add("UnitPrice");
chart2.Series.Add("UnitPrice");
chart2.Series[0].ChartType = SeriesChartType.Line;
Random rand = new Random();
var valuesArray = Enumerable.Range(0, 500).Select(x => rand.Next(0, 100)).ToArray();
for (int i = 0; i < 500; i++)
{
chart2.Series["UnitPrice"].Points.AddXY(i+10, valuesArray[i]);
}
I tried TextAnnotaions, Line annotations, etc Nothing helped me.
Then I tried drawing dynamic labels inside MS chart also. Labels remain immovable while scrolling horizontal scroll bar.
This code works perfectly in your machine also.
Sounds a lot as if you want to add TextAnnotations.
If you want them to stick with your data points you should anchor them to the points they shall stay with.
Here are a few examples:
// directly anchored to a point
TextAnnotation TA1 = new TextAnnotation();
TA1.Text = "DataPoint 222";
TA1.SetAnchor(chart2.Series["UnitPrice"].Points[222]);
chart2.Annotations.Add(TA1);
// anchored to a point but shifted down
TextAnnotation TA2 = new TextAnnotation();
TA2.Text = "DataPoint 111";
TA2.AnchorDataPoint = chart2.Series["UnitPrice"].Points[111];
TA2.AnchorY = 0;
chart2.Annotations.Add(TA2);
// this one is not anchored on a point:
TextAnnotation TA3 = new TextAnnotation();
TA3.Text = "At 50% width BC";
TA3.AnchorX = 50; // 50% of chart width
TA3.AnchorY = 20; // 20% of chart height, from top!
TA3.Alignment = ContentAlignment.BottomCenter; // try a few!
chart2.Annotations.Add(TA3);
By default they either anchor to DataPoints or are positioned in % of the chart size.
It is also possible to set the positions according to pixel coordinates, but for this you need to calculate the positions each time the chart changes its view!
See here for an example how to transform chart data positions to chart control coordinates and vice versa.. (Not really recommended, though)
I have this chart in my WinForms application:
and I need to show the reference number of the red line (in this case "37")
When I tried to add a CustomLabel to the Y Axis, all the other numbers disappeared.
I tried changing the RowIndex of the label to 2, but here's the result:
it only shows an inverted "3" instead of a straight "37"
Here's what I need:
how can I do this?
You can use a TextAnnotation to show a data value :
ChartArea ca = chart1.ChartAreas[0];
TextAnnotation ta = new TextAnnotation();
DataPoint dp0 = Series2.Points[0]; // pick a datapoint!
ta.Text = dp0.YValues[0] + "";
ta.ForeColor = dp0.Color;
ta.AxisY = ca.AxisY;
ta.Y = dp0.YValues[0];
ta.X = 5; // pick a value that fits with your y-axis!
ta.Alignment = ContentAlignment.MiddleLeft;
chart1.Annotations.Add(ta);
I have a charting application that has an overlay function which reassigns the 'from' chart series to the 'to' chart using this code :
chTo.Series.Add(chFrom.Series[s]); //Reassign series to new chart
chTo.Legends.Add(chFrom.Legends[s]); //Reassign legend to new chart
Works great. However, I am trying to implement tooltips for the legends and am running into an issue where only the first legend in the chart will show tooltips. When I do a hittest only the first legend is recognized. All subsequent legends, while visible on the chart, aren't 'seen' to the hittest method. I'm thinking this is why the tooltips aren't showing as there is no object to trigger the mouseover event for the tooltip.
I have been unable to find a way to 'expand' the legend area (as detected by the hittest method) to make this work.
Does anyone have any ideas? Thanks!
Responding to King King --
The original legend is created in the same method as the chart thus:
//Create the series legend
chartSel.Series[ySeries.Name].ChartArea = "ChartArea1";
chartSel.Legends.Remove(chartSel.Legends.FindByName("Legend1"));
chartSel.Legends.Add(ySeries.Name);
chartSel.Legends[0].Name = ySeries.Name;
//Format the series legend
chartSel.Legends[ySeries.Name].Docking = Docking.Right;
chartSel.Legends[ySeries.Name].DockedToChartArea = "ChartArea1";
chartSel.Legends[ySeries.Name].Alignment = StringAlignment.Near;
chartSel.Legends[ySeries.Name].IsDockedInsideChartArea = false;
chartSel.Legends[ySeries.Name].LegendStyle = LegendStyle.Table; //.Row;
chartSel.Legends[ySeries.Name].TableStyle = LegendTableStyle.Tall;
chartSel.Legends[ySeries.Name].IsEquallySpacedItems = false;
chartSel.Legends[ySeries.Name].Font = new Font("Segoe UI", 7, FontStyle.Bold);
//chartSel.Legends[ySeries.Name].TextWrapThreshold = 17; // 19;
chartSel.Legends[ySeries.Name].Position.Auto = false;
chartSel.Legends[ySeries.Name].Position.X = 80;
chartSel.Legends[ySeries.Name].Position.Y = 2;
chartSel.Legends[ySeries.Name].Position.Width = 18;
chartSel.Legends[ySeries.Name].Position.Height = 12;
//Format series data point value cell
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ""));
chartSel.Legends[ySeries.Name].CellColumns[0].Alignment = ContentAlignment.MiddleLeft; //.TopLeft;
chartSel.Legends[ySeries.Name].CellColumns[0].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(10, 10, 1, 1);
chartSel.Legends[ySeries.Name].CellColumns[0].MinimumWidth = 500;
chartSel.Legends[ySeries.Name].CellColumns[0].MaximumWidth = 500;
chartSel.Legends[ySeries.Name].CellColumns[0].BackColor = Color.FromArgb(120, chartSel.Series[ySeries.Name].Color);
//Format legend cell spacer
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ""));
chartSel.Legends[ySeries.Name].CellColumns[1].Alignment = ContentAlignment.TopLeft;
chartSel.Legends[ySeries.Name].CellColumns[1].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(0, 0, 0, 0);
chartSel.Legends[ySeries.Name].CellColumns[1].MinimumWidth = 25;
chartSel.Legends[ySeries.Name].CellColumns[1].MaximumWidth = 25;
chartSel.Legends[ySeries.Name].CellColumns[1].BackColor = Color.Black;
//Format series title cell
chartSel.Legends[ySeries.Name].CellColumns.Add(new LegendCellColumn("", LegendCellColumnType.Text, ySeries.Name));
chartSel.Legends[ySeries.Name].CellColumns[2].Alignment = ContentAlignment.MiddleLeft;
chartSel.Legends[ySeries.Name].CellColumns[2].Margins = new System.Windows.Forms.DataVisualization.Charting.Margins(0, 0, 1, 1);
chartSel.Legends[ySeries.Name].CellColumns[2].MinimumWidth = 1475; //1500;
chartSel.Legends[ySeries.Name].CellColumns[2].MaximumWidth = 1475; //1500;
chartSel.Legends[ySeries.Name].CellColumns[2].ToolTip = ySeries.Name;
After the series and legends have been reassigned (using the code in my original post) I then set the legend values based on the cursor position located by the following hittest in response to a mouse-down event:
pt = activePanel.PointToClient(Control.MousePosition);
ch = activePanel.GetChildAtPoint(pt) as Chart;
if (ch != null)
{
HitTestResult ht = ch.HitTest(e.X, e.Y, false);
if (ht.ChartElementType == ChartElementType.PlottingArea)
{
SetLegendValueText(ht, ch);
}
}
private void SetLegendValueText(HitTestResult ht, Chart ch)
{
//Get the datapoint 'x' index value
int dpIndex = 0;
if (ht != null)
{
switch (ht.ChartElementType)
{
case ChartElementType.DataPoint: //Cursor is on a series line
DataPoint dp = ht.Object as DataPoint;
if (dp != null)
{
dpIndex = ht.PointIndex;
}
break;
case ChartElementType.PlottingArea: //Cursor is somewhere in the plot area of the chart
dpIndex = (int)ht.ChartArea.CursorX.Position;
break;
}
}
//Set legend value and legend tooltip
for (int x = 0; x < ch.Legends.Count; x++) //foreach (Series s in ch.Series)
{
if (dpIndex > 0)
{
ch.Legends[x].Name = "Legend_" + x;
ch.Legends[x].CellColumns[0].Text = ch.Series[x].Points[dpIndex - 1].YValues[0].ToString();
ch.Legends[x].CellColumns[0].ToolTip = ch.Legends[x].CellColumns[0].Text;
}
}
}
So, I end up with the legends looking the way I want them, but the tooltips only show for the first legend item. I've tried to do custom items as well. With them I get the tooltips, but I lose the formatting. This has been driving me crazy for weeks (off and on) and I would really like to move on to other issues. Clearly (to me anyway), I am not doing something right simply because I don't know everything there is to know about the charts, and the MSChart Samples are of very limited benefit.
I'd be most grateful if I could be pointed in the right direction.