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?
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)...
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?
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];
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))
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
- performance is a feature -
Microsoft MVP - @Website - @AzureStuttgart - github.com/BenjaminAbt - Sustainable Code
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).
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.
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]);
}
Schau mal in den Code von Create Pie Chart in Windows Forms Application using C# and VB.Net.
Hast du evtl. mehr als eine Serie dem Pie-Chart zugefügt?
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.
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).
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_;
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).
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;
}
}