none
MS Chart: 4 Achsen RRS feed

  • Frage

  • Hallo,

    hat jemand von Euch Erfahrungen mit dem neuen .NET MSChart Control (.NET 3.5) und weiß, wie ich 4 Y-Achsen in das Diagramm bekomme? Ich möchte das so machen, dass auf der linken Seite 2 Y-Achsen (z.B. 0, 10, 20, .., 100 und 0, 25, 50, 75, .. 300) angezeigt werden und auf  der rechten Seite ebenfalls zwei Y-Achsen angezeigt werden. Es soll also 4 Serien mit Datenwerten im Diagramm geben.

    Dienstag, 9. November 2010 08:10

Antworten

  • Hallo SP.,

    Zunächst kann man ja über AxisY2 nebst Enabled setzen die zweite Y-Achse aktivieren.
    Die 2 anderen könnte man "zum Beispiel" als zweite ChartArea (mit Add) darunter platzieren. Etwa so:

    [Align Multiple Chart Areas « Better Dashboards]
    http://betterdashboards.wordpress.com/2009/02/11/align-multiple-chart-areas/

    Hier weitere Beispiele:

    [Samples Environment for Microsoft Chart Controls - Home]
    http://code.msdn.microsoft.com/mschart

    Will man zwei links und zwei rechts positionieren, könnte man über die InnerPlotPosition die ChartArea auch woanders platzieren und ggf. deren StripLines nicht anzeigen.


    ciao Frank
    • Als Antwort markiert SPDeveloperXP Donnerstag, 11. November 2010 12:06
    Mittwoch, 10. November 2010 08:40
  • Hallo SP.,

    ja, Du verstehst mich richtig, leider "kenne" ich da keine bessere Möglichkeit bei 4 so (wie bei Dir) platzierten Achsen.

    Siehe Referenz-Doku zur [Axis Klasse] :

    • "Hinweis   Es kann für jedes ChartArea-Objekt nur einen Satz von Achsen geben – zwei vertikale und zwei horizontale."

    Insofern meine "Folgerung": mehrere ChartArea's sind nötig.


    ciao Frank
    • Als Antwort markiert SPDeveloperXP Donnerstag, 11. November 2010 12:06
    Donnerstag, 11. November 2010 10:35
  • Hallo SP.,

    Ein Beispiel für 3 Y-Achsen findest Du in den downloadbaren WinFormChartSamples:
    (s. Contents > Chart Features > Axes > Multiple Y Axes)
    http://code.msdn.microsoft.com/mschart/Release/ProjectReleases.aspx?ReleaseId=1591

    Hier eine etwas erweiterte Implementation für die gewünschten 4 Achsen (2:2), didaktisch aufbereitet:

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Windows.Forms.DataVisualization.Charting;
    
    namespace ChartMultipleAxis
    {
     public partial class Form1 : Form
     {
     private const int MAX_POINTS = 13;
     private bool showCharAreaRectangle = true;
     private bool showInnerPlotRectangle = true;
    
     public Form1()
     {
     InitializeComponent();
     }
    
     private void Form1_Load(object sender, EventArgs e) {
     PopulateSeries();
     CreateAllYAxes();
     }
    
     private void PopulateSeries() {
     GenerateRandomSeries();
     }
    
    private void GenerateRandomSeries() {
      Random random = new Random(DateTime.Now.Millisecond);
    
      chart1.Series.Clear();
    
      for (int seriesIndex = 0; seriesIndex < 4; seriesIndex++)
      {
        Series series = new Series { ChartType = SeriesChartType.Line, BorderWidth = 2 };
        chart1.Series.Add(series);
        if (seriesIndex % 2 != 0) series.YAxisType = AxisType.Secondary;
      }
    
    
      for (int pointIndex = 0; pointIndex < MAX_POINTS; pointIndex++) {
        DateTime dateX = DateTime.Now.Date.AddDays(pointIndex);
    
        for (int seriesIndex = 0; seriesIndex < chart1.Series.Count; seriesIndex++) {
          chart1.Series[seriesIndex].Points.AddXY(dateX, random.Next(1, (seriesIndex + 1) * 1000));
        }
      }
    }
    
     private void CreateAllYAxes() {
     HideUnnededChartElements();
     CreateCustomChartAreaPositions();
     CreateAdditionalYAxes();
     }
    
     private void HideUnnededChartElements() {
     chart1.Legends[0].Enabled = false;
     chart1.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
     chart1.ChartAreas[0].AxisY2.MajorGrid.Enabled = false;
     chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
     }
    
     private void CreateCustomChartAreaPositions() {
     // Note: All positions are relative 
     chart1.ChartAreas[0].Position = new ElementPosition(20, 20, 60, 60); 
     chart1.ChartAreas[0].InnerPlotPosition = new ElementPosition(10, 10, 80, 80);
     }
    
     private void CreateAdditionalYAxes() {
     DoCreateYAxis(chart1, chart1.ChartAreas[0], chart1.Series[2], 12, 8);
     DoCreateYAxis(chart1, chart1.ChartAreas[0], chart1.Series[3], 12, 8);
     }
    
     public void DoCreateYAxis(Chart chart, ChartArea area, Series series, float axisOffset, float labelsSize) {
     CreateNewChartAreaForOriginalSeries(chart, area, series);
     ChartArea areaAxis = CreateNewChartAreaForAxis(chart, series);
     CreateHiddenCopyOfOriginalSeries(chart, series, areaAxis);
     AdjustChartAreaPosition(area, series, axisOffset, labelsSize, areaAxis);
     }
    
     private static void AdjustChartAreaPosition(ChartArea area, Series series, float axisOffset, float labelsSize, ChartArea areaAxis) {
     if (series.YAxisType == AxisType.Primary) {
     areaAxis.AxisY.MajorGrid.Enabled = false;
     areaAxis.AxisY.IsStartedFromZero = area.AxisY.IsStartedFromZero;
     areaAxis.AxisY.LabelStyle.Font = area.AxisY.LabelStyle.Font;
    
     areaAxis.Position.X -= axisOffset;
     areaAxis.InnerPlotPosition.X += labelsSize;
     }
     else {
     areaAxis.AxisY2.MajorGrid.Enabled = false;
     areaAxis.AxisY2.IsStartedFromZero = area.AxisY2.IsStartedFromZero;
     areaAxis.AxisY2.LabelStyle.Font = area.AxisY2.LabelStyle.Font;
    
     areaAxis.Position.X += axisOffset;
     areaAxis.InnerPlotPosition.X -= labelsSize;
     }
     }
    
     private static void DisableGridLinesAndTickmarks(ChartArea areaAxis) {
     areaAxis.AxisX.LineWidth = 0;
     areaAxis.AxisX.MajorGrid.Enabled = false;
     areaAxis.AxisX.MajorTickMark.Enabled = false;
     areaAxis.AxisX.LabelStyle.Enabled = false;
     }
    
     private static void CreateHiddenCopyOfOriginalSeries(Chart chart, Series series, ChartArea areaAxis) {
     Series seriesCopy = chart.Series.Add(series.Name + "_Copy");
     seriesCopy.ChartType = series.ChartType;
    
     seriesCopy.YAxisType = series.YAxisType;
    
     foreach (DataPoint point in series.Points)
     seriesCopy.Points.AddXY(point.XValue, point.YValues[0]);
    
     HideCopiedSeries(areaAxis, seriesCopy);
     DisableGridLinesAndTickmarks(areaAxis); 
     }
    
     private static void HideCopiedSeries(ChartArea areaAxis, Series seriesCopy)
     {
     seriesCopy.IsVisibleInLegend = false;
     seriesCopy.Color = Color.Transparent;
     seriesCopy.BorderColor = Color.Transparent;
     seriesCopy.ChartArea = areaAxis.Name;
     }
    
     private static ChartArea CreateNewChartAreaForAxis(Chart chart, Series series)
     {
     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());
     return areaAxis;
     }
    
     private static void CreateNewChartAreaForOriginalSeries(Chart chart, ChartArea area, Series 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;
    
     if (series.YAxisType == AxisType.Primary)
     {
     areaSeries.AxisY.MajorGrid.Enabled = false;
     areaSeries.AxisY.MajorTickMark.Enabled = false;
     areaSeries.AxisY.LabelStyle.Enabled = false;
     areaSeries.AxisY.IsStartedFromZero = area.AxisY.IsStartedFromZero;
     }
     else
     {
     areaSeries.AxisY2.MajorGrid.Enabled = false;
     areaSeries.AxisY2.MajorTickMark.Enabled = false;
     areaSeries.AxisY2.LabelStyle.Enabled = false;
     areaSeries.AxisY2.IsStartedFromZero = area.AxisY2.IsStartedFromZero;
     }
    
     series.ChartArea = areaSeries.Name;
     }
    
     private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
     {
     if (showCharAreaRectangle)
     {
     RectangleF absoluteChartRectangle = e.ChartGraphics.GetAbsoluteRectangle(chart1.ChartAreas[0].Position.ToRectangleF());
     e.ChartGraphics.Graphics.DrawRectangle(Pens.Red, absoluteChartRectangle.X, absoluteChartRectangle.Y, absoluteChartRectangle.Width, absoluteChartRectangle.Height);
     }
    
     if (showInnerPlotRectangle)
     {
     RectangleF absoluteInnerPlotRectangle = e.ChartGraphics.GetAbsoluteRectangle(chart1.ChartAreas[0].InnerPlotPosition.ToRectangleF());
     e.ChartGraphics.Graphics.DrawRectangle(Pens.Blue, absoluteInnerPlotRectangle.X, absoluteInnerPlotRectangle.Y, absoluteInnerPlotRectangle.Width, absoluteInnerPlotRectangle.Height);
     }
     }
     }
    
    }

    Gruß
    Marcel

    Donnerstag, 11. November 2010 15:33
    Moderator

Alle Antworten

  • Hallo SP.,

    Zunächst kann man ja über AxisY2 nebst Enabled setzen die zweite Y-Achse aktivieren.
    Die 2 anderen könnte man "zum Beispiel" als zweite ChartArea (mit Add) darunter platzieren. Etwa so:

    [Align Multiple Chart Areas « Better Dashboards]
    http://betterdashboards.wordpress.com/2009/02/11/align-multiple-chart-areas/

    Hier weitere Beispiele:

    [Samples Environment for Microsoft Chart Controls - Home]
    http://code.msdn.microsoft.com/mschart

    Will man zwei links und zwei rechts positionieren, könnte man über die InnerPlotPosition die ChartArea auch woanders platzieren und ggf. deren StripLines nicht anzeigen.


    ciao Frank
    • Als Antwort markiert SPDeveloperXP Donnerstag, 11. November 2010 12:06
    Mittwoch, 10. November 2010 08:40
  • Hallo Frank,

    danke für Deine Hilfe!

    Verstehe ich das richtig, dass man dann mit der InnerPlotPosition und ggf. transparentem Hintergrund im zweiten Chart die beiden Charts übereinander legen muss, wenn man etwas in der Art hier haben will?

    http://imagebin.ca/view/XNgi5jK.html

    Donnerstag, 11. November 2010 07:41
  • Hallo SP.,

    ja, Du verstehst mich richtig, leider "kenne" ich da keine bessere Möglichkeit bei 4 so (wie bei Dir) platzierten Achsen.

    Siehe Referenz-Doku zur [Axis Klasse] :

    • "Hinweis   Es kann für jedes ChartArea-Objekt nur einen Satz von Achsen geben – zwei vertikale und zwei horizontale."

    Insofern meine "Folgerung": mehrere ChartArea's sind nötig.


    ciao Frank
    • Als Antwort markiert SPDeveloperXP Donnerstag, 11. November 2010 12:06
    Donnerstag, 11. November 2010 10:35
  • Okay, dann tüftel ich da mal noch herum ;-)

    Danke Dir!

    Donnerstag, 11. November 2010 12:06
  • Hallo SP.,

    Ein Beispiel für 3 Y-Achsen findest Du in den downloadbaren WinFormChartSamples:
    (s. Contents > Chart Features > Axes > Multiple Y Axes)
    http://code.msdn.microsoft.com/mschart/Release/ProjectReleases.aspx?ReleaseId=1591

    Hier eine etwas erweiterte Implementation für die gewünschten 4 Achsen (2:2), didaktisch aufbereitet:

    using System;
    using System.Drawing;
    using System.Windows.Forms;
    using System.Windows.Forms.DataVisualization.Charting;
    
    namespace ChartMultipleAxis
    {
     public partial class Form1 : Form
     {
     private const int MAX_POINTS = 13;
     private bool showCharAreaRectangle = true;
     private bool showInnerPlotRectangle = true;
    
     public Form1()
     {
     InitializeComponent();
     }
    
     private void Form1_Load(object sender, EventArgs e) {
     PopulateSeries();
     CreateAllYAxes();
     }
    
     private void PopulateSeries() {
     GenerateRandomSeries();
     }
    
    private void GenerateRandomSeries() {
      Random random = new Random(DateTime.Now.Millisecond);
    
      chart1.Series.Clear();
    
      for (int seriesIndex = 0; seriesIndex < 4; seriesIndex++)
      {
        Series series = new Series { ChartType = SeriesChartType.Line, BorderWidth = 2 };
        chart1.Series.Add(series);
        if (seriesIndex % 2 != 0) series.YAxisType = AxisType.Secondary;
      }
    
    
      for (int pointIndex = 0; pointIndex < MAX_POINTS; pointIndex++) {
        DateTime dateX = DateTime.Now.Date.AddDays(pointIndex);
    
        for (int seriesIndex = 0; seriesIndex < chart1.Series.Count; seriesIndex++) {
          chart1.Series[seriesIndex].Points.AddXY(dateX, random.Next(1, (seriesIndex + 1) * 1000));
        }
      }
    }
    
     private void CreateAllYAxes() {
     HideUnnededChartElements();
     CreateCustomChartAreaPositions();
     CreateAdditionalYAxes();
     }
    
     private void HideUnnededChartElements() {
     chart1.Legends[0].Enabled = false;
     chart1.ChartAreas[0].AxisY.MajorGrid.Enabled = false;
     chart1.ChartAreas[0].AxisY2.MajorGrid.Enabled = false;
     chart1.ChartAreas[0].AxisX.MajorGrid.Enabled = false;
     }
    
     private void CreateCustomChartAreaPositions() {
     // Note: All positions are relative 
     chart1.ChartAreas[0].Position = new ElementPosition(20, 20, 60, 60); 
     chart1.ChartAreas[0].InnerPlotPosition = new ElementPosition(10, 10, 80, 80);
     }
    
     private void CreateAdditionalYAxes() {
     DoCreateYAxis(chart1, chart1.ChartAreas[0], chart1.Series[2], 12, 8);
     DoCreateYAxis(chart1, chart1.ChartAreas[0], chart1.Series[3], 12, 8);
     }
    
     public void DoCreateYAxis(Chart chart, ChartArea area, Series series, float axisOffset, float labelsSize) {
     CreateNewChartAreaForOriginalSeries(chart, area, series);
     ChartArea areaAxis = CreateNewChartAreaForAxis(chart, series);
     CreateHiddenCopyOfOriginalSeries(chart, series, areaAxis);
     AdjustChartAreaPosition(area, series, axisOffset, labelsSize, areaAxis);
     }
    
     private static void AdjustChartAreaPosition(ChartArea area, Series series, float axisOffset, float labelsSize, ChartArea areaAxis) {
     if (series.YAxisType == AxisType.Primary) {
     areaAxis.AxisY.MajorGrid.Enabled = false;
     areaAxis.AxisY.IsStartedFromZero = area.AxisY.IsStartedFromZero;
     areaAxis.AxisY.LabelStyle.Font = area.AxisY.LabelStyle.Font;
    
     areaAxis.Position.X -= axisOffset;
     areaAxis.InnerPlotPosition.X += labelsSize;
     }
     else {
     areaAxis.AxisY2.MajorGrid.Enabled = false;
     areaAxis.AxisY2.IsStartedFromZero = area.AxisY2.IsStartedFromZero;
     areaAxis.AxisY2.LabelStyle.Font = area.AxisY2.LabelStyle.Font;
    
     areaAxis.Position.X += axisOffset;
     areaAxis.InnerPlotPosition.X -= labelsSize;
     }
     }
    
     private static void DisableGridLinesAndTickmarks(ChartArea areaAxis) {
     areaAxis.AxisX.LineWidth = 0;
     areaAxis.AxisX.MajorGrid.Enabled = false;
     areaAxis.AxisX.MajorTickMark.Enabled = false;
     areaAxis.AxisX.LabelStyle.Enabled = false;
     }
    
     private static void CreateHiddenCopyOfOriginalSeries(Chart chart, Series series, ChartArea areaAxis) {
     Series seriesCopy = chart.Series.Add(series.Name + "_Copy");
     seriesCopy.ChartType = series.ChartType;
    
     seriesCopy.YAxisType = series.YAxisType;
    
     foreach (DataPoint point in series.Points)
     seriesCopy.Points.AddXY(point.XValue, point.YValues[0]);
    
     HideCopiedSeries(areaAxis, seriesCopy);
     DisableGridLinesAndTickmarks(areaAxis); 
     }
    
     private static void HideCopiedSeries(ChartArea areaAxis, Series seriesCopy)
     {
     seriesCopy.IsVisibleInLegend = false;
     seriesCopy.Color = Color.Transparent;
     seriesCopy.BorderColor = Color.Transparent;
     seriesCopy.ChartArea = areaAxis.Name;
     }
    
     private static ChartArea CreateNewChartAreaForAxis(Chart chart, Series series)
     {
     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());
     return areaAxis;
     }
    
     private static void CreateNewChartAreaForOriginalSeries(Chart chart, ChartArea area, Series 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;
    
     if (series.YAxisType == AxisType.Primary)
     {
     areaSeries.AxisY.MajorGrid.Enabled = false;
     areaSeries.AxisY.MajorTickMark.Enabled = false;
     areaSeries.AxisY.LabelStyle.Enabled = false;
     areaSeries.AxisY.IsStartedFromZero = area.AxisY.IsStartedFromZero;
     }
     else
     {
     areaSeries.AxisY2.MajorGrid.Enabled = false;
     areaSeries.AxisY2.MajorTickMark.Enabled = false;
     areaSeries.AxisY2.LabelStyle.Enabled = false;
     areaSeries.AxisY2.IsStartedFromZero = area.AxisY2.IsStartedFromZero;
     }
    
     series.ChartArea = areaSeries.Name;
     }
    
     private void chart1_PostPaint(object sender, ChartPaintEventArgs e)
     {
     if (showCharAreaRectangle)
     {
     RectangleF absoluteChartRectangle = e.ChartGraphics.GetAbsoluteRectangle(chart1.ChartAreas[0].Position.ToRectangleF());
     e.ChartGraphics.Graphics.DrawRectangle(Pens.Red, absoluteChartRectangle.X, absoluteChartRectangle.Y, absoluteChartRectangle.Width, absoluteChartRectangle.Height);
     }
    
     if (showInnerPlotRectangle)
     {
     RectangleF absoluteInnerPlotRectangle = e.ChartGraphics.GetAbsoluteRectangle(chart1.ChartAreas[0].InnerPlotPosition.ToRectangleF());
     e.ChartGraphics.Graphics.DrawRectangle(Pens.Blue, absoluteInnerPlotRectangle.X, absoluteInnerPlotRectangle.Y, absoluteInnerPlotRectangle.Width, absoluteInnerPlotRectangle.Height);
     }
     }
     }
    
    }

    Gruß
    Marcel

    Donnerstag, 11. November 2010 15:33
    Moderator
  • Hallo Marcel,

    das sieht super aus!

    Ich entdecke hier viele wichtige Elemente, die ich bei meinen Versuchen, nicht gemacht hatte, die aber zur richtigen Darstellung sehr wichtig sind.

    Freitag, 12. November 2010 08:21
  • Hallo Marcel,

    habe mir das Beispiel gerade noch einmal angeschaut und eine Legende zum Chart hinzugefügt. Lustigerweise werden in der Legende die einzelnen Serien nicht gruppiert.

    Wenn ich MAX_POINTS = 1 setze, bekomme ich 4 verschiedene Farben in der Legende. Bei MAX_POINTS = 2 bekomme ich 8 und so weiter. Also immer 4*MAX_POINTS.

    Hab dann testweise bei den einzelnen Series die Legende gesetzt, z.B.

    chart1[serie].Legend = "Legend1"

    Leider hat das nichts gebracht. Bei der Analyse des Codes ist mir auch nicht bewusst geworden, warum überhaupt so viele Punkte in der Legende auftauchen. Gibt es dafür eine Erklärung?

    Freitag, 12. November 2010 09:57
  • Hallo SP,

    Danke für die Rückmeldung. Du hast Recht. Ich hatte kurz bevor ich den Code gepostet habe, noch Änderungen an GenerateRandomSeries() gemacht. Da hab ich wohl in der Eile ein Bug reingepflanzt, der dazu führte, dass insgesamt 52 statt 4 Series generiert wurden (ich habe den Code korrigiert). Im Grunde genommen, kannst Du GenerateRandomSeries() rausnehmen und durch Deine eigene Routine ersetzen, welche die realen Serien zur Verfügung stellt.

    Gruß
    Marcel

    Freitag, 12. November 2010 10:21
    Moderator
  • Ah jetzt ja. Jetzt passt es! Danke für den Hinweis!
    Freitag, 12. November 2010 12:47