Matrix aus mehreren Tabellen in DGV erstellen

  • VB.NET
  • .NET 5–6

Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von Montoyafan.

    Matrix aus mehreren Tabellen in DGV erstellen

    Hallo,

    vllt hat jemand eine Idee wie man das umsetzen könnte.
    Ich habe über 3 Tabellen verteilt eine Art Ausbildungsstatus von Leuten stehen.
    Tabelle 1 enthält die Werkzeuge:
    tool_idtool_name
    150075
    250080
    350082

    insgesamt ca. 400 Einträge.

    Tabelle 2 enthält das Personal:
    personal_idfirstnamelastname
    1KlausMeier
    2RamonaWerner
    3ReneMüller

    auch hier ca. 300 Einträge.

    Und dann noch eine 3. Tabelle die Personal und Werkzeug verlinkt und den Ausbildungsstand dazu enthält
    matrix_idpersonal_idtool_idstatus
    1112
    2212
    3313
    4121
    5221


    Wir führen das aktuell als Excel-Dokument und das auf viele Arten unpraktisch.
    Das sind über 400 Werkzeuge und über 300 Mitarbeiter, das ganze über 2 oder 3 Tabellenblätter verteilt. Total unübersichtlich.
    So sieht das bspw. aus:


    Deswegen habe ich diese Matrix in obriges Datenbankmodel überführt.
    Soweit alles klein Problem.

    Ich möchte jetzt nur in einem DGV diese Matrix nachbilden. Da habe ich nun ein Problem.
    Personal mit Status auslesen, kein Problem. Werkzeuge auslesen und als Header ausgeben auch nicht.
    Aber nur getrennt.

    Ich habe jetzt nicht so wirklich eine Idee wie ich beides zusammen bekomme.



    Nimmt man jetzt obrige Beispiel-Tabellen, sollte das Ergebnis dann so aussehen:


    Könnt ihr mir helfen?

    Danke
    Da wirst du nicht drum rum kommen, das DGV "manuell" zu befüllen, anstatt mit einer DataTable als Datenquelle.
    Also zu erzeugst erst deine benötigten Header und holst dir dann in einer Schleife die Daten der Rows rein und erzeugst die. Dabei könntest du direkt her gehen und z.B. alle
    Zellinhalte mit "0" als "" anzeigen lassen, verbessert meiner Meinung nach die Übersicht.

    Problematisch wird's wenn die Anzahl deiner Header bzw. dessen Inhalt variabel ist. Denn zur Erstellung der Rows musst du wissen, was in den Headern drinne steht bzw. wofür die gedacht sind.

    EDIT: evtl. geht's auch anders. Kannst du ein Testprojekt bauen und anhängen? Man könnt ggf. aus deiner verlinkten Tabelle alle Daten sauber darstellen und mit einem "Mitarbeiter-Filter" arbeiten oder einen "Werkzeug-Filter"
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:
    Also zur Laufzeit soll der Inhalt nur dahin gehend variabel sein, als das man die Ausbildungswerte verändern kann.
    Werkzeuge und Personal kann dazu kommen. Aber nicht im Programm. Also der Header bleibt gleich solange das Programm offen ist.

    Hier das Testprojekt:
    Test_Matrix.zip

    Es ist eine Datenbank mit ein paar Einträgen enthalten

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

    Im Anhang mal "quick & dirty".
    Es werden die Daten aus der "Matrix"-Tabelle im DataGridView angezeigt. Ich habe keine einzige Zeile Code hinzufügen müssen.
    Allerdings hast du schon einen Fehler in deinem Datenmodell: Es sind keine Relationen vorhanden. Fügt man diese hinzu, dann kracht's

    So sieht das bei dir aktuell aus:


    So gehört's: (läuft wie oben beschrieben aber nicht, weil die Daten nicht zusammenpassen)


    So hätte ich's gemacht (vor allem die deutlich bessere Benamung!):


    D.h. die Tabelle "Matrix" bildet eine "Summe" aus den beiden anderen Tabellen. Bedeutet da muss alles drin landen.
    Du legst neues Personal an und evtl. neues Tool.

    Dann muss das in der Matrix-Tabelle zugeordnet werden. Neuer Eintrag: Personal auswählen, zugehöriges Tool

    Jo. Dann könnte man jetzt einen schönen Filter auf die BindingSource des DGV legen, dann kannste dir z.B. alle Tools eines Personals anzeigen lassen. Oder alle Personal, welches ein bestimmtes Tool hat.
    Ich mach da gleich mal was zum Testen fertig.


    EDIT: Anhang WindowsApp100.zip ist ohne Filter und ohne Codezeilen von mir, WindowsApp101.zip ist mit Filterung der BindingSource.
    Wenn die Relationen jetzt noch passen würden, dann könnte man noch einfacher und eleganter filtern. Aber ich denk als Grundgerüst und Denkansatz reicht das erstmal und du kannst das entsprechend ausarbeiten.
    Auch empfiehlt es sich erstmal nur mit einem DataSet zu arbeiten und die Datenbank später dranzuheften wenn alles läuft.
    Dateien
    • WindowsApp100.zip

      (532,5 kB, 105 mal heruntergeladen, zuletzt: )
    • WindowsApp101.zip

      (533,66 kB, 108 mal heruntergeladen, zuletzt: )
    "Na, wie ist das Wetter bei dir?"
    "Caps Lock."
    "Hä?"
    "Shift ohne Ende!" :thumbsup:

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

    wie gesagt: Standardmässig gibts da nix, da müssteste ziemlich kunstvoll was basteln.
    Alternativ zum "Matrix-View" könntest du einen "m:n-View" bauen - das gibts allerdings von der Stange.
    da hättest du links ein Grid, was alle Mitarbeiter anzeigt, und kannst einen auswählen. Rechts ein Grid, was dessen Werkzeuge + Ausbildungsstand anzeigt.
    Du siehst also nicht alle Ausbildungsstände auf einmal, sondern "nur" die Ausbildungsstände des angewählten Mitarbeiters.

    (Einen m:n-View kann man auach umgekehrt konstruieren: links alle Werkzeuge, und rechts alle Mitarbeiter, die an diesen ausgebildet sind.)
    gugge die vier Views auf Video , den m:n-View.
    (oder auch die anderen, schadet nix, auch das zu wissen, was noch damit zusammenhängt/möglich ist)
    Hallo,

    tragl schrieb:

    Allerdings hast du schon einen Fehler in deinem Datenmodell: Es sind keine Relationen vorhanden.

    Das war ja nur ein schnell zusammen geschustertes Beispiel. In der Original Tabelle sind die Relationen natürlich vorhanden.
    Ich wollte ja aber auch nicht die Werkzeuge untereinander in einer Liste. Das ist ja kein Problem.
    Ist ja im Grunde nichts anderes wie

    SQL-Abfrage

    1. SELECT * FROM personal
    2. INNER JOIN matrix ON matrix.personal_ID = personal.personal_ID
    3. INNER JOIN tool ON tool.tool_id = matrix.tool_ID


    Ich hätte aber gerne die Werkzeuge als Spaltenköpfe und die Mitarbeiter als Zeilen mit dem jeweiligen Status zum Werkzeug. Wie im letzten Bild in meinem ersten Post.

    VB.NET-Quellcode

    1. Using conn As New SqlConnection With {.ConnectionString = connStr}
    2. Try
    3. conn.Open()
    4. 'Alle Werkzeuge auslesen und in einer Liste speichern
    5. Using cmd As New SqlCommand With {.Connection = conn}
    6. cmd.CommandText = "SELECT tool_id, tool_name FROM tool;"
    7. Using reader As SqlDataReader = cmd.ExecuteReader
    8. If reader.HasRows Then
    9. While reader.Read
    10. Dim tool As New tools With {
    11. .tool_id = Convert.ToInt32(reader("tool_id")),
    12. .tool_name = Convert.ToString(reader("tool_name"))
    13. }
    14. listTools.Add(tool)
    15. End While
    16. End If
    17. End Using
    18. End Using
    19. If listTools.Count > 0 Then
    20. For Each tool In listTools
    21. 'Eine Spalte als WKZ-Name erzeugen
    22. '......
    23. DataGridView1.Columns.Add(tool.tool_id.ToString, tool.tool_name)
    24. Next
    25. End If
    26. Catch ex As Exception
    27. Finally
    28. conn.Close()
    29. End Try
    30. End Using


    Auf diese Weise hätte ich alle Werkzeuge als Spalten.
    Im nächsten Schritt würde ich dann das Personal mit dem entsprechenden Status auslesen.
    Nur weis ich jetzt nicht wie ich diese dann der "richtigen" Werkzeugspalte zuordnen kann.
    Das ist so ein bisschen mein Problem.

    Edit:
    Jetzt habe ich es soweit:


    Das sieht ja schon einmal sehr gut aus :)

    So habe ich das jetzt umgesetzt:

    VB.NET-Quellcode

    1. Using conn As New MySqlConnection With {.ConnectionString = connStr}
    2. Try
    3. conn.Open()
    4. 'Alle Werkzeuge auslesen und in einer Liste speichern
    5. Using cmd As New MySqlCommand With {.Connection = conn}
    6. cmd.CommandText = "SELECT tool_id, tool_name FROM tools;"
    7. Using reader As MySqlDataReader = cmd.ExecuteReader
    8. If reader.HasRows Then
    9. While reader.Read
    10. Dim tool As New tools With {
    11. .tool_id = Convert.ToInt32(reader("tool_id_id")),
    12. .tool_name = Convert.ToString(reader("tool_name"))
    13. }
    14. listTools.Add(tool)
    15. End While
    16. End If
    17. End Using
    18. End Using
    19. If listTools.Count > 0 Then
    20. UiDataGridView1.Columns.Add("worker_id", "Mitarbeiter")
    21. For Each tool In listTools
    22. 'Eine Spalte als WKZ-Name erzeugen
    23. '......
    24. UiDataGridView1.Columns.Add(tool.tool_id.ToString, tool.tool_name)
    25. Next
    26. Using cmd As New MySqlCommand With {.Connection = conn}
    27. cmd.CommandText = "SELECT personal_id,matrix.tool_id,tools.tool_name,status FROM matrix INNER JOIN tools ON tools.tool_id=matrix.tool_id;"
    28. Using reader As MySqlDataReader = cmd.ExecuteReader
    29. If reader.HasRows Then
    30. Dim rowID As Integer = -1
    31. Dim columnID As Integer = -1
    32. While reader.Read
    33. Dim workerID As Integer = Convert.ToInt32(reader("personal_id"))
    34. Dim tool_id As Integer = Convert.ToInt32(reader("tool_id"))
    35. Dim tool_name As String = reader("tool_name").ToString
    36. Dim status As String = reader("status").ToString
    37. Dim isAvailable As Boolean = False
    38. For Each row As DataGridViewRow In UiDataGridView1.Rows
    39. If Convert.ToInt32(row.Cells("worker_id").Value) = workerID Then
    40. 'In dieser Zeile das Werkzeug suchen
    41. Debug.WriteLine("WerkzeugID " & tool_id.ToString & " für Werker " & workerID.ToString & " mit Status " & status & " hinzufügen")
    42. rowID = row.Index + 1
    43. isAvailable = True
    44. Exit For
    45. End If
    46. Next
    47. 'Wurde die Zeile gefunden?
    48. 'Wenn nicht, müssen wir eine neue einfügen
    49. If isAvailable = False Then
    50. Dim newRow As New DataGridViewRow
    51. newRow.CreateCells(UiDataGridView1)
    52. newRow.Cells(0).Value = workerID
    53. UiDataGridView1.Rows.Add(newRow)
    54. rowID = UiDataGridView1.Rows.Count - 1
    55. End If
    56. If rowID >= 0 Then
    57. UiDataGridView1.Item(tool_id.ToString, rowID - 1).Value = status
    58. Select Case status
    59. Case "1"
    60. UiDataGridView1.Rows(rowID - 1).Cells(tool_id.ToString).Style.BackColor = Color.White
    61. Case "2"
    62. UiDataGridView1.Rows(rowID - 1).Cells(tool_id.ToString).Style.BackColor = Color.Yellow
    63. Case "3"
    64. UiDataGridView1.Rows(rowID - 1).Cells(tool_id.ToString).Style.BackColor = Color.Green
    65. End Select
    66. rowID = -1
    67. End If
    68. End While
    69. End If
    70. End Using
    71. End Using
    72. End If
    73. Catch ex As Exception
    74. Debug.WriteLine(ex.Message)
    75. End Try
    76. End Using


    Gibt es dazu Meinungen, Verbesserungsvorschläge?

    Gruß

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

    Hallo,
    ich habe mal ein Beispielprojekt angehängt, wie man es machen könnte und dient nur als Idee. Arbeitet allerdings nicht mit DataBinding. Das Erstellen des DataGrids findet in Form1.cs statt. Evtl. kannst du das für dich adaptieren.
    Hinweis: Das Projekt ist .NET 7, verwendet Entity Framework und eine SQLite-Datenbank. Du musst die Anwendung prinzipiell nur starten. Meine Demo-Daten entsprechen grob deinen Musterdaten aus Post #1, nur deinen Status habe ich als Boolean implementiert (aus Einfachheit für mich).
    Edit: Zu Spät :D . Naja...
    Dateien
    • MatrixDemo.zip

      (8,33 kB, 106 mal heruntergeladen, zuletzt: )
    Hallo,

    ich habe die Matrix jetzt in einer Dataview, welche ich an das Datagridview gebunden habe.
    Dadurch kann ich im Dataview jetzt auch suchen und bspw. nach Nachnamen filtern

    VB.NET-Quellcode

    1. Private Sub _ParentForm_FilterByName(name As String) Handles _ParentForm.FilterByName
    2. ShowWaitForm("Ausbildungsmatrix wird aktualisiert, bitte warten.")
    3. _dvMatrix.RowFilter = String.Concat("worker_Lastname LIKE '", name, "%'")
    4. HideWaitForm()
    5. End Sub



    Weis jemand ob es auch möglich ist nach den Spalten, also hier die Werkzeugnummern zu filtern?
    Also wenn ich jetzt nach Nachname "Mü" und Werkzeug "50490" suche, er mir alle Müllers und nur den Status am Werkzeug "50490" anzeigt?

    Gruß

    Montoyafan schrieb:

    Weis jemand ob es auch möglich ist nach den Spalten, also hier die Werkzeugnummern zu filtern?

    Kasi schrieb:

    dein Filter mit "AND" erweitern.

    Das wird nicht gehen. Er will ja Spalten wegfiltern.

    ErfinderDesRades schrieb:

    wie gesagt: Standardmässig gibts da nix, da müssteste ziemlich kunstvoll was basteln.
    kannste iwie alle DGV-Spalten durchlaufen, und bei denen, die du nicht sehen willst, Width=0 einstellen oder sowas.

    Ansonsten ist sone Ansicht, wo zu einem (oder mehreren) Namen der Status nur eines Werkzeugs steht, genau ein m:n-View, wie es ihn auch von der Stange gibt.
    Ja genau, das geht so natürlich nicht.
    Ist ja ein ".RowFilter" ;)

    Ja, bleibt wohl nur alle Spalten durchlaufen uns ausblenden.

    VB.NET-Quellcode

    1. Private Sub _ParentForm_FilterTool(tool As String) Handles _ParentForm.FilterTool
    2. For Each column As DataGridViewColumn In UiDataGridView1.Columns
    3. If Not column.Name.StartsWith(tool) Then
    4. column.Visible = False
    5. End If
    6. Next
    7. UiDataGridView1.Columns.Item("worker_Firstname").Visible = True
    8. UiDataGridView1.Columns.Item("worker_Lastname").Visible = True
    9. End Sub


    Danke