Mehrfacheinträge im DGV behandeln

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

Es gibt 17 Antworten in diesem Thema. Der letzte Beitrag () ist von affrop.

    Mehrfacheinträge im DGV behandeln

    Hallo Leute,

    ich habe ein kleines Proggi geschrieben zur Erfassung von Aufmaßen von der Baustelle. Nun stehe ich vor folgendem Problem:
    Ein Aufmaß besteht aus mehreren Aufmaßblättern, in welchem mehrere Positionsnummern entahlten sind. Bei der Datenerfassung kann im Moment der Einfachheit halber jede Positionsnummer mit den jeweiligen Mengenangaben unbegrenzt oft in dem DGV erfasst werden. Da ich die Datenbank aber nicht unnötig vollmüllen will, möchte ich nun die Mehrfacheinträge zusammenfassen.
    Kleines Beispiel dazu:
    Der User gibt folgendes ein:
    1) Positionsnummer 01.01.0010, Menge 23,7, Einheit m, Aufmaßblatt 1
    2) Positionsnummer 01.01.0010, Menge 23,7, Einheit m, Aufmaßblatt 1
    usw.
    Am Ende soll dann daraus folgendes werden:
    1) Positionsnummer 01.01.0010, Menge 47,4, Einheit m, Aufmaßblatt 1

    Ich hoffe der Sinn ist einigermaßen rüber gekommen.

    Nun habe ich dazu folgenden Code mir ausgetüftelt, der allerdings nur bedingt funktioniert und das auch nicht wirklich perfomant.

    Vielleicht hat jemand von euch ne Idee dazu.

    VB.NET-Quellcode

    1. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    2. Me.Cursor = Cursors.WaitCursor
    3. Dim liste1 As New List(Of String)
    4. Dim liste2 As New List(Of String)
    5. Dim liste3 As New List(Of String)
    6. Dim liste4 As New List(Of String)
    7. For Each dr As DataGridViewRow In DataGridView1.Rows
    8. If Not DataGridView1.Item(0, dr.Index).Value Is Nothing Then
    9. liste1.Add(DataGridView1.Item(0, dr.Index).Value.ToString)
    10. If Not DataGridView1.Item(3, dr.Index).Value Is Nothing Then
    11. liste3.Add(DataGridView1.Item(3, dr.Index).Value.ToString)
    12. End If
    13. End If
    14. Next
    15. liste2 = liste1.Distinct.ToList
    16. liste4 = liste3.Distinct.ToList
    17. Dim dtv As DataView
    18. Dim dt As New DataTable
    19. Dim dtResult As New DataTable
    20. For Each colum As DataGridViewColumn In DataGridView1.Columns
    21. dt.Columns.Add(colum.HeaderText)
    22. dtResult.Columns.Add(colum.HeaderText)
    23. Next
    24. For Each dr As DataGridViewRow In DataGridView1.Rows
    25. dt.Rows.Add(dr.Cells.Item(0), dr.Cells.Item(1), dr.Cells.Item(2), dr.Cells.Item(3))
    26. Next
    27. dtv = New DataView(dt)
    28. For i = 0 To liste2.Count - 1 Step 1
    29. dtv.RowFilter = "[" & DataGridView1.Columns(0).HeaderText & "] = '" & liste2.Item(i).ToString & "'"
    30. Dim dta As New DataTable
    31. dta = dtv.ToTable
    32. Dim dtva As New DataView(dta)
    33. For j = 0 To liste4.Count - 1 Step 1
    34. dtva.RowFilter = "[" & DataGridView1.Columns(3).HeaderText & "] = '" & liste4.Item(j).ToString & "'"
    35. Dim dtb As New DataTable
    36. dtb = dtva.ToTable
    37. Dim Menge As Double = 0
    38. For Each dr As DataRow In dtb.Rows
    39. If dr.Item(2).ToString = "" OrElse dr.Item(2) Is Nothing Then
    40. Menge = Menge + 0
    41. Else
    42. Menge = Menge + Double.Parse(dr.Item(2).ToString)
    43. End If
    44. Next
    45. dtResult.Rows.Add(liste2.Item(i).ToString, Menge, "Einheit", liste4.Item(j).ToString)
    46. Next
    47. Next
    48. DataGridView1.Rows.Clear()
    49. For Each dr As DataRow In dtResult.Rows
    50. DataGridView1.Rows.Add(dr.Item(0).ToString, dr.Item(1).ToString, dr.Item(2).ToString, dr.Item(3).ToString)
    51. Next
    52. DataGridView1.Refresh()
    53. Me.Cursor = Cursors.Default
    54. End Sub
    um mich der Sache langsam zu nähern habe ich erst einmal ein Testprojekt erstellt, dass nur über ein dgv und einen button verfügt um zu sehen wie sich der code verhält. als einfache filterungsvariante fiel mir da das dataview ein. im Prinzip soll das dgv geändert werden und das geänderte dgv später in eine Datenbank.
    die relation zwischen positionsnummer und aufmaßblattnummer ist ja theoretisch bereits vorhanden, deshalb habe ich ja ein solch verschachtelten code ausgetüdelt um dem gerecht zu werden.
    Im Grundsatz funktioniert der Code ja, jedoch die Kumulation der mengenwerte nicht.
    ich kann mich nur wiederholen: Ohne die Gesamtheit der in Datenverarbeitungs-Vorraussetzungen aufgeführten Punkte ist sowas komplexes nicht zu stemmen.
    • Es reicht nicht, ungefähre Vorstellungen von "theoretisch vorhandenen Relationen" zu haben, sondern das Konzept relationaler Datenmodellierung muss vollständig verstanden sein. Insbesondere der Begriff "Relation" - das ist was klar definiertes inne Datenbänkerei.
      Wonach es mir bei dir nicht aussieht, wenn du von einer "relation zwischen nummern" sprichst, denn inne Datenbänkerei bestehen Relationen zwischen "Entitäten", bzw. - weniger abstrakt: zwischen "Tabellen".
      Relationen bestehen aber nicht zwischen Nummern.
    • Nachdem die relationale Grundidee verstanden ist, gilt es, diese anzuwenden, und ein stimmiges Datenmodell für konkret deine Anforderungen aufzustellen. Am einfachsten ist das, indem du ein typisiertes Dataset erstellst.
    • Nachdem du ein typDataset erstellt hast, gilt es, Databinding zu verstehen, und damit (nicht mit Code!) eine Oberfläche zu erstellen
    • bischen Laden und Speichern dran, und fertig (in Grundzügen)
    alles annere wird nix, also sieh zu, dass du dir die Datenverarbeitungs-Vorraussetzungen erarbeitest.
    Übrigens gibts doch eine Alternative zum typDataset, ebenfalls typisiert, mit Databinding, und aufbauend auf relationaler Datenmodellierung: Datenbanking mit EntityFramework. Ist aber wesentlich schwieriger, sich da einzuarbeiten - nur kleiner Einstieg: EntityFramework-CodeFirst-Sample

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

    ich kann mich auch nur wiederholen: ich habe 1 dgv welches 4 spalten besitzt und so als string typisiert in eine datatable geladen wird. welche relation zu anderen Tabellen sollen da ne rolle spielen????? ich habe ja nur eine Tabelle....
    der grund das in dem code mehrere datatable und dataview enthalten sind ist einzig der versuch der 2-kriteriellen Filterung. Aber der Ausgangspunkt bleibt eine einzige Tabelle. zu was soll eine stand-alone-Tabelle Relationen haben??
    ich will lediglich die pro aufmaßblattnummer mehrfach vorhandenen positionsnummern zusammenführen.

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

    affrop schrieb:

    Aber der Ausgangspunkt bleibt eine einzige Tabelle.
    naja - wenn du damit auskommst, hast du natürlich recht.

    Ich hab halt mal ein proggi geschrieben, mit dem von Pdf-Bauplänen die Aufmasse ausgemessen und Positionen in sog. "LeistungsVerzeichnissen" zugeordnet wurden. Da war ich mit glaub 15 Tabellen unterwegs (ok, war auch einiges an Grafik dabei).
    Also ich gewann stark den Eindruck: wenn man mit Baustellen und Aufmassen zu tun hat - 1 Tabelle reicht sicherlich nicht.
    ok mal zur Verdeutlichung:
    Voraussetzung ist ein vorhandenes leistungsverzeichnis, welches auf der Baustelle abgearbeitet wird. damit monatlich ne teilrechnung geschrieben werden kann, muss die monatliche Leistung auf der Baustelle aufgemessen werden. die aufmasse sollen hier im proggi zusammengestellt werden.

    hier das dgv nach dem dateninput, unsortiert und mit mehrfacheinträgen:


    und so soll es dann aussehen, sortiert nach blatt-nr. und mehrfacheinträge zusammengefasst:


    da ich hauptberuflich Projektleiter im Baugewerbe bin und programmieren nur ein Hobby ist, sehe ich da vielleicht diverse zusammenhänge etwas anders als ein "reiner" programmierer. insofern Entschuldigung an dieser stelle, wenn es an mancher stelle zu verständnissproblemen kommt.

    Du hast sicherlich recht, das gesasmte proggi kommt mit nur einer Tabelle nicht aus. aber die jeweiligen Tabellen sind weitestgehend stand-alone und sonst über nen primärschlüssel relationell verbunden.
    aber der hier beschriebene programmabschnitt benötigt nur eine stand-alone Tabelle.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „affrop“ ()

    affrop schrieb:

    verständnissprobleme
    ja - dir sind halt die datenbänkerischen Grundlagen nicht bekannt.
    Dein Bildle zeigt bereits ganz klar ein unzureichendes Datenmodell - sowas erkennt man an mehrfach vorkommenden Einträgen, sog. "Redundanzen".
    Redundante Werte in Tabellen sind klare Signale, dass hier übergeordnete Tabelle auszulagern wären, und die hier gezeigte Tabelle würde nur per Fremdschlüssel darauf verweisen.
    "Ausnormalisieren" nennt man sowas (ein Datenmodell in die sog. 3. Normalform" bringen).

    Also an Bild1 sieht jeder Datenbänker sofort die Notwendigkeit , die Entitäten "Position" und "Einheit" aus der aktuellen Ansicht (ich nenne sie mal "Erfassung") auszulagern, sodass minimum 3 Tabellen entstehen.
    Eventuell auch "Blatt" auslagern, aber zumindest "Position" und "Einheit".

    Also ein Datenbänker denkt: "Eine Position hat einen Positions-Text, eine Nummer, eine Einheit und einen Einheitspreis.
    Ein Erfassungs-Datensatz verweist auf eine Position, fügt eine Menge hinzu, und dann kann ein Gesamtpreis berechnet werden - der wird übrigens nicht gespeichert."

    Jo - so um den Dreh sind datenbänkerischen Prinzipien - ich sags ja nur.
    Steht dir natürlich frei, zu versuchen, daran vorbei-zuprogrammieren wird halt Auswirkungen haben auf Leisungsfähigkeit, Korrektheit, Stabilität und Wartbarkeit deines Programms.
    wie bereits eingangs erwähnt habe ich die die mehrfacheingabe (Redundanz) bewusst zugelassen in meinen Überlegungen. Insofern ist das kein unzureichendes datenmodell sondern in erster Linie Bequemlichkeit meiner Wenigkeit in hinblick auf den code.
    denn während der eingabe schon zu prüfen ob diese positionsnummer bereits in dem jeweiligen aufmaßblatt vorhanden ist wollte ich mir vom hals halten.
    ein eintrag in dieser aufmaßliste, die positionsnummer, ist untrennbar mit dem positionstext, der Einheit, dem EP und der Blattnummer verbunden. diese gehören zur exakten Identifikation der auf der baustelle ausgeführten arbeit. nur die menge muss separat erfasst werden. da irgenwas auszulagern ist doch programmatisch exorbitant mehr aufwand als wenn ich einfach die paar eingenschaften mit der positionsnummer als primärschlüssel in eine Tabelle schreibe.
    vor diesem Hintergrund erschließt sich mir die Notwendigkeit einer Auslagerung überhaupt nicht.
    zur weiteren Verarbeitung im Programmablauf könnte ich natürlich die mehrfacheinträge drinne lassen, das hat keinen einfluss. es sieht einfach nur beschissen aus.
    und deshalb, aus rein optischen gründen, und um die im Hintergrund beteiligte Datenbank nicht zu übermüllen, wollte ich die mehrfachen einträge zusammenführen und die jeweiligen mengen aufaddieren.
    übrigens beim erfassen der Daten wird die entsprechende Position direkt aus dem lv in einem dgv ausgewählt. vie TextBox wird nur noch die menge und die blattnummer eingegeben. die erfassten Daten werden dann DIREKT in als datagridviewrow das dgv geschrieben, also ohne datentabelle zwischendrinne.
    aber insgesamt kommen wir von eigentlichen Thema ab. der in #1 gepostete code führt die mehrfacheinträge zusammen, leider addiert er die mengen nicht auf.
    haste da schonmal hingeschaut woran das liegen könnte?

    affrop schrieb:

    erschließt sich mir die Notwendigkeit einer Auslagerung überhaupt nicht.
    naja, ein Zweck wäre, die Benutzung zu vereinfachen:
    Wie ich dein Bild verstehe, hat der User da (bei 10.01.0010) 4 mal dieselbe Positionsnummer eingeben müssen, 4 mal denselben Positions-Text, Einheit, Einheitspreis.
    Ordentlich ausnormalisiert hätte er einfach eine Listen-Auswahl zur Verfügung gehabt, wo er diese Position einfach auswählt, statt den ganzen Sermon jedesmal neu und fehlerfrei eintippen zu müssen. Sowas ist mit "Leistungsfähigkeit" eines Programms gemeint: Ein korrekt aufgezogenes könnte in allen solchen Fällen Auswahlen anbieten.



    Sorry - bei deim Code blick ich nicht durch.
    Mir scheint die Methode mehrere verschiedene Dinge zu machen, was mit Listen, was mit dem DGV, was mit einer DataTable.
    Und nirgends steht, was sie nun eiglich tun soll.

    Verstehe ich das richtig, dass alle Datensätze mit derselben PosNr zu einem zusammengefasst werden sollen, der dann die Gesamt-Mengen und Preise etc aussagt?

    Also "Gruppieren nach PositionsNummer" ist gefragt?
    Da gibts sehr trickreiche Möglichkeiten, aber du müsstest erstmal richtige Datensätze bilden.
    also nochmal zum verständniss:
    der user muss nicht 25mal das selbe eintippen. er hat ein dgv in welchem er die Position auswählen kann und gibt dann via TextBox nur noch die menge und die blattnummer ein.
    wie du in den Bildern gesehen hast, sollen die positionsnummern so gruppiert oder zusammengeführt werden, dass sie je blatt-nr. nur einmal vorkommen. dabei sollen die mengen der jeweiligen Positionen aufaddiert werden, quasi aus 25mal Pos. 01.01.0010, 23,1m wird dann 1mal pos. 01.01.0010 577,5m

    im code habe ich halt versucht den Inhalt des dgv in eine datatable zu laden um die Methoden der dataview nutzen zu können. mit den listen habe ich versucht erstmal die mehrfacheinträge rauszulöschen um dann filtern zu können. letztlich sollte es eine resultierende datatable geben, welche wieder in das dgv geladen wird.

    das soll im groben der code machen.

    affrop schrieb:

    letztlich sollte es eine resultierende datatable geben, welche wieder in das dgv geladen wird.
    Letztlich sollte es genau eine DataTable geben.
    Der User sollte das DGV ühaupt nicht zu Gesicht bekommen, höchstens final.
    Mach Dir einen Dialog, der alle Eingaben wohlsortiert entgegennimmt, und wenn dieser fertig befüllt ist, mach einen Sinnigkeits-Check der Daten und lege sie dann in der DataTable ab.
    Mach, wie der @ErfinderDesRades sagt, eine ComboBox, wenn Du fixe Vorgaben zu einem Posten hast, nimm NumericUpDowns, wenn Zahlen eingegeben werden müssen. Da musst Du Dich nicht um die Konvertierung kümmern.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    affrop schrieb:

    er hat ein dgv in welchem er die Position auswählen kann
    aha!
    Also existiert bereits zusätzlich zur Tabelle "Erfassung" eine Tabelle "Position".
    Auch wenns dir selbst nicht klar ist, so hast du bereits ein relationales Modell mit 2 Tabellen implementiert - erkenntlich daran, dass du aus dem einen dgv was für das andere dgv aussuchen kannst.
    Nur setzst du es mit völlig unzureichenden Mitteln um.

    Aber offensichtlich klammerst du dich an deine unzureichenden Mittel - wärst du denn wenigstens bereit, ordentlich typisierte Datensätze zu erstellen?
    Weil für solche zu gruppieren gibts sehr elegante Möglichkeiten.

    Allerdings da du typDataset ja so grundsätlich ablehnst, musst du dir etwas eigenes ausdenken, um typisierte Datensätze zu erstellen.

    Edit

    RodFromGermany schrieb:

    Letztlich sollte es genau eine DataTable geben.
    ähm - keinesfalls!
    Es müssen 2, 3 oder noch mehr werden.
    Aber macht ihr mal, ich halt mich erstmal raus - zuviele Köche verderben Brei.


    also der Einfachheit halber, werden sämtliche Infos als string in die Datenbank geschrieben. werden dann zahlen zur Berechnung gebraucht werden die entsprechenden strings in double konvertiert.
    Auch auf die Gefahr hin das ich mich wiederhole: alles was über Programmierung weiß habe ich mir selbst durch diverse bücher, learning-by-doing und dieses Forum bei gebracht. Sollte ich mich also an irgendwas klammern dann an das was mir bekannt ist.
    wenn du von eleganten Möglichkeiten mittels typdatasets sprichst, welche sind das genau?

    affrop schrieb:

    Möglichkeiten
    Ohne Dich jetzt mit den Tools vom @ErfinderDesRades zu überhäufen, sieh Dir mal dieses Beispiel an.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    affrop schrieb:

    Sollte ich mich also an irgendwas klammern dann an das was mir bekannt ist.
    Logisch - an was anneres kann man sich ja nicht klammern.
    Was einen jedoch wirklich weiter bringt, ist, sich mal auf was unbekanntes einzulassen, und es kennenzulernen, damit das auch mal bekannt wird.

    affrop schrieb:

    wenn du von eleganten Möglichkeiten mittels typdatasets sprichst, welche sind das genau?
    ich sprach von eleganteren Möglichkeiten typisierter Datensätze (als Alternative zu typDataset, weil das lehnst du ja ab).
    Also wenn typisierte Datensätze gleich welcher Art vorliegen hat man die Möglichkeit mit Linq zu gruppieren.
    Mehr kann ich dazu nicht erklären, denn ohne typisierte Datensätze kann man kein Linq-Beispiel bringen.

    Bei typisiertem Dataset hingegen wäre Linq bis auf Ausnahmefälle unnötig, denn dieses hat für 80% der Anforderungen bereits viel bessere Möglichkeiten eingebaut: vier Views-Videos
    Aber diesen Link gab ich dir mittelbar bereits in post#2
    so leute, hab mich jetzt mal was mit linq befasst und das ding umgeschrieben:

    Spoiler anzeigen

    VB.NET-Quellcode

    1. 'DGV in die DataTable laden
    2. Dim dt As New DataTable
    3. For Each column As DataGridViewColumn In Form7.DataGridView1.Columns
    4. dt.Columns.Add(column.HeaderText)
    5. Next
    6. For Each row As DataGridViewRow In Form7.DataGridView1.Rows
    7. If Not row.Cells(0).Value Is Nothing Then
    8. dt.Rows.Add(row.Cells(0).Value.ToString, row.Cells(1).Value.ToString, row.Cells(2).Value.ToString, row.Cells(3).Value.ToString, row.Cells(4).Value.ToString, row.Cells(5).Value.ToString, row.Cells(6).Value.ToString)
    9. End If
    10. Next
    11. 'Mehrfacheinträge sondieren
    12. Dim Liste1 As New List(Of String)
    13. Dim Liste2 As New List(Of String)
    14. For Each row As DataRow In dt.Rows
    15. If Not row.Item(0) Is Nothing Then
    16. Liste1.Add(row.Item(0).ToString)
    17. If Not row.Item(6) Is Nothing Then
    18. Liste2.Add(row.Item(6).ToString)
    19. End If
    20. End If
    21. Next
    22. Dim ListPosNr As List(Of String) = Liste1.Distinct.ToList
    23. Dim ListBlattNr As List(Of String) = Liste2.Distinct.ToList
    24. 'Zusammenführung der Werte via LINQ
    25. dt.AsEnumerable()
    26. Dim dtRersult As New DataTable
    27. For Each col As DataColumn In dt.Columns
    28. dtRersult.Columns.Add(col.ColumnName)
    29. Next
    30. Dim menge As Double = 0
    31. For Each item As String In ListBlattNr
    32. For Each item1 As String In ListPosNr
    33. Dim pos = From p In dt Where p.Item(0).ToString = item1 AndAlso p.Item(6).ToString = item Select postext = p.Item(1).ToString, posmenge = p.Item(2).ToString, poseinheit = p.Item(3).ToString, posep = p.Item(4).ToString
    34. Dim postexte As New List(Of String)
    35. Dim poseinheiten As New List(Of String)
    36. Dim poseps As New List(Of String)
    37. For Each p In pos
    38. postexte.Add(p.postext.ToString)
    39. menge = menge + Double.Parse(p.posmenge.ToString)
    40. poseinheiten.Add(p.poseinheit.ToString)
    41. poseps.Add(p.posep.ToString)
    42. Next
    43. Dim posgp As Double = Math.Round(menge * Double.Parse(poseps.Item(0).ToString), 2)
    44. dtRersult.Rows.Add(item1, postexte.Item(0).ToString, Strings.FormatNumber(menge.ToString), poseinheiten.Item(0).ToString, poseps.Item(0).ToString, Strings.FormatNumber(posgp.ToString), item)
    45. Next
    46. Next
    47. Form7.DataGridView1.DataSource = dtRersult



    Es gibt da leider noch en kleines Problem: die Listen für PosText, PosEinheit und PosEP sind leer und die Summe der Mengen sind nicht realistisch.
    Beispiel ich habe eingegeben 3x27,9m als Mengen und die Summe gibt was von 4297m raus.
    Liegt das an der LINQ-abfrage ????