typisiertes Dataset an Chart anbinden - mehrere Kurven anzeigen lassen

  • VB.NET

Es gibt 44 Antworten in diesem Thema. Der letzte Beitrag () ist von egon.

    typisiertes Dataset an Chart anbinden - mehrere Kurven anzeigen lassen

    Wie kann man sich mehrere Kurven in einem Chart anzeigen lassen, wenn sich die Daten in einem typisierten Dataset befinden?
    Ich habe zwei Datensätzen mit jeweils einem X-Wert und zwei zugehörigen Y-Werten. So ergeben sich vier Kurven die im Diagramm dargestellt werden sollen.
    Mit Chart1.DataBind() kann ich je nach ausgewähltem Datensatz immer nur die zwei zugehörigen Kurven X&Y1 und X&Y2 darstellen.
    Da muss es doch einen Trick geben, wie man sich alle vier Kurven gleichzeitig anzeigen lassen kann.
    Das Projekt habe ich angehängt

    Hier der Code:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Private Sub Form1_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
    3. DataSet1.Clear()
    4. DataSet1.Messreihe.AddMessreiheRow("Datensatz1")
    5. DataSet1.Messreihe.AddMessreiheRow("Datensatz2")
    6. DataSet1.Messwert.AddMesswertRow(2, 3, 5, DataSet1.Messreihe(0))
    7. DataSet1.Messwert.AddMesswertRow(3, 3.1, 4, DataSet1.Messreihe(0))
    8. DataSet1.Messwert.AddMesswertRow(4, 3.0, 2, DataSet1.Messreihe(0))
    9. DataSet1.Messwert.AddMesswertRow(5, 2.3, 1, DataSet1.Messreihe(0))
    10. DataSet1.Messwert.AddMesswertRow(6, 1.3, 4, DataSet1.Messreihe(0))
    11. DataSet1.Messwert.AddMesswertRow(0, 8, 2, DataSet1.Messreihe(1))
    12. DataSet1.Messwert.AddMesswertRow(2, 3, 3, DataSet1.Messreihe(1))
    13. DataSet1.Messwert.AddMesswertRow(4, 5, 2, DataSet1.Messreihe(1))
    14. DataSet1.Messwert.AddMesswertRow(6, 6, 5, DataSet1.Messreihe(1))
    15. DataSet1.Messwert.AddMesswertRow(8, 2, 6, DataSet1.Messreihe(1))
    16. DataSet1.Messwert.AddMesswertRow(10, 1, 9, DataSet1.Messreihe(1))
    17. End Sub
    18. Private Sub Button6_Click(sender As Object, e As EventArgs) Handles Button_Databinding.Click
    19. 'Geht leider nicht
    20. 'Chart1.Series(0).Points.DataBindXY(_DataSet1.Messreihe(0).GetMesswertRows().X_Wert, _DataSet1.Messreihe(0).GetMesswertRows().Y1_Wert)
    21. ' Geht leider auch nicht
    22. ' Chart1.Series(0).DataBind
    23. ' So kann ich aber nicht die Daten aus mehreren Serien gleichzeitig anzeigen lassen
    24. Chart1.DataBind()
    25. End Sub
    26. End Class
    Bilder
    • Databinding2.png

      3,83 kB, 539×199, 189 mal angesehen
    • Databinding.png

      67,27 kB, 982×854, 190 mal angesehen
    Dateien
    • Databinding.zip

      (309,41 kB, 175 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „egon“ ()

    Da Du m.E. nicht gleichzeitig ein Objekt (Dein Chart) an mehrere Quellen (nämlich mehrere DataRows DataTable-Teile (einmal die Messpunkte für die 1. Messreihe und einmal für die 2.)) gleichzeitig binden kannst, bleibt Dir wohl nur sowas:

    VB.NET-Quellcode

    1. Dim IndexDerAktuellenLinie = 0
    2. For Each Reihe In DataSet1.Messreihe
    3. For Each Punkt In Reihe.GetMesswertRows
    4. Chart1.Series(IndexDerAktuellenLinie).Points.AddXY(Punkt.X_Wert, Punkt.Y1_Wert)
    5. Chart1.Series(IndexDerAktuellenLinie + 1).Points.AddXY(Punkt.X_Wert, Punkt.Y2_Wert)
    6. Next
    7. IndexDerAktuellenLinie += 2
    8. Next

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „VaporiZed“ ()

    Hmmm. Irgendwie ist das noch unschön. Solche Schleifen müsste ich für jede Kurve durchführen, da alle Datensätze eine unterschiedliche Anzahl an X-Werten haben.
    Ich frage mich, ob es vielleicht noch einen eleganteren Weg gibt. In meiner späteren Anwendung sollen bis zu 20 Kurven dargestellt werden können.
    Kann man vielleicht das Problem mit Chart1.Series(i).Points.DataBindXY( lösen? In einem anderen Programm hatte ich jede Kurve so angebunden: Chart1.Series(i).Points.DataBindXY(_CurveDatas(i).XValues, _CurveDatas(i).YValues) Hierbei war _CurveDatas eine andere Klasse und keine Dataset.
    Mit DataBindXY ist man beim Databind flexibler. Ich habe nur keine Ahnung, wie man aus dem Dataset die x-Werte oder die Y-Werte von einer bestimmten Kurve ausliest (außer mit For Each) und dann mit DataBindXY arbeitet.

    [EDIT] Ich bin mir nun nicht mehr so sicher, ob meine "Kritik" gerechtfertigt ist. Dein Vorschlag funktioniert sehr gut, wenn man für jede Kurve Chart1.Series(0).Points.Clear() vor deiner Routine einfügt.
    Eine Frage bleibt aber noch offen. Kann man sich ohne For Each die x-Werte oder die Y-Werte aus einem bestimmten Datensatz auslesen, z.B. als List of Double?

    Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „egon“ ()

    Hab ich grad auch probiert. Ob es schöner ist, musst Du entscheiden:

    VB.NET-Quellcode

    1. Chart1.Series(0).Points.DataBindXY(DataSet1.Messreihe(0).GetMesswertRows.Select(Function(x) x.X_Wert).ToList, DataSet1.Messreihe(0).GetMesswertRows.Select(Function(y) y.Y1_Wert).ToList)
    2. Chart1.Series(1).Points.DataBindXY(DataSet1.Messreihe(0).GetMesswertRows.Select(Function(x) x.X_Wert).ToList, DataSet1.Messreihe(0).GetMesswertRows.Select(Function(x) x.Y2_Wert).ToList)
    3. Chart1.Series(2).Points.DataBindXY(DataSet1.Messreihe(1).GetMesswertRows.Select(Function(x) x.X_Wert).ToList, DataSet1.Messreihe(1).GetMesswertRows.Select(Function(x) x.Y1_Wert).ToList)
    4. Chart1.Series(3).Points.DataBindXY(DataSet1.Messreihe(1).GetMesswertRows.Select(Function(x) x.X_Wert).ToList, DataSet1.Messreihe(1).GetMesswertRows.Select(Function(x) x.Y2_Wert).ToList)

    dazu eine Anmerkung: Obwohl die Funktion DataBindXY() IEnumerables haben will, geht es ohne .ToList nicht. Es kommen zwar auch IEnumerables(Of Double) raus, aber es wird dadurch eine NotImplementedException erzeugt.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @ErfinderDesRades: Ist das auch eine Lösung für folgendes, ohne jetzt noch interpolierte X-Werte einzufügen:

    egon schrieb:

    da alle Datensätze eine unterschiedliche Anzahl an X-Werten haben.

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    Das ist LINQ. Zuerst wird in der Zeile die Messreihe genommen, der mehrere Messpunkte zugeordnet sind, die man sich per .GetMesspunktRows() holt. Nun kann die DataBindXY-Funktion aber nix mit einer typisierten DataRow anfangen. Daher holt man sich von jeder Row per Select einen (Column-)Wert, der in die finale Auflistung reinkommen soll. Du machst also mit dem Select klar: "Ich will von den ganzen Messpunkten momentan nur deren X-Werte". Ich persönlich finde, dass "Select" ein etwas komischer Begriff da ist, aber AFAIK wurde es von schlauen Leuten aus dem SQL-Bereich übernommen erfunden. Übernommen wurde er später für LINQ. Zweifellos, um den Übergang für SQL-Leute leichter zu gestalten.

    EDIT: @EdR: Und schon wieder hab ich versehentlich eine Lektion kaputtgemacht. ||
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.

    egon schrieb:

    Könnt ihr mir das mit Select(Function(x) x.X_Wert) bitte erklären.
    ich stell mich da ja immer gern bisserl stur: Nö - was du da angibst, kompiliert ühaupt nicht, also kanns auch niemand erklären.
    Bitte gibt Codezeilen oder Ausdrücke an, die auch kompilieren.

    Lektion kaputtgemacht.
    schlimm, schlimm. ;)

    Auch dass ihr immer von Select() redet, dabei heisst es minimal .Select() - es ist ja eine Extension-Methode, also eine, die den Funktions-Umfang eines Objektes erweitert - nämlich des Objektes vor dem .. Deshalb ist der . so wichtig, und ohne ihn ergibt sich garkein Sinn.


    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „ErfinderDesRades“ ()

    Wenn Du unbedingt ein Array brauchst (DataBindXY braucht es nicht unbedingt, akzeptiert es aber auch), dann kannst Du die Zwischenstufe .ToList() weglassen. Ein IEnumerable(Of) verfügt auch eine Methode .ToArray()
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    ErfinderDesRades und VaporiZed vielen Dank für eure Hilfe.

    Kann ich das Dataset einfach in ein anderes Projekt kopieren und die Bindingsources mitnehmen? Reicht eine Kopie der Dateien DataSet1.Designer.vb, DataSet1.xsc, DataSet1.xsd und DataSet1.xss aus?

    Wie kann man auf das Dataset aus einer Klasse zugreifen? Soll ich für diese Frage ein eigenes Thema eröffnen oder können wir das hier so "nebenbei" abhandeln?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „egon“ ()

    Wär tatsächlich fast schon ein eigenes Thema wert. Aber probieren wir es doch mal auf die Schnelle. Wird's zuviel, kann man ggf. einen Moderator bitten, die Themen zu splitten.

    Zu den ersten Fragen: Probier's aus. Die tDS-Designer-Datei wurde bei mir doppelt erzeugt, weshalb es reicht, wenn Du die XSD-Datei rüberkopierst. Das mit den BindingSources sollte dann auch gehen. Aber dann gleich die Frage: Wozu? Wie ich in einem anderen Thread schon erwähnte: Von "all den" (paar Dutzend) bisherigen Projekten musste ich erst bei einem das tDS auslagern. Ist das der Plan? Eine DLL zu erzeugen, welche ein öffentliches tDS anbietet? Ist die Verwendung in mehreren Projekten wirklich sinnvoll für Dich?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    >>> Ist das der Plan? Eine DLL zu erzeugen, welche ein öffentliches tDS anbietet? Ist die Verwendung in mehreren Projekten wirklich sinnvoll für Dich?
    Es geht nur darum, dass ich in einem kleinen Testprogramm alles ausprobieren und dann durch das Kopieren Fehler vermeiden im Hauptprogramm vermeiden möchte.
    1. Dafür sollte man Backups anlegen.
    2. Dann reicht ja das einfache XSD-Kopieren.
    3. Ich weiß immer noch nicht was Du dann mit "Wie kann man auf das Dataset aus einer Klasse zugreifen?" meinst? Wie hast Du Dir die Situation vorgestellt? Das tDS stellt ja - einmal ins Projekt eingebunden - eine eigene Klasse dar. Aber das scheinst Du nicht zu meinen. Was genau dann?
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    An das Testprojekt habe ich eine Klasse angehängt. Den Fehler habe ich als Bild auch angehängt.
    Es geht mir darum, dass ich auch aus irgendwelchen Klassen auf das Dataset zugreifen kann, z.B. X-Werte und Y-Werte hinzufügen oder verändern.
    Wie auch immer, den Fehler kann ich nicht beseitigen.
    Ihr habt mir immer gepredigt, dass ich möglichst viel sinnvoll in irgendwelche Klassen auslagern soll. Mein Problem ist dabei, dass ich dabei immer wieder auf den Grundbestand an Daten zugreifen können muss. Wenn sich alles im Hauptprogramm abspielen muss, entsteht doch wieder Spaghetticode...
    Mache ich dabei irgendeinen groben Gedankenfehler weil ich den grundsätzlichen Umgang mit Klassen und einem Datenmodell noch nicht verstanden habe?

    Spoiler anzeigen

    VB.NET-Quellcode

    1. Public Class Form1
    2. Dim Testclass As New Testclass
    3. ...
    4. ...
    5. Public Class Testclass
    6. Dim Test2() As Double
    7. Public Sub New()
    8. Test2 = DataSet1.Messreihe(0).GetMesswertRows.Select(Function(x) x.X_Wert).ToList.ToArray
    9. For i = 0 To Test2.Count - 1
    10. Console.WriteLine(Test2(i))
    11. Next
    12. End Sub
    13. End Class



    [Nachtrag]
    Zum Databinding habe ich noch zwei Beispiele von Microsoft gefunden, welche eine mir unbekannt Überladung ausnutzt. Übertragen kann ich das Beispiel auf meinen Fall aber nicht. Vielleicht könnt ihr etwas damit anfangen.
    Kann man da nicht irgendetwas mit einer Bindingsource machen?
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Windows.Forms.DataVisualization.Charting
    2. Imports System.Data
    3. Imports System.Data.OleDb
    4. ...
    5. ' The Access database
    6. Dim fileNameString As String
    7. fileNameString = "data\chartdata.mdb"
    8. ' Initialize a connection string
    9. Dim myConnectionString As String = "PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=" + fileNameString
    10. ' Define the database query
    11. Dim mySelectQuery As String = "SELECT Name, Sales FROM REPS WHERE RegionID < 3;"
    12. ' Create a database connection object using the connection string
    13. Dim myConnection As New OleDbConnection(myConnectionString)
    14. ' Create a database command on the connection using query
    15. Dim myCommand As New OleDbCommand(mySelectQuery, myConnection)
    16. ' Open the connection
    17. myCommand.Connection.Open()
    18. ' Create a database reader
    19. Dim myReader As OleDbDataReader = myCommand.ExecuteReader(CommandBehavior.CloseConnection)
    20. ' Since the reader implements IEnumerable, pass the reader directly into
    21. ' the DataBind method with the name of the Column selected in the query
    22. Chart1.Series("Default").Points.DataBindXY(myReader, "Name", myReader, "Sales")
    23. ' Close the reader and the connection
    24. myReader.Close()
    25. myConnection.Close()

    oder
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System.Windows.Forms.DataVisualization.Charting
    2. Imports System.Data
    3. Imports System.Data.OleDb
    4. ...
    5. ' The Access database
    6. Dim fileNameString As String
    7. fileNameString = "data\chartdata.mdb"
    8. ' Initialize a connection string
    9. Dim myConnectionString As String = "PROVIDER=Microsoft.Jet.OLEDB.4.0;Data Source=" + fileNameString
    10. ' Define the database query
    11. Dim mySelectQuery As String = "SELECT * FROM REPS;"
    12. ' Create a database connection object using the connection string
    13. Dim myConnection As New OleDbConnection(myConnectionString)
    14. ' Create a database command on the connection using query
    15. Dim myCommand As New OleDbCommand(mySelectQuery, myConnection)
    16. ' Open the connection
    17. myCommand.Connection.Open()
    18. ' Initializes a new instance of the OleDbDataAdapter class
    19. Dim custDA As New OleDbDataAdapter()
    20. custDA.SelectCommand = myCommand
    21. ' Initializes a new instance of the DataSet class
    22. Dim custDS As New DataSet()
    23. ' Adds rows in the DataSet
    24. custDA.Fill(custDS, "Customers")
    25. ' Initializes a new instance of the DataView class
    26. Dim firstView As New DataView(custDS.Tables(0))
    27. ' Since the DataView implements IEnumerable, pass the reader directly into
    28. ' the DataBind method with the name of the Columns selected in the query
    29. Chart1.Series("Default").Points.DataBindXY(firstView, "Name", firstView, "Sales")
    30. ' Closes the connection to the data source. This is the preferred
    31. ' method of closing any open connection.
    32. myCommand.Connection.Close()
    33. ...

    Bilder
    • fehler.png

      5,71 kB, 811×96, 188 mal angesehen
    Dateien
    • Databinding.zip

      (310,94 kB, 166 mal heruntergeladen, zuletzt: )

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „egon“ ()

    egon schrieb:

    Da hast du mich falsch verstanden. Mit Datenbanken möchte ich nicht anfangen - habe schon genügend offene Baustellen. Es geht mir nur um die Art wie DataBindXY( angewendet wird.
    Ja, und von der Art, wie's da demonstriert ist, täte ich abraten.
    Tatsächlich kann ChartControl nur ein "halbes" Databinding.
    Bei richtigem Databinding updated sich das Control automatisch, wenn die Daten sich ändern - gebunden eben.
    Beim Chart hingegen muss man das aktualisieren anstossen. Das hat gute Gründe - ist aber eiglich kein Databinding mehr.

    egon schrieb:

    Da hast du mich falsch verstanden. Mit Datenbanken möchte ich nicht anfangen - habe schon genügend offene Baustellen. Es geht mir nur um die Art wie DataBindXY( Viel größer ist für mich aber die Frage, wie man aus einer Klasse heraus auf Daten aus dem Dataset zugreift.
    Ich gestalte meine Datasetse oft Singleton-ähnlich.
    Das heisst, es darf nur eine public shared Instanz des Datasets geben.
    Auf die kann man dann von überall zugreifen - das ist das Wesen von Public Shared.

    Der Singleton-Pattern verträgt sich leider überhaupt nicht mit der Art, wie man im WinForm-Designer mit Datasetsen arbeitet - da hat ja jedes Form sein eigenes Dataset (und das Chaos kann beginnen).
    Daher habich eine "Register"-Extension-Methode erfunden, die das Form nach Datasetsen durchsucht, und alle redundanten ersetzt durch das einzig zulässige.
    Sources finden sich glaub in Daten laden, speichern, verarbeiten - einfachste Variante , Abschnitt "formübergreifendes Databinding".
    oder gugge codeproject.com/Articles/10351…ped-Dataset-for-Beginners - da habich glaub eine etwas verbesserte Fassung reingebastelt.
    Das mit dem Klassen voneinander separieren ist so eine Sache. Grundsätzlich wiederhol ich mich: Das DataSet ist ja schon eine Klasse für sich (im doppelten Sinne, ggf. wird mir das EdR bestätigen) ;) .
    Die Fehlermeldung ist leicht erklärt (aber schwieriger beseitigt): "DataSet1" beschreibt 2 Sachen: Die Klasse in der DataSet1.Designer.VB-Datei und eine Instanz jener Klasse. Diese Instanz ist in Form1 ein konkretes Objekt. Eine Instanz. Und wenn Du in Form1 schreibst: DataSet1.Messreihe(0).GetMesswertRows.Select(Function(x) x.X_Wert).ToList.ToArray, dann ist dem Compiler klar: "Aha, ich nehm also die DataSet-Objektinstanz her und mach was damit." Das geht in der Testklasse aber nicht. Da hat "DataSet1" nur noch eine Bedeutung. Es ist ein Klassenname. Und mit einem Klassennamen kann man nicht hantieren wie mit einem Objekt. Dazu nur kurz die Anmerkung: Dass DataSet in Form1 eine Doppelbedeutung bekommt, ist durch den Inhalt der Form1.Designer.VB begründet. Da steht fast ganz unten:

    VB.NET-Quellcode

    1. Friend WithEvents DataSet1 As DataSet1
    Also: Erzeuge ein projektweit bekanntes eventauslösenden* Objekt vom Typ DataSet1 und nenne es "DataSet1".

    Wie man das Problem jetzt beseitigen kann: Z.B. mit den Extensions vom EdR. Der kann Dir da weiterhelfen. Ich kenne diese nicht, sondern ich wähle den harten, dummen Weg: Selbst rausfinden. Das Rad neu erfinden. Muahaha. Wie passend. Auch wenns 8-eckig wird ||

    Bzgl. der Mikrosaft-Beispiele: Diese bringen keinen Mehrwert. Auch dort geht es nur darum: Woher kommen die X-Werte, woher die Y-Werte für eine Punkteserie. Das hast Du aber auch schon vorher gehabt. Dass sie jetzt aus ggf. 2 DataTables kommen, bringt Dir doch keinen Vorteil. Das hättest Du vorher auch schon haben können. Nur Dir geht's doch um: mehrere Kurven ohne Code aus mehreren Quellen füttern. Und das bleibt m.E. unerreichbar.

    *eventauslösend ist es auch ohne WithEvents, aber mit dem Schlüsselwort kann man eben auch schon zur Designzeit dessen Events fest im Code verdrahten und muss nicht mit AddHandler während der Laufzeit rumhantieren.
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.