Laden...

Chart1_click get series?

Erstellt von jodahush vor 2 Jahren Letzter Beitrag vor 2 Jahren 561 Views
J
jodahush Themenstarter:in
23 Beiträge seit 2021
vor 2 Jahren
Chart1_click get series?

Ich nutze einen PieChart in c#
Nun möchte ich bei DoppelClick auf ein Pie eine Referenz auf die Serie erhalten.

Ich habe folgenden Code gefunden:


        private void Chart1_Click(object sender, EventArgs e)
        {

            string[] pointData = e.PostBackValue.Split(',');
            // Add click event code here

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            foreach (DataPoint dp in this.Chart1.Series["YourSeriesName"].Points)
            {
                dp.PostBackValue = "#VALX,#VALY";
            }
        }

Leider klappt es nicht, weil DatePoint wohl nicht verfügbar ist.

Dann wollte ich System.Web.UI. zur using Liste hinzufügen, aber es ist nicht vorhanden.
Dann über Projekt / Verweis hinzufügen / Com : System.web; oder System.UI gesucht um ihn zuzufügen, aber auch dort nicht gefunden.

Wie macht man es denn richtig?

4.931 Beiträge seit 2008
vor 2 Jahren

Hallo,

du vermischst anscheinend verschiedene Komponenten für verschiedene Projekt-Typen (WinForms, Web UI).

Ich nehme mal an, du hast die Windows Forms Komponente Chart?
Die zugehörige DataPoint-Klasse hat keine Eigenschaft namens PostBackValue.

Es gibt auch keine eingebaute Methode zum Ermitteln der Serie anhand der (x,y)-Position.

Du kannst aber die HitTest-Methode benutzen, um das angeklickte Diagramm-Element anhand der (x,y)-Position zu ermitteln. Beachte dazu die Rückgabe: HitTestResult.ChartElementType.

Dann müßtest du je nach ChartElementType (DataPoint, PlottingArea, ..) weitere Berechnungen durchführen. Das erfordert dann aber trigonometrische Berechnungen (Radialsystem)...

J
jodahush Themenstarter:in
23 Beiträge seit 2021
vor 2 Jahren

das ist ja echt tricky.
Gerade experiementiere ich mit HitTest und versuche das:


 public static DataPoint GetPointAtMouse(Chart c, MouseEventArgs e)
 {
     var result = c.HitTest(e.X, e.Y);
     // If the mouse if over a data point
     if (result.ChartElementType == ChartElementType.DataPoint)
     {
         // Find selected data point
         var point = c.Series[0].Points[result.PointIndex];
         return point;
     }
     return null;
 }

Dazu brauche ich wohl :
using System.Windows.Forms.DataVisualization.Charting;

Sobald ich diese using direktive dazu setze, funktioniert Cursor.Current = Cursors.WaitCursor; nicht mehr. Wie kann denn sowas sein?

4.931 Beiträge seit 2008
vor 2 Jahren

Das liegt daran, daß in DataVisualization.Charting selber wieder eine Cursor-Klasse definiert ist. Du mußt also entweder jeweils den vollen Namensbereich beim Cursor angeben oder aber alternativ einen Aliasnamen vergeben, z.B.


using WinFormsCursor = System.Windows.Forms.Cursor;

// Verwendung
WinFormsCursor.Current = Cursors.WaitCursor;

Ich hatte aber übersehen, daß die HitResult-Klasse ja sowohl Series als auch PointIndex zurückgibt (man also nicht selber die Umrechnung durchführen muß). Du solltest daher statt Series[0] dies so verwenden:


var point = result.Series.Points[result.PointIndex];

J
jodahush Themenstarter:in
23 Beiträge seit 2021
vor 2 Jahren

Vielen Dank.
Das ist schon sehr verzwickt.
Zuerst noch zum Cursor.
Warum macht MS die gleiche Klasse in verschiedene Module? Aber sei es drum.
Ich konnte deinen Code nutzen, aber der Cursor veränderte sich nicht.


using System;
using System.Collections.Generic;
using System.IO;
using System.Windows.Forms;
using WinFormsCursor = System.Windows.Forms.Cursor;  //ich nehme an hier ist das using richtig
//using System.Windows.Media;
using System.Windows.Forms.DataVisualization.Charting;

//using System.Runtime.InteropServices;


namespace DirSize
{
    public partial class Form1 : Form
    {
        private static readonly HistoList histoList = new HistoList();
        private readonly HistoList Hi = histoList;
        private string myLastRoot;
        // private ListViewEx ListView1;

        public Form1()
        {
            InitializeComponent();


            LastRoot = "C:\\" + Path.DirectorySeparatorChar;
            textBox1.Text = Path.GetFullPath(LastRoot);
            Hi.AddToHist(LastRoot);//initial dir
        }
        private void ScanDirs(string dr)
        {
            
            {
                //Hier sollen die Directories u. Größe des Stammverzeichnisses rein

                var DirNames = new List<string>();
                var DirSz = new List<long>();
                // Set cursor as hourglass
                //Das liegt daran, daß in DataVisualization.Charting selber wieder eine Cursor-Klasse definiert ist.
                ////Du mußt also entweder jeweils den vollen Namensbereich beim Cursor angeben oder aber alternativ einen Aliasnamen vergeben, z.B.
                WinFormsCursor.Current = Cursors.WaitCursor; //beides compiliert funktioniert, aber der Cursor hat sich nicht verändert. Blieb auf Pfeil
                System.Windows.Forms.Cursor.Current = Cursors.WaitCursor; //zweite Methode
                //Cursor.Current = Cursors.WaitCursor;
                //if start scan, textbox entree will be the next root
                //History Liste
                if (Directory.Exists(dr))

16.806 Beiträge seit 2008
vor 2 Jahren

Warum macht MS die gleiche Klasse in verschiedene Module? Aber sei es drum.

Das hat nichts mit Microsoft zutun, sondern wie Software Architektur funktioniert.
Und hier kann es in einer modularen Denkweise durchaus valide sein, dass man entsprechende Dinge modelliert.

Dass dabei die Namen manchmal gleich sind: kommt vor.
Aber genau deswegen gibt es Namespaces, zur unabhängigen Architektur-Möglichkeit.

Daher sollte jeder Entwickler auch die Namespaces seiner Sprache (C#, Java..) im Griff haben.
Organizing types in namespaces

4.931 Beiträge seit 2008
vor 2 Jahren

Das Ändern eines Cursors ist (oftmals) nicht ganz so einfach (je nachdem, wie dein Code danach aussieht). Bei einer blockierenden Operation (s.a. [FAQ] Warum blockiert mein GUI?) wird keine Nachrichtenschleife mehr ausgeführt und der Cursor ändert sich nicht.
Schau mal in Wie kann ich den Cursor zum Wait-Cursor drehen lassen? (bzgl. dem Vorschlag mittels Application.DoEvents s. aber auch Warum DoEvents Mist ist!).
Probiere aber mal UseWaitCursor = true (auch wenn ich selber damit auch einige Probleme - je nach Code - hatte).

J
jodahush Themenstarter:in
23 Beiträge seit 2021
vor 2 Jahren

Ok, ich habe folgendes versucht:
Application.UseWaitCursor = true;
System.Windows.Forms.Cursor.Current = Cursors.WaitCursor;
und vice versa

Das ganze auf einer einfachen Form geklebt, mit zwei Buttons, funktioniert.
In der App habe ich es noch nicht hinbekommen.
Ich dachte mit
Application.UseWaitCursor = true;
sagt man der App - sie darf den Wait Cursor nutzen den ich dann mit Current ... ein und ausschalte.

Aber wenn ich Application.UseWaitCursor = true; in die Form Load setze sieht man die ganze zeit den Ladecursor.
In der Methode klappt es - nur manchmal! Komisch.
Ich probiere noch weiter rum.

J
jodahush Themenstarter:in
23 Beiträge seit 2021
vor 2 Jahren

Ich habe mich mit dem Chart rumgeschlagen und dachte ich hätte die Lösung. (Zumindest für den Chart, nicht für den Wait Cursor)
Tatsächlich klappte sie in einem Projekt mit Balkenchart.
siehe hier:


        private void chart1_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            int clkdGraphPoint;
            string clkdGraphSeries = null;

            System.Windows.Forms.DataVisualization.Charting.HitTestResult result = chart1.HitTest(e.X, e.Y);
            try
            {

                clkdGraphPoint = result.PointIndex;
                if (clkdGraphPoint > 0)
                {
                    clkdGraphSeries = result.Series.Name;
                    StatusLabel1.Text = (clkdGraphSeries);
                }
                else
                {
                    StatusLabel1.Text = ("nix da!");
                }

            }
            catch (Exception)
            {
                
                StatusLabel1.Text=( "No valid area on chart");
                //throw;
            }
        }

nun habe ich hier aber ein Pie Chart und das ganze funzt nicht mehr

Vielleicht liegt es dran, wie ich das Chart gefüllt habe - selbst wenn ich ein Pie wähle wird es wieder ein BarChart


     public void BarExample()
        {
            this.chart1.Series.Clear();

            // Data arrays
            string[] seriesArray = { "Cat", "Dog", "Bird", "Monkey" };
            int[] pointsArray = { 2, 1, 7, 5 };

            // Set palette
            this.chart1.Palette = ChartColorPalette.EarthTones;

            // Set title
            this.chart1.Titles.Add("Animals");

            // Add series.
            for (int i = 0; i < seriesArray.Length; i++)
            {
                Series series = this.chart1.Series.Add(seriesArray[i]);
                series.Points.Add(pointsArray[i]);
            }
        }
        private void Form1_Load(object sender, EventArgs e)
        {
            BarExample();
        }

Möglicherweise muss ich die Chartelemente anders in das Chart reinschreiben. Ich habe es so gemacht:


  chart1.Series["DirSizes"].Points.AddXY(DirNames[i], DirSz[i]);
                    }

J
jodahush Themenstarter:in
23 Beiträge seit 2021
vor 2 Jahren

Danke schön.
Tja, die Northwind database hätte ich auch gerne genutzt/das Bsp. nachgebaut. Scheint aber auch nicht zu gehen. Man kann jetzt nur die Access DB nutzen, oder?

Gut, zur Frage: Wie kann ich das BarChart als Pie darstellen.

Als Balken Chart klappt das ganze, auch soweit, dass ich interaktiv auf die Balken klicken kann und das Label sehe. Allerdings scheitere ich beim Pie (unabhängig davon sehe ich die Darstellung des Pie mittlerweile eher kritisch, weil die Anzahl der Items oft zu hoch ist. Trotzdem würde ich es gerne verstehen.).


                        Series series = this.chart1.Series.Add(DirNames[i]);
                        chart1.Series[i].ChartType = SeriesChartType.Pie;
                        series.Points.Add(DirSz[i]);

Bei diesem Beispiel führt die zweite Zeile dazu dass ich nur ein Collection Element sehe (also einen Vollkreis, kein PieChart). Es sollten aber mehrere sein. Wie gesagt, als Balkenchart klappt das ganze (ohne Zeile 2)

Die Series_ verstehe ich nicht. Series Serie ist eine neue Serie, zu der ich Elemente hinzufüge. Ich kann die series aber nicht als Pie definieren, sondern nur die addierten series** Elemente.
Nach meiner Denke würde man mit chart1.Series auch den Typ bestimmen können. Ist aber nicht möglich. Man kann nur die Collection hinzufügen.
Der Collection (Series_) kann man einen Typ verpassen - was für mich unlogisch erscheint, da ich am Ende ein Chart erzeugen könnte bei dem jede Collection ein eigener Typ ist, wobei das zu einem Laufzeit Fehler führt.

4.931 Beiträge seit 2008
vor 2 Jahren

Mit series.Points.Add(DirSz[__i]) hast du ja auch nur genau 1 Punkt zu der Serie hinzugefügt - und dann wird bei einem Pie-Chart ein Vollkreis (100%) gezeichnet.

Bei mehreren Serien werden diese übereinander gezeichnet (so daß man bei nur Pie-Serien nur die oberste sieht).

PS: Du solltest deinen Code dann eher so schreiben:


Series series = this.chart1.Series.Add(DirNames[i]);
series.ChartType = SeriesChartType.Pie;
// Punkte hinzufügen ...

(damit sich deine Zuweisung auch garantiert auf die zuletzt hinzugefügte Serie bezieht).

PPS: Ich finde die Namenswahl Series für die Klasse aber etwas unglücklich, da die Eigenschaft Series beim Chart sich dann aber auf die gesamte Collection bezieht (weil "Series" im englischen Ein- und Mehrzahl ist).

J
jodahush Themenstarter:in
23 Beiträge seit 2021
vor 2 Jahren

Danke.
Der Name ist blöd, hab ich bei vielen CopyPaste Aktionen irgendwo hergenommen. Da kommt jetzt was anderes hin.

Also so:
name der serie
Series MySeries = this.chart1.Series.Add(textBox1.Text);
MySeries.ChartType= SeriesChartType.Pie;
in der Schleife
//FillChart
//Loop through DirNames and DirSizes
MySeries.Points.Add(DirSz_);
MySeries.Name = DirNames_;
**
Ergebnis schon besser, allerdings zeigt die Legende nur points an**
Entsprechend klappt auch der MausClick nicht mehr richtig.
Scheint das mit dem name nicht richtig zu sein: MySeries.Name = DirNames_;

4.931 Beiträge seit 2008
vor 2 Jahren

Setze mal den LegendText für jeden DataPoint.

PS: Ich meinte die Benennung von Microsoft aus, nicht deine (weil man da einfach durcheinander kommt, wenn etwas Series heißt und man nicht sofort erkennt, ob Ein- oder Mehrzahl gemeint ist).

J
jodahush Themenstarter:in
23 Beiträge seit 2021
vor 2 Jahren

Kaum zu Glauben, ich habe es hinbekommen.
Vielen Dank für deine Unterstützung.

Hier meine Lösung
So fülle ich den Chart:


            //DS is the total Dirsize
            for (int i = 0; i < DirNames.Count; i++)
            {

                try
                {
                    // if (DirSz[i]>0)
                    {//MaxV
                        int PerSz = 0;
                        if (DirSz[i] > 0)
                        {
                            long PercSize = (DirSz[i] * 100 / (MaxV));
                            PerSz = Convert.ToInt32(PercSize);
                        }
                        //index name prozentwert
                        AddLVItem(i, DirNames[i], PerSz, DirSz[i] / 1024 / 1024);

                        //FillChart
                        //Loop through DirNames and DirSizes
                        MySeries.Points.Add(DirSz[i]);
                        MySeries.Points[i].LegendText = DirNames[i];

                    }
                }
                catch (Exception e)
                {
                   
                    MessageBox.Show(e.ToString());                        //throw;
                }
            }

Und so zeige ich die Pie Legendentext an wenn ich auf das Chart klicke.


 private void chart1_MouseClick(object sender, MouseEventArgs e)
        {
            int clkdGraphPoint;
            string clkdGraphSeries = null;


            System.Windows.Forms.DataVisualization.Charting.HitTestResult result = chart1.HitTest(e.X, e.Y);
            try
            {

                clkdGraphPoint = result.PointIndex;
                if (clkdGraphPoint >= 0)
                {
                    clkdGraphSeries = result.Series.Points[clkdGraphPoint].LegendText;
                    StatusLabel1.Text = (clkdGraphSeries);
                    textBox1.Text = Path.GetFullPath(LastRoot + @"\" + clkdGraphSeries);
                }
                else
                {
                    StatusLabel1.Text = ("nix da!");
                }

            }
            catch (Exception)
            {

                StatusLabel1.Text = ("No valid area on chart");
                //throw;
            }
        }