Arbeiten mit dem Dataset

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 14 Antworten in diesem Thema. Der letzte Beitrag () ist von VaporiZed.

    Arbeiten mit dem Dataset

    Grüßt euch,

    ich habe nach meinen letzten Fragen eine Menge gelernt was das Arbeiten mit typisierten Datasets angeht, habe die "4 Views" vom EDR durchgearbeitet und weitgehend verstanden, mich mit der dbextention beschäftigt, die ja auch sehr hilfreiche Funktionen bereitstellt.

    Gehen wir mal davon aus es geht um eine einfache Auftragsverwaltung mit den Tabellen Artikelkategorie, Artikel, Kunde, Aufträge und AuftragArtikel. Ich denke das sollte soweit selbsterklärend sein.
    Diese habe ich angelegt und wenn ich nur Datagridviews erstelle und diese auch miteinander verknüpfe auf einem einzelnen Form, dann klappt das auch wie geschmiert, also gehe ich davon aus, dass die Relationen passen usw.
    So weit so gut.

    Jetzt möchte ich das ganze ein bisschen schöner machen, also soll es ein AuftragÜbersicht-Form geben, in dem ein Datagridview mit allen Aufträgen enthalten ist. Das ist ja erstmal einfach. Dann kann man einen Eintrag auswählen und mit Klick auf "bearbeiten" soll dieser Auftrag bearbeitet werden können. Dazu soll sich ein weiteres Fenster öffnen (AuftragDetails), oben mit den Details wie Kundennummer, Name, Adresse... und im unteren Bereich die Auflistung der zugehörigen Artikel. Diese sollen bearbeitet werden können, also hinzufügen, ändern, löschen, Suche für einen anderen Kunden,... Anschließend kann mit Klick auf speichern gespeichert und das neue Fenster wieder geschlossen werden. Man ist also wieder bei der tabellearischen Übersicht aller Aufträge.
    Für die Artikel klappt das schon wunderbar, entsprechend dem VideoTutorial vom Erfinder der Rades (dort gibt es ja keine untergeordnete Sammlung an Zeilen wie das beim Auftrag der Fall ist...).

    1. Frage: Wie geht man richtig vor um das umzusetzen?
    Mein Ansatz: Ich kann in der Auftragsmaske über bindingsource.current die aktuell ausgewählte Zeile an das neue Form übergeben, dort verändern und anschließend bei Klick auf speichern die Row speichern oder wieder zurückgeben... Ist das ein (logischer) Weg?
    Mein Gedanke: Das klappt ein bisschen: Ich kann vorhandene Aufträge anschauen und vorhandene Artikel auch bearbeiten. Das Hinzufügen oder Löschen von Artikeln klappt aber nicht. (Ich hoffe, dass dieser Schritt jetzt nur noch "trivial" ist...) Wie müsste das "im Dataset speichern" umgesetzt werden?

    2. Frage: Kann ich in der AuftragDetails Maske das Datagridview auch durch ein eigen entworfenes Benutzersteuerelement ersetzen? (Zum Beispiel ein Steuerelement welches jede Artikelzeile repräsentiert und dann in einem FlowLayoutPanel angeordnet wird? Da habe ich schon wirklich ganz coole Sachen basteln können die dann zum Beispiel den Inhalt einer List (of EigeneKlasse) enthalten. In der Hilfe von Microsoft habe ich zwar sogar etwas in der Richtung gefunden, aber bin nicht richtig weiter gekommen, dort habe ich nicht alles verstanden.

    Könnt Ihr mir da weiterhelfen ? Kennt jemand darüber hinaus Tutorials oder Bücher die an dieser Stellen weiterhelfen?



    Was ich bis jetzt habe:
    (Im Form Auftragsübersicht)

    VB.NET-Quellcode

    1. Private Sub ToolStripMenuItem_Click(sender As Object, e As EventArgs) Handles AuftragBearbeitenToolStripMenuItem.Click
    2. Select Case True
    3. Case sender Is AuftragBearbeitenToolStripMenuItem
    4. Dim Adet As New Auftragdetails(DirectCast(DirectCast(TblAufträgeBindingSource.Current, DataRowView).Row, DTSAuftragsverwaltung.tblAufträgeRow).ID)
    5. Adet.Show()
    6. End Select


    (im Form AuftragDetails)

    VB.NET-Quellcode

    1. Public Class Auftragdetails
    2. Dim rwAuftrag As DTSAuftragsverwaltung.tblAufträgeRow
    3. Dim _Auftragsnummer As Integer
    4. Public Sub New(ByVal Auftragsnummer As Integer)
    5. ' Dieser Aufruf ist für den Designer erforderlich.
    6. InitializeComponent()
    7. _Auftragsnummer = Auftragsnummer
    8. ' Fügen Sie Initialisierungen nach dem InitializeComponent()-Aufruf hinzu.
    9. rwAuftrag = Auftragsmaske.DTSAuftragsverwaltung.tblAufträge.FindByID(_Auftragsnummer)
    10. End Sub
    11. Private Sub Auftragdetails_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    12. IDTextBox.Text = rwAuftrag.ID
    13. VornameTextBox.Text = rwAuftrag.tblKundenRow.Vorname
    14. NachnameTextBox.Text = rwAuftrag.tblKundenRow.Nachname
    15. StraßeTextBox.Text = rwAuftrag.tblKundenRow.Straße
    16. PostleitzahlTextBox.Text = rwAuftrag.tblKundenRow.Postleitzahl
    17. OrtTextBox.Text = rwAuftrag.tblKundenRow.Ort
    18. AnredeTextBox.Text = rwAuftrag.tblKundenRow.Anrede
    19. Dim lst As New List(Of DTSAuftragsverwaltung.tblAuftrArtikelRow)
    20. For Each rw In rwAuftrag.GettblAuftrArtikelRows
    21. lst.Add(rw)
    22. Next
    23. DataGridView1.DataSource = lst
    24. End Sub
    25. End Class



    Insbesondere beim Füllen des DGV und der List of bin ich mir sicher, dass es dort einen anderen Way to go gibt, vermutlich auch bei der Übergabe der Auftragsnummer...

    Danke schonmal für eure Hilfe :)

    CodeTags korrigiert ~VaporiZed

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

    Adet :huh: Komische Variablenbenennung. Aber wurscht.
    Du übergibst eine ID an das SubForm? Warum nicht gleich die Row an sich?

    Philipp schrieb:

    Das Hinzufügen oder Löschen von Artikeln klappt aber nicht.
    Warum nicht? Wenn Du einen Auftrag bearbeitest, stell ich mich vor, dass Du eine Artikelliste hast, bei der Du dann einen Artikel auswählst, dann auf einen [+]-Button drückst und so den Artikel zu Auftrag hinzufügst. Da jeder Auftrag seine Artikel kennt, sollte es doch leicht zu bewerkstelligen sein, dieser Auflistung ein Artikel hinzuzufügen. Ich gehe mal davon aus, dass Du tDS-VVV-typisch eine ArtikelTabelle, eine AuftragsTabelle und eine ArtikelAuftragsTabelle hast. Ach, steht ja sogar oben. Dann fügst Du der letzteren eine neue Zeile hinzu, die auf den Artikel und den Auftrag verweist und gut ist. Löschen ähnlich: Nimm den entsprechenden Eintrag aus der ArtikelAuftragsTabelle und lösche ihn. Fertig.
    Zu Frage 2: Wenn Du gutes Baumaterial hast, dann schon.

    Philipp schrieb:

    Zum Beispiel ein Steuerelement welches jede Artikelzeile repräsentiert und dann in einem FlowLayoutPanel angeordnet wird
    Punkt eins kann ein DGV oder z.B. auch ne ListBox mit DataBinding machen. Punkt zwei versteh ich inhaltlich nicht. Geht's um die Details des Artikels, die angezeigt werden sollen? Ein DGV bekommt das von Haus aus hin. Aber Du willst wohl was anderes. Was konkret? Gib mal n Beispielbild oder ne gute Beschreibung.
    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.

    VaporiZed schrieb:

    Adet Komische Variablenbenennung. Aber wurscht.

    Oh da hast du recht. Da hat man mal was ein paar mal hin und her probiert und zack hat man so ein Überbleibsel im Code... :o

    VaporiZed schrieb:

    Du übergibst eine ID an das SubForm? Warum nicht gleich die Row an sich?

    Stimmt, das wäre ja noch einfacher... Das bekomme ich hin.

    VaporiZed schrieb:

    Dann fügst Du der letzteren eine neue Zeile hinzu, die auf den Artikel und den Auftrag verweist und gut ist. Löschen ähnlich: Nimm den entsprechenden Eintrag aus der ArtikelAuftragsTabelle und lösche ihn. Fertig.


    Ja und das bekomme ich nicht hin. An und für sich kann es nicht schwer sein... aber vermutlich habe ich nur ein riesen Brett vorm Kopf.
    Ich habe ja die Row und um die Artikel die zu dem Auftrag gehören habe ich in eine Liste eingelesen, weil es mir leichtergefallen ist damit zu arbeiten. Aber wenn ich jetzt zu dieser Liste einen Eintrag hinzufüge, dann erscheint der ja nicht im Dataset...

    Zu 2. Du hast vollkommen Recht. Ich will quasi auch genau das was ein DGV kann -> Ein DGV ist sehr zweckmäßig und etwas nüchtern -> Ich möchte die Daten gerne etwas schöner darstellen können.
    Zum Beispiel dachte ich an ein Benutzersteuerelement welches im Kern Textfelder enthält mit den Daten die ich anzeigen möchte, also zum Beispiel Artikelnummer, Preis, Anzahl usw...
    Und für jeden Artikel im Auftrag wird ein Steuerelement zu einem FlowLayoutPanel hinzugefügt. Ich hoffe es wird klar was ich meine, sonst erstelle ich morgen mal fix ein Beispiel...
    Moment. Du hast beim Umgang mit tDS erstmal keine eigen erstellten Listen. Das macht das tDS für Dich. Bzw. die Designer-Unterstützung. Im Fenster Datenquellen hast Du Dein tDS, darunter die DataTables und wenn Du die "aufklappst", kommen jene DataTables, die von der selektierten abhängig sind. Wenn Du also Deine DataTable Auftrag auf's Form ziehst und danach noch die Auftrags-Artikel-Tabelle, die bei den Datenquellen als abhängige (Unter)Tabelle von der Auftragstabelle erscheint, werden zur Laufzeit nur die Auftragsartikel angezeigt, die zum selektierten Auftrag gehören. Sonst keine. Daher musst Du gar keine eigenen Listen führen.

    Philipp schrieb:

    Zum Beispiel dachte ich an ein Benutzersteuerelement welches im Kern Textfelder enthält mit den Daten die ich anzeigen möchte, also zum Beispiel Artikelnummer, Preis, Anzahl usw...
    Und für jeden Artikel im Auftrag wird ein Steuerelement zu einem FlowLayoutPanel hinzugefügt.
    10 Artikel mit 5 Eingenschaften ergibt mit Deinem Plan schon 50 Controls. Ist das Dein Plan? Ich bin auf Dein Beispiel gespannt.
    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.
    Okay ich habe mal Quick und Dirty ein Benutzersteuerelement erstellt um zu Zeigen was ich meine:

    Das Steuerelement hat die Textboxen Beschreibung, Preis und Anzahl (beispielhaft). Eben genau die Informationen die ich dem Nutzer für jeden Artikel bereitstellen möchte, also ggf. auch noch mehr.

    Zur Laufzeit wird dann für jeden Artikel EIN Steuerelement hinzugefügt, so wie in Screenshot 2.png.

    Ich hoffe jetzt wird deutlich was ich meine.

    Dieses Design hat natürlich noch keinerlei Vorteile gegenüber einem Datagridview, aber in einer etwas durchdachteren Form hat man da natürlich schon schönere Möglichkeiten denke ich...
    Bilder
    • 2.png

      40,8 kB, 790×627, 76 mal angesehen
    • Screenshot 2021-06-16 221418.png

      5,49 kB, 765×205, 64 mal angesehen
    Klar, kein Problem, mach ruhig, wenn Du Dir davon Designvorteile versprichst. Mit wenig Aufwand lässt sich da auch datenmäßig ne TableRow dranbinden.
    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.

    VaporiZed schrieb:

    Moment. Du hast beim Umgang mit tDS erstmal keine eigen erstellten Listen. Das macht das tDS für Dich. Bzw. die Designer-Unterstützung. Im Fenster Datenquellen hast Du Dein tDS, darunter die DataTables und wenn Du die "aufklappst", kommen jene DataTables, die von der selektierten abhängig sind. Wenn Du also Deine DataTable Auftrag auf's Form ziehst und danach noch die Auftrags-Artikel-Tabelle, die bei den Datenquellen als abhängige (Unter)Tabelle von der Auftragstabelle erscheint, werden zur Laufzeit nur die Auftragsartikel angezeigt, die zum selektierten Auftrag gehören. Sonst keine. Daher musst Du gar keine eigenen Listen führen.

    Das ist richtig. Das klappt auch solange beide DGVs auf einem Form sind. Wenn das AuftragsDGV auf einem und das AuftragArtikelDGV auf einem anderen ist klappt das nicht mehr. Das ist mein Problem...



    Und wenn ich an das zweite Formular die row übergebe, dann sehe ich alles richtig, kann vorhandene Datensätze auch im DGV bearbeiten. Aber irgendwie bin ich zu doof den richtigen Befehl zum löschen und hinzufügen von Datensätzen herauszufinden... *help*

    VB.NET-Quellcode

    1. Public Class Auftragdetails
    2. Dim _rwAuftrag As DTSAuftragsverwaltung.tblAufträgeRow
    3. Public Sub New(ByVal RwAuftrag As DTSAuftragsverwaltung.tblAufträgeRow)
    4. ' Dieser Aufruf ist für den Designer erforderlich.
    5. InitializeComponent()
    6. ' Fügen Sie Initialisierungen nach dem InitializeComponent()-Aufruf hinzu.
    7. _rwAuftrag = RwAuftrag
    8. End Sub
    9. Private Sub Auftragdetails_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    10. IDTextBox.Text = _rwAuftrag.ID
    11. VornameTextBox.Text = _rwAuftrag.tblKundenRow.Vorname
    12. NachnameTextBox.Text = _rwAuftrag.tblKundenRow.Nachname
    13. StraßeTextBox.Text = _rwAuftrag.tblKundenRow.Straße
    14. PostleitzahlTextBox.Text = _rwAuftrag.tblKundenRow.Postleitzahl
    15. OrtTextBox.Text = _rwAuftrag.tblKundenRow.Ort
    16. AnredeTextBox.Text = _rwAuftrag.tblKundenRow.Anrede
    17. DataGridView1.DataSource = _rwAuftrag.GettblAuftrArtikelRows 'DataGridView1 ist ein bis dahin leeres und ungebundenes DGV
    18. End Sub
    19. End Class

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

    Warum klappt das mit den getrennten DGVs nicht? Was heißt das genau? Wenn Du die auftragsabhängige-Artikelliste auf's SubForm haust, schau Dir an, welche Datenquelle für die generierte BS eingestellt wird. Das müsste eine AuftragsBindingSource sein. Diese musst Du natürlich vor SubForm-Aufruf korrekt befüllen, und zwar mit dem im Hauptform gewählten Auftrag, sonst weiß das Programm ja gar nicht, welchen Auftrag es bearbeiten soll. Also sowas ungefähr:

    VB.NET-Quellcode

    1. Using Dialog As New FrmBestellung
    2. Dialog.AuftragBindingSource.DataSource = AuftragBindingSource.Current
    3. Dialog.ShowDialog(Me)
    4. End Using

    Das mit der GUI-Befüllung: Bitte nicht, dafür gibt es DataBinding. Binde die Controls an die Properties der DataTable, die über die BS verfügbar ist.
    Und Löschen und Hinzufügen: Das kommt drauf an, wie Dein SubForm funktionieren soll. Wenn Du nur einen Artikel im SubForm auswählen und dann ins MainForm zurückkehren willst, mach die Entscheidung vom DialogResult abhängig und füge direkt nach dem ShowDialog(Me) (s. Code oben) den HinzufügeCode aus (DeinTds.AuftragsArtikel.AddAuftragsArtikelRow(…)). Wenn Du mehrere Artikel hinzufügen willst, schieb den Code ins SubForm.

    ##########

    @Philipp: Anbei mal ein simples Beispielprojekt
    Dateien
    • WinFormsVB.zip

      (32,63 kB, 62 mal heruntergeladen, zuletzt: )
    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“ ()

    Sehr cool, danke. Damit werde ich erstmal weiter kommen.

    VB.NET-Quellcode

    1. Private Sub BtnAddItem_Click(sender As Object, e As EventArgs) Handles BtnAddItem.Click
    2. DirectCast(BsTds.DataSource, Tds).OrderItems.AddOrderItemsRow(DirectCast(DirectCast(BsOrders.Current, Data.DataRowView).Row, Tds.OrdersRow), DirectCast(DirectCast(BsItems.Current, Data.DataRowView).Row, Tds.ItemsRow))
    3. End Sub


    Okay machen wir uns nichts vor, sooo trivial ist dieses Gebilde nun auch nicht :D
    Nun, das sollte es aber fast sein. Klar, in der vorliegenden Rohform sieht es ziemlich lang und unübersichtlich aus. Aber der Doppelcast muss im Zusammenhang mit tDS-Arbeiten bekannt sein. Sonst kommst Du nicht sinnvoll weiter. Klar, später kannst Du dann Dir ne Extension nutzen (entweder die von EdR, die irgendwo in seinen Helpers drinsteckt oder indem Du sie Dir selber baust), wodurch DirectCast(DirectCast(BsOrders.Current, Data.DataRowView).Row, Tds.OrdersRow) zu BsOrders.Current(Of Tds.OrdersRow) wird. Und das mit dem DirectCast(BsTds.DataSource, Tds) ist ne Sache von mir selbst, was eine vereinfachte formübergreifende tDS-Arbeit ermöglicht und benötigt. Aber sonst sollte das Konstrukt an sich inhaltlich klar, nachvollziehbar und zukünftig auch von Dir nutzbar sein. Sonst wird das mit tDS und Co auf Dauer nix.
    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.
    Wo lernt man das? Leider habe ich noch keine Lektüre gefunden die das mal anschauliche erklärt. Die "einfache" Variante des doppelcasts a la

    VB.NET-Quellcode

    1. Dim rwKunde = DirectCast(DirectCast(TblKundenBindingSource.Current, DataRowView).Row, DTSAuftragsverwaltung.tblKundenRow)


    habe ich mittlerweile verstehen können... aber es fällt mir unheimlich schwer das zu lernen. Lernt man das sonst im Beruf oder wie läuft das? (Ich selber mache es ja nicht beruflich, wie man sieht...)
    Ich lerne ganz viel aus dem ObjectBrowser, und aus dem LokalFenster und so.
    Dazu muss man die Dinger aber kennen, und man muss auch Grundlagenwissen haben: Was ist ein Objekt, ein Datentyp, ein Member, ein Shared Member, eine Enumeration, Vererbung, Interface, Überladung, Überschreibung...
    Dann kann man an einem Haltepunkt zB eine BindingSource ins Überwachungsfenster holen, und auskundschaften: Was ist ihre Datasource, was ihre .List, was ist grad .Current - und wie zum Kuckuck ist da eiglich die DataRow drin versteckt - die ja im DGV angezeigt wird.

    Zu ObjectBrowser und Kram: VisualStudio richtig nutzen (Google ist nicht deine Mami)
    Das ist einer der komplizierteren Ausdrücke, die Du Dir vom Aufbau her musst. Nicht auswendig lernen, sondern nachbauen können. Sobald Du das kannst (aber erst dann, denn dann haste es Dir verdient :P ), mach Dir das Leben leichter, indem Du anfängst, mit Extensions zu arbeiten. Extensions sind Prozeduren, die so tun, als gehören sie zu einer Klasse, ohne das tatsächlich zu sein. Erstell Dir dazu in einer neuen Datei im Projekt ein Modul:

    VB.NET-Quellcode

    1. Imports System.Runtime.CompilerServices
    2. Public Module Extensions
    3. <Extension> Public Function Current(Of T As Data.DataRow)(BindingSource As BindingSource) As T
    4. If BindingSource.Count = 0 Then Return Nothing
    5. If TypeOf BindingSource.Current IsNot Data.DataRowView Then Throw New ArgumentException("Diese BindingSource ist nicht an ein tDS-DataTable gebunden.")
    6. Dim UntypedDataRow = DirectCast(BindingSource.Current, Data.DataRowView).Row
    7. Try
    8. Return DirectCast(UntypedDataRow, T)
    9. Catch ICEx As System.InvalidCastException
    10. Dim TypeName = UntypedDataRow.GetType.Name
    11. Throw New ArgumentException($"Diese BindingSource beinhaltet Rows vom Typ {TypeName}, nicht vom Typ {GetType(T).Name}.")
    12. End Try
    13. End Function
    14. End Module

    Dann kannst Du im Hauptprogramm nämlich schreiben:

    VB.NET-Quellcode

    1. Dim rwKunde = TblKundenBindingSource.Current(Of DTSAuftragsverwaltung.tblKundenRow)
    Du kannst diese Prozedur also so verwenden, als sei sie eine Prozedur der BindingSource-Klasse. Ist sie aber gar nicht. Macht einem das Codieren aber leichter.
    Wenn Du einen falschen Row-Typ übergibst oder die Extension auf eine falsche BS anwendest, wirste dezent drauf hingewiesen. Ok, es ist auch ohne das Exception-Rethrowing in Zeile#14 aussagekräftig genug, aber wurscht.
    Später kannst Du das, ggf. mit zahlreichen anderen, selbstgebastelten Extensions auch in eine DLL auslagern und für andere Projekte nutzen. Und wenn Du die Extension verstanden hast und erfolgreich anwenden kannst, dann kannste Dir auch mal meine typed-BS reinziehen.
    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.
    Doch. Das Thema hatten wir schon mal. Nutze ich seit langer Zeit.
    Bilder
    • uBS-Current.png

      9,56 kB, 675×105, 53 mal angesehen
    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.