i got a Little "Problem", i want to create a Chart looking like this:
So basically
Series 1 = Normal bar Chart. Color green if it Ends before the "time max" (series2) Series 2 = just a DataPoint / Marker on top of series 1 items.
I am struggling with this though...
my Code:
chart_TimeChart.Series.Clear();
string series_timeneeded = "Time Needed";
chart_TimeChart.Series.Add(series_timeneeded);
chart_TimeChart.Series[series_timeneeded]["PixelPointWidth"] = "5";
chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.Size = 10;
chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.ButtonStyle = ScrollBarButtonStyles.SmallScroll;
chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.IsPositionedInside = true;
chart_TimeChart.ChartAreas[0].AxisY.ScrollBar.Enabled = true;
chart_TimeChart.Series[series_timeneeded].BorderWidth = 2;
chart_TimeChart.Series[series_timeneeded].ChartType = SeriesChartType.StackedBar;
chart_TimeChart.Series[series_timeneeded].YValueType = ChartValueType.Time;
chart_TimeChart.ChartAreas[0].AxisY.LabelStyle.Format = "HH:mm:ss";
chart_TimeChart.Series[series_timeneeded].XValueType = ChartValueType.String;
for (int i = 0; i < MaxNumber; i++)
{
chart_TimeChart.Series[series_timeneeded].Points.AddXY("item"+ " " + (i + 1).ToString(), DateTime.Now.Add(Timespans[i]));
}
chart_TimeChart.Series.Add(series_FinishTime);
chart_TimeChart.Series[series_FinishTime].ChartType = SeriesChartType.StackedBar;
chart_TimeChart.Series[series_FinishTime].BorderWidth = 0;
chart_TimeChart.Series[series_FinishTime].MarkerSize = 15;
chart_TimeChart.Series[series_FinishTime].MarkerStyle = MarkerStyle.Square;
chart_TimeChart.Series[series_FinishTime].MarkerColor = Color.Black;
chart_TimeChart.Series[series_FinishTime].YValueType = ChartValueType.DateTime;
chart_TimeChart.Series[series_FinishTime].XValueType = ChartValueType.String;
for (int i = 0; i < MaxNumber; i++)
{
DateTime YPosition = GetFinishTime(i);
chart_TimeChart.Series[series_FinishTime].Points.AddXY("item"+ " " +(i+1).ToString(), YPosition);
}
but this only Displays the 2nd series on top of the first one but the first one isnt visible anymore. The Maker of series 2 isnt shown but instead the bar is (eventhough i made borderwidth to 0). In my opinion/thinking i just have to make the "bar" of series 2 invisible and just Show the marker Points for series 2.
Any ideas?
Update:
string seriesname = Name+ i.ToString();
chart_TimeChart.Series.Add(seriesname);
chart_TimeChart.Series[seriesname].SetCustomProperty("DrawSideBySide", "false");
chart_TimeChart.Series[seriesname].SetCustomProperty("StackedGroupName", seriesname);
chart_TimeChart.Series[seriesname].ChartType = SeriesChartType.StackedBar; //Y and X are exchanged
chart_TimeChart.Series[seriesname].YValueType = ChartValueType.Time;
chart_TimeChart.ChartAreas[0].AxisY.LabelStyle.Format = "HH:mm:ss";
chart_TimeChart.Series[seriesname].XValueType = ChartValueType.String;
DateTime TimeNeeded = DateTime.Now.Add(List_AllLiniengroupsTimespans[k][i]);
DateTime TimeMax = GetFinishTime(k, i);
TimeSpan TimeDifference = TimeNeeded - TimeMax;
if (TimeNeeded > TimeMax) //All good
{
chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded); //Time till finish
chart_TimeChart.Series[seriesname].Points[0].Color = Color.Blue;
chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);
chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded.Add(TimeDifference)); //time left
chart_TimeChart.Series[seriesname].Points[1].Color = Color.Red;
chart_TimeChart.Series[seriesname].Points[1].SetCustomProperty("StackedGroupName", seriesname);
}
else if (TimeMax > TimeNeeded) //wont make it in time
{
chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded); //time till still okay
chart_TimeChart.Series[seriesname].Points[0].Color = Color.Blue;
chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);
chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded.Add(TimeDifference)); //Time that is too much
chart_TimeChart.Series[seriesname].Points[1].Color = Color.Green;
chart_TimeChart.Series[seriesname].Points[1].SetCustomProperty("StackedGroupName", seriesname);
}
else if (TimeMax == TimeNeeded) //fits exactly
{
chart_TimeChart.Series[seriesname].Points.AddXY(seriesname, TimeNeeded);
chart_TimeChart.Series[seriesname].Points[0].Color = Color.DarkOrange;
chart_TimeChart.Series[seriesname].Points[0].SetCustomProperty("StackedGroupName", seriesname);
}
the Code will be displayed as:
but i want it to look like this:
!! See the update below !!
If you really want to create a StackedBar chart, your chart has two issues:
If you want to stack datapoints they need to have meaningful x-values; without them how can it know what to stack on each other?
You add strings, which look fine but simply don't work. That is because the DataPoint.XValue field is double and when you add string into it it is set to 0 !! Your string is copied to the Label but otherwise lost.
So you need to come up with a suitable numeric value you use for the x-values..
And you also need to group the series you want to stack. For this there is a special property called StackedGroupName which serves to group those series that shall be stacked.
Here is how you can use it:
yourSeries1.SetCustomProperty("StackedGroupName", "Group1");
For a full example see this post !
It also shows one way of setting the Labels with string values of your choice..
This is the way to go for real StackedBar charts. Your workaround may or may not work. You could try to make the colors transparent or equal to the chart's backcolor; but it won't be more than a hack, imo.
Update
I guess I have misread the question. From what I see you do not really want to create a stacked chart.
Instead you struggle with these issues:
displaying bars at the same y-spot
making some bars invisible
displaying a vertical line as a marker
Let's tackle each:
Some column types including all Bars, Columns and then some have a little known special property called DrawSideBySide.
By default is is set to Auto, which will work like True. This is usually fine as we don't want bars to sit upon each other, effectively hiding all or part of the overlaid points.
But here we do want them to share the same y-position, so we need to set the property to false for at least one Series; the others (on Auto) will follow..:
You can do it either like this:
aSeries["DrawSideBySide"] = "false";
or like this:
aSeries.SetCustomProperty("DrawSideBySide", "false");
Next we hide the overlaid Series; this is simple:
aSeries.Color = Color.Transparent;
The last issue is displaying a line marker. There is no such MarkerStyle, so we need to use a custom style. For this we need to create a suitable bitmap and add it as a NamedImage to the chart's Images collection.
This sounds more complicated than it is; however the MarkerImage will not be scaled, so we need to created suitable sizes whenever we resize the Chart or add/remove points. I will ignore this complication for now..
int pointCount = 10;
Bitmap bmp = new Bitmap(2, chart.ClientSize.Height / pointCount - 5);
using (Graphics g = Graphics.FromImage(bmp)) g.Clear(Color.Black);
NamedImage marker = new NamedImage("marker", bmp);
chart.Images.Clear(); // quick & dirty
chart.Images.Add(marker);
Here is the result:
A few notes:
I would recommend to use variables for all chart elements you refer to repeatedly instead of using indexed references all the time. Less code, easier to read, a lot easier to maintain, and probably better performance.
Since your code called for the visible datapoints to be either red or green the Legend will not show a good representation. See here for an example of drawing a multi-colored legend item..
I used the chart height; this is not really recommended as there may be Titles or Legends or even more ChartAreas; instead you should use the height of the ChartArea, or even more precise, the height of the InnerPlotPosition. You would need to convert those from percentages to pixels. Not too hard, see below or see here
or here for more examples!
The markers should be adapted from the Resize and probably from the AxisViewChanged events. Putting it in a nice function to call (e.g. void setMarkerImage(Chart chart, Series s, string name, int width, Color c)) is always a good idea.
If you need to adapt the size of the marker image repeatedly, you may want to write better code for clearing the old one; this should include disposing of the Bitmap that was used before..
Here is an example:
var oldni = chart.Images.FindByName("marker");
if (oldni != null)
{
oldni.Image.Dispose();
chart.Images.Remove(oldni);
oldni.Dispose();
}
In some situations one needs to nudge the Chart to update some of its properties; RecalculateAxesScale is one such nudge.
Example for calculating a suitable marker height:
ChartArea ca = chart.ChartAreas[0];
ca.RecalculateAxesScale();
float cah = ca.Position.Height;
float iph = ca.InnerPlotPosition.Height;
float h = chart3.ClientSize.Height * cah / 100f * iph / 100f;
int mh = (int)(h / s.Points.Count);
Final note: The original answer stressed the importance of using meaningful x-values. Strings are useless! This was important for stacking bars; but it is equally important now, when we want bars to sit at the same vertical positions! Adding the x-values as strings is again resulting in nonsense..
(Since we have Bars the x-values go along the vertical axis and vice versa..)
Related
I have a Winforms chart in which I have temperature readings arriving and displaying every second. I like the way the chart works automatically handling the display of the values, but I want to change one simple thing.
I want to increase the minimum displayed y axis range, so it displays a range of 20. At the moment it only displays around 5. I have tried a few things:
//(when new data arrives...)
//Does not work, I think because by default, Size is always NaN?
if (chart1.ChartAreas[0].AxisY.ScaleView.Size < 20)
{
chart1.ChartAreas[0].AxisY.ScaleView.Size = 20;
}
None of these work either:
chart1.ChartAreas[0].AxisY.ScaleView.SmallScrollMinSize = 20;
chart1.ChartAreas[0].AxisY.ScaleView.SmallScrollSize = 20;
chart1.ChartAreas[0].AxisY.ScaleView.MinSize = 20;
chart1.ChartAreas[0].AxisY.Minimum //doesn't seem to have any effect
chart1.ChartAreas[0].AxisY.Maximum //doesn't seem to have any effect
I'm sure I've missed something simple. I hope I have anyway.
The 'minimum display range' is not something built-in in the MSChart control.
But you can easily fake it:
Add a dummy Series which contains only two points to make sure the display range will not go below the range of their y-values..:
int rangeMin = -10;
int rangeMax = 20;
sDummy = chart.Series.Add("dummy");
sDummy.Color = Color.Transparent;
sDummy.IsVisibleInLegend = false;
sDummy.ChartType = SeriesChartType.Point;
sDummy.Points.AddXY(0, rangeMin + 1);
sDummy.Points.AddXY(0, rangeMax - 1);
Style your y-axis as you like:
Axis ay = chart.ChartAreas[0].AxisY;
ay.MajorGrid.Interval = 5;
And add one or more data Series:
sData = chart.Series.Add("data");
sData.LegendText = "Temperature";
sData.ChartType = SeriesChartType.Line;
Now as you add data points with a larger range of values the y-axis will grow its display range to accommodate them. And if you remove the larger points it will shrink back, but not below the range needed for the dummy series..:
Note that since the Chart automatically adds some slack I reduce the range on both sides by 1; with other Intervals etc other numbers are needed..
The code to remove the larger values, btw:
var toRemove = sData.Points.Cast<DataPoint>()
.Where(x => x.YValues[0] >= rangeMax).ToList();
foreach (var dp in toRemove) sData.Points.Remove(dp);
I want to make a chart in C# with custom elements. What I have:
What I want:
Elements marked by red circles need to be replaced by the image. My program code is very short, just some values for the chart. All settings for chart were set by the "Collection" in Chart section (as shown on first image).
This is a BoxPlot chart and it takes 6 y-values.
You can add them or let the chart calculate them.
Looks like you want to add several images to the various y-values..
Here is an example of how to do that by owner-drawing the Chart. (No, not the whole chart, just a little extra custom-drawing ;-)
It adds an image to each of the y-values; it should be easy to adapt to only those values you really want. And if you only want one you may even do away with the ImageList and pick the image from the resources; (although using an ImageList is a nice way, as long as you can live with the limitations of 256x256 maximum size and all images having the same size and color depth..)
You seem to want one of these only:
4 Average and mean
5 Median
private void chart_PostPaint(object sender, ChartPaintEventArgs e)
{
Series s1 = chart.Series[0];
ChartArea ca = chart.ChartAreas[0];
Axis ax = ca.AxisX;
Axis ay = ca.AxisY;
Graphics g = e.ChartGraphics.Graphics;
int iw = imageList1.ImageSize.Width / 2;
int ih = imageList1.ImageSize.Height / 2;
foreach (DataPoint dp in s1.Points)
{
int x = (int) ax.ValueToPixelPosition(dp.XValue);
for (int i = 0; i < 6; i++)
{
int y = (int) ay.ValueToPixelPosition(dp.YValues[i]);
g.DrawImage(imageList1.Images[i], x - iw, y - ih);
}
}
}
I suggest to use png files with transparency and an odd width so they look nice and centered. (I used randomly 16x16, which is not quite that nice ;-) - For this you need to set the ImageSize and the ColorDepth of the ImageList.
To further style the chart you may use these special properties
Custom attributes
BoxPlotPercentile, BoxPlotSeries, BoxPlotShowAverage,
BoxPlotShowMedian, BoxPlotShowUnusualValues, BoxPlotWhiskerPercentile,
DrawSideBySide, MaxPixelPointWidth, MinPixelPointWidth,
PixelPointDepth, PixelPointGapDepth, PixelPointWidth, PointWidth
Note that you need to set them all as strings, maybe like this:
s1.SetCustomProperty("someAttribute", "someValue");
So I use an open source .NET library for plotting: FPlot
Here's the function that I'm trying to draw:
f(x,y) = x^2+3*y^2+2*x*y
Here's what I want it to look like:
Clarification:
I don't need the exact same appearence as in the image, I just need the plot to be mathematically correct
There are only 10 conours in the picture, I need as much as can be fit on the screen
Here's how I tried to do this:
var graphFunction = new Function2D();
graphFunction.source = "return (pow(x,2)+3*pow(y,2)+2*x*y)/10;";
/* I'm dividing by 10 because otherwise the whole plot is solid color */
graphFunction.Compile(true);
That's how the FPlot generated plot looks up close:
This is exactly what I want, but when I zoom out here's what happens:
Theese extra ellipses are not supposed to be there, in fact they are not there, this is just a graphical artefact, because when you zoom into one of theese 'fake' ellipses this is what you see:
The problem can be in this line:
graphFunction.source = "return (pow(x,2)+3*pow(y,2)+2*x*y)/10;";
...or in the FPlot source code. Any Ideas?
UPDATE:
So, z value in graph seems to be the problem. When value of a function, z = f(x,y) in a graph exceeds the z1 (max z) value it resets to z = z%z1 (same happens when z is lower then z0), which causes these "lines" - they are not countour lines, like I thought.
So that means the solution is: set z0 to min f(x,y) on screen, and set z1 to max f(x,y) on screen.
Make the displaying borders of your FPlotLibrary.GraphControl have the same value in all 3 dimensions and the problem goes away:
graphControl1.x0 = -40;
graphControl1.x1 = 40;
graphControl1.y0 = -40;
graphControl1.y1 = 40;
graphControl1.z0 = -40;
graphControl1.z1 = 40;
Btw, the "problem" reproduces, for instance, if you do
graphControl1.x0 = -40;
graphControl1.x1 = 40;
graphControl1.y0 = -40;
graphControl1.y1 = 40;
graphControl1.z0 = -1;
graphControl1.z1 = 1;
I am trying to control the value labels for my zedgraph's x-axis. Before, the labels would "fly around" and not really stay put on the axis. They might move left or right on the axis and pop in and out of existence based on the data. Like in the picture below
I first tried to draw the labels myself when I finally found good documentation for the zedgraph library. There I found the [AXIS].Scale options of MajorStep MinorStep and BaseTic. Which if set correctly should cause the labels to stay in place and just change value as the data is added.
The issue I am running into though is that my x-axis scale is in XDate units. Which means I cannot do the simple math I was hoping to. So I have since figured a way that I think I can find the values I need using TimeSpan and DateTime. Below curMaxX and curMinX are XDate values of the current minimum and maximum x-axis values (curMinX should basically be DateTime.Now because this data is realtime)
// Setup
TimeSpan scaleDist = curMaxX.DateTime.Subtract(curMinX.DateTime);
TimeSpan major = new TimeSpan(scaleDist.Ticks / 5);
TimeSpan minor = new TimeSpan(major.Ticks / 5);
TimeSpan baseT = new TimeSpan(curMinX.DateTime.Ticks + major.Ticks);
// Setting the values
myPane.XAxis.Scale.MajorStep = new XDate(new DateTime(major.Ticks));
myPane.XAxis.Scale.MinorStep = new XDate(new DateTime(minor.Ticks));
myPane.XAxis.Scale.BaseTic = new XDate(new DateTime(baseT.Ticks));
// Print statement of the values
SDist: 00:00:10
Major: 00:00:02
Minor: 00:00:00.4000000
BaseT: 735382.07:32:34
BaseTAsDateTime: 5/30/2014 7:32:34 AM
CurMinX: 5/30/2014 7:32:32 AM
Though setting the values as such still does not achieve what I want. With this code my x-axis comes out looking like so
It is somewhat closer to what I am looking for but still off. Only a single major tic is shown, and no minor tics. I am not sure what other ways there might be to specify the values.
For the major and minor step documentation it says
For Date axes, this value is defined in units of Major/MinorUnit.
Which my MajorUnit is minutes and the MinorUnit is seconds, but I pass them a XDate value which specifies both minutes and seconds. Also, setting the values as fractions of a whole (so if I want MajorStep to be 2 seconds I'd set it as (2/60)), causes nothing to show up.
Any ideas/suggestions?
Well, I determined that it was a waste of time to try and use the default zedgraph tics, steps, etc. So I made my own method to do so based on the data currently on the graph.
After I posted this I realized trying to figure out the tics was a lost cause so I looked for other ways to do it. I was going to add text beside each point showing its values, but it became very cluttered, then I realized I can just draw the text at the bottom of the graph which is what I wanted all along. I also added a little label on the right showing the current value.
A little bit o code
private void setPointLabels()
{
// All hail the almighty pane.
GraphPane myPane = theGraph.GraphPane;
// Give us some room to draw labels
myPane.Margin.Right = 50;
myPane.Margin.Bottom = 20;
// Dont show the default stuff
myPane.XAxis.Scale.IsVisible = false;
myPane.XAxis.MajorTic.IsAllTics = false;
myPane.XAxis.MinorTic.IsAllTics = false;
// Remove the old labels
myPane.GraphObjList.Clear();
// Get the curve showing our data
LineItem myCurve = (LineItem)myPane.CurveList[0];
// Show a voltage value on the far right
PointPair pt = myCurve.Points[myCurve.Points.Count - 1];
TextObj text = new TextObj(" " + pt.Y.ToString("f2"), pt.X, pt.Y, CoordType.AxisXYScale, AlignH.Left, AlignV.Center);
text.ZOrder = ZOrder.A_InFront;
text.FontSpec.Border.IsVisible = false;
text.FontSpec.Fill.IsVisible = false;
myPane.GraphObjList.Add(text);
// Determine a hardcoded yOffset for the labels
double yOffset = -1.2;
// Determine if we need to fix the center label
int fixVal = 1;
if (xScaleSec == 10)
fixVal = 0;
// Loop over each point in the curve
for (int i = 0; i < myCurve.Points.Count; i++)
{
if (i == 0 ||
i == (myCurve.Points.Count/4) ||
i == ((myCurve.Points.Count/2)-fixVal) ||
i == ((3*myCurve.Points.Count)/4) ||
i == myCurve.Points.Count-1)
{
PointPair aPt = myCurve.Points[i];
// Add a text object just below the x-axis showing the point's x-value
XDate xVal = new XDate(aPt.X);
TextObj label = new TextObj(xVal.ToString("hh:mm.ss"), aPt.X, myPane.YAxis.Scale.Min + yOffset, CoordType.AxisXYScale, AlignH.Center, AlignV.Center);
label.ZOrder = ZOrder.A_InFront;
label.FontSpec.Fill.IsVisible = false;
label.FontSpec.Border.IsVisible = false;
myPane.GraphObjList.Add(label);
// Add a line object on the x-axis representing a tic mark
LineObj aTic = new LineObj(aPt.X, myPane.YAxis.Scale.Min - (yOffset / 4), aPt.X, myPane.YAxis.Scale.Min + (yOffset / 4));
myPane.GraphObjList.Add(aTic);
}
}
}
I'm building a chart to show items by volume by category. So far I've been succcessful in showing items by volume as it's a simple x/y chart, however I'd like to show y2 and I know MS Chart Controls has a built in AxisY2 however when I try anything with it the Chart get's all funky.
Here's what I'm looking for (in ascii art):
item1 |[][][][][].............| cat1
item2 |[][]...................| cat2
item3 |[][....................| cat1
item4 |[][][][][][][][........| cat1
|_______________________|
0 1 2 3 4 5
Like previously mentioned I can get Items and counts to show fine as that's relatively easy, it's the Categories that I can't seem to place.
Thanks
Here's what did it for me- after I created the chart I added the following lines:
chrtMain.Series[0].YAxisType = AxisType.Primary;
chrtMain.Series[1].YAxisType = AxisType.Secondary;
chrtMain.ChartAreas[0].AxisY2.LineColor = Color.Transparent;
chrtMain.ChartAreas[0].AxisY2.MajorGrid.Enabled = false;
chrtMain.ChartAreas[0].AxisY2.Enabled = AxisEnabled.True;
chrtMain.ChartAreas[0].AxisY2.IsStartedFromZero = chrtMain.ChartAreas[0].AxisY.IsStartedFromZero;
There was no need to superimpose two charts or anything!
It gets even better:
For using the second Y axis, there is no need for a second chart area. You can decide per serie which axis you want to use with the Series.YAxisType property.
Take a look at the documentation on http://msdn.microsoft.com/en-us/library/dd489216.aspx
Martijn
Short Answer first : According to MS Examples, there is no straight way to do that, but just a workaround trick : Plot your series on a second chartArea matching exactly your existing area position, (by performing a copy of your Series) having invisible primary X/Y Axis and a visible secondary Y Axis (AxisY2). And set the chartArea and the copied series's backcolors to transparent. (This can be applied to secondary X axis in case of column graphs rather that bars)
//Suppose you already have a ChartArea with the series plotted and the left Y Axis
//Add a fake Area where the only appearent thing is your secondary Y Axis
ChartArea area1 = chart.ChartAreas.Add("ChartAreaCopy_" + series.Name);
area1.BackColor = Color.Transparent;
area1.BorderColor = Color.Transparent;
area1.Position.FromRectangleF(area.Position.ToRectangleF());
area1.InnerPlotPosition.FromRectangleF(area.InnerPlotPosition.ToRectangleF());
area1.AxisX.MajorGrid.Enabled = false;
area1.AxisX.MajorTickMark.Enabled = false;
area1.AxisX.LabelStyle.Enabled = false;
area1.AxisY.MajorGrid.Enabled = false;
area1.AxisY.MajorTickMark.Enabled = false;
area1.AxisY.LabelStyle.Enabled = false;
area1.AxisY2.Enabled = AxisEnabled.True;
area1.AxisY2.LabelStyle.Enabled = true;
// Create a copy of specified series, and change Y Values to categories
Series seriesCopy = chart.Series.Add(series.Name + "_Copy");
seriesCopy.ChartType = series.ChartType;
foreach(DataPoint point in series.Points)
{
double category = getYourItemCategory(point.XValue);
seriesCopy.Points.AddXY(point.XValue, category);
}
// Hide copied series
seriesCopy.IsVisibleInLegend = false;
seriesCopy.Color = Color.Transparent;
seriesCopy.BorderColor = Color.Transparent;
//Drop it in the chart to make the area show (only the AxisY2 should appear)
seriesCopy.ChartArea = area1.Name;
PS : I've spent two nights awake messing with MS chart controls, trying to put two different Y axis on a Chart Area. I wanted to put two differently scaled series (same X scale, different Y Scales : one on the left for Series A , the other on the right for Series B).
In fact, this proved to be a real nightmare, when one could expect this to be pretty straightforward. The truth is that MS Chart Controls are definitely NOT adapted for this particular use case IMHO. The multiple Y axis sample suggested in the MSCC sample examples is an awful and very ugly workaround, which requires two chartareas on top of the default one, playing with visibility and transparency, to achieve the desired effect (which sounds like a very bad illusion magic trick).
While hoping for this to be enriched and fixed in a proper way in future versions, if you really need an efficient way to manage multiple Y-Axis, sitck to ZedGraph
You can add as many series on the Y axis as you want, below code is an extract from a chart I use that has more than 2 secondary y axes, code is for vb.net but i'm sure you can work it out:
ChartKPI.Series.Clear()
ChartKPI.Series.Add("Series1")
ChartKPI.Series("Series1").XValueMember = "Date"
ChartKPI.Series("Series1").YValueMembers = "HSDPA_Vol_MBy"
ChartKPI.Series("Series1").Name = "HSDPA_Vol_MBy"
ChartKPI.Series("HSDPA_Vol_MBy").ChartType = SeriesChartType.Column
ChartKPI.Series("HSDPA_Vol_MBy").ToolTip = "HSDPA MBytes: #VAL"
ChartKPI.Series.Add("Series2")
ChartKPI.Series("Series2").YAxisType = AxisType.Secondary
ChartKPI.Series("Series2").XValueMember = "Date"
ChartKPI.Series("Series2").YValueMembers = "cs_voice_traffic"
ChartKPI.Series("Series2").Name = "cs_voice_traffic"
ChartKPI.Series("cs_voice_traffic").ChartType = SeriesChartType.Line
ChartKPI.Series("cs_voice_traffic").BorderWidth = 3
ChartKPI.Series("cs_voice_traffic").ToolTip = "CS Voice Traffic: #VAL"
ChartKPI.Series.Add("Series3")
ChartKPI.Series("Series3").YAxisType = AxisType.Secondary
ChartKPI.Series("Series3").XValueMember = "Date"
ChartKPI.Series("Series3").YValueMembers = "cs_conv_traffic"
ChartKPI.Series("Series3").Name = "cs_conv_traffic"
ChartKPI.Series("cs_conv_traffic").ChartType = SeriesChartType.Line
ChartKPI.Series("cs_conv_traffic").BorderWidth = 3
ChartKPI.Series("cs_conv_traffic").ToolTip = "CS Conv Traffic: #VAL"
ChartKPI.Series.Add("Series4")
ChartKPI.Series("Series4").YAxisType = AxisType.Secondary
ChartKPI.Series("Series4").XValueMember = "Date"
ChartKPI.Series("Series4").YValueMembers = "ps_backg_traffic_ul"
ChartKPI.Series("Series4").Name = "ps_backg_traffic_ul"
ChartKPI.Series("ps_backg_traffic_ul").ChartType = SeriesChartType.Line
ChartKPI.Series("ps_backg_traffic_ul").BorderWidth = 3
ChartKPI.Series("ps_backg_traffic_ul").ToolTip = "PS Backg Traffic UL: #VAL"
ChartKPI.Series.Add("Series5")
ChartKPI.Series("Series5").YAxisType = AxisType.Secondary
ChartKPI.Series("Series5").XValueMember = "Date"
ChartKPI.Series("Series5").YValueMembers = "ps_backg_traffic_dl"
ChartKPI.Series("Series5").Name = "ps_backg_traffic_dl"
ChartKPI.Series("ps_backg_traffic_dl").ChartType = SeriesChartType.Line
ChartKPI.Series("ps_backg_traffic_dl").BorderWidth = 3
ChartKPI.Series("ps_backg_traffic_dl").ToolTip = "PS Backg Traffic DL: #VAL"
ChartKPI.ChartAreas("ChartArea1").AxisX.Title = "HSDPA Traffic (MB)"
ChartKPI.ChartAreas("ChartArea1").AxisX.MajorGrid.Interval = 1
ChartKPI.ChartAreas("ChartArea1").AxisX.LabelStyle.Interval = 1
ChartKPI.ChartAreas("ChartArea1").AxisY.Title = "RRC Attempts"
ChartKPI.ChartAreas("ChartArea1").AxisY2.Title = "R99 Traffic (Erlang)"
ChartKPI.DataBind()
Solution:
chart1.ChartAreas[1].AlignWithChartArea = chart1.ChartAreas[0].Name;
chart1.ChartAreas[1].AlignmentOrientation = AreaAlignmentOrientations.All;