Index einer DataGridView.Row anhand der DataTable.DataRow ermitteln

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

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von VB1963.

    Index einer DataGridView.Row anhand der DataTable.DataRow ermitteln

    Hallo

    ich habe ein DataGridView, dass als DataSource eine DataTable hat. Nun füge ich der DataTable eine neue DataRow hinzu.
    Wie kann ich anhand der neuen DataRow herausfinden, welcher Index die neue DataRow im DataGridView hat?

    dt.Rows.IndexOf(newRow) gibt mir ja den neuen Index in der DataTable zurück, dieser stimmt ja aber nicht mit dem Index des DataGridView überein.
    @DanCooper Wie fügst Du denn die neue Zeile hinzu?
    Bei einem Append ist es einfach die alte Zeilenanzahl, bei einem Insert musst Du den Index eh angeben.
    Wenn Du sortierst ist es danach meist anders.
    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!
    @RodFromGermany
    Ich füge sie mit dt.Rows.Add(newRow) hinzu (vollständiger Code unten). Da der DataGridView aber über diverse Filter und Sortierungen verfügt, kann ich nicht ohne weiteres den Index ermitteln. Es könnte z.B. auch durchaus sein, dass der Eintrag im DataGridView gar nicht auftaucht, da er rausgefiltert worden ist.

    Der DataGridView wird so definiert:

    VB.NET-Quellcode

    1. Private dtMovieSets As New DataTable
    2. Private bsMovieSets As New BindingSource
    3. Private dgvMovieSets As New DataGridView
    4. 'dtMovieSets = Abfrage aus der Datenbank
    5. bsMovieSets.DataSource = dtMovieSets
    6. dgvMovieSets.DataSource = bsMovieSets


    VB.NET-Quellcode

    1. Private Function AddRow_MovieSet(ByVal lngID As Long) As Integer
    2. If lngID = -1 Then Return -1
    3. Dim myDelegate As New Delegate_dtListAddRow(AddressOf dtListAddRow)
    4. Dim newRow As DataRow = Nothing
    5. Dim newTable As New DataTable
    6. Master.DB.FillDataTable(newTable, String.Format("SELECT * FROM setslist WHERE idSet={0}", lngID))
    7. If newTable.Rows.Count = 1 Then
    8. newRow = newTable.Rows.Item(0)
    9. End If
    10. Dim dRow = dtMovieSets.NewRow()
    11. dRow.ItemArray = newRow.ItemArray
    12. If newRow IsNot Nothing Then
    13. RemoveHandler dgvMovieSets.CellEnter, AddressOf dgvMovieSets_CellEnter
    14. If InvokeRequired Then
    15. Invoke(myDelegate, New Object() {dtMovieSets, dRow})
    16. Else
    17. dtMovieSets.Rows.Add(dRow)
    18. End If
    19. AddHandler dgvMovieSets.CellEnter, AddressOf dgvMovieSets_CellEnter
    20. 'Return Index der neuen Row im DGV
    21. End If
    22. Return -1
    23. End Function


    Zitat entfernt. ~Thunderbolt

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

    DanCooper schrieb:

    da er rausgefiltert worden ist
    Sorry, da muss ich das Handtuch werfen, das ist nicht mein Ding. :/
    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!

    RodFromGermany schrieb:

    das Handtuch
    nehme ich erst garnet - der gezeigte Code ist so verworren, das kann man nicht verbessern, das muss man neu machen.
    Also um im DGV eine Zeile mehr zu haben muss man keine komplette neue DataTable erzeugen, und auch keine Datenbank-Abfrage abfahren.

    Und die Filter sind übrigens nicht auffm DGV drauf, sondern auf der BindingSource. Klingt wie Klugsch..., aber wenn man den Unterschied nicht macht, führt das dazu, dass am Ende am Dgv herumgepuhlt wird, was die BindingSource eiglich auffm Teller präsentiert.
    @ErfinderDesRades
    Ja, die Filter sind auf der BindingSource, da hast du recht. Das habe ich falsch beschrieben.

    Aber was soll am Code verworren sein? Ist doch alles ganz einfach. Es wird auch keine neue DataTable für das DGV erzeugt, sondern nur eine temporäre, damit ich mit der vorhanden Funktion "FillDataTable" den neuen, bereits in der DB vorhandenen Eintrag für die angegebene lngID erhalte.
    Der Vorgang ist:
    • newTable wird mit einem Datensatz aus der DB gefüllt
    • newRow übernimmt den einzigen Eintrag aus newTable
    • mit dRow wird eine neue Row in dtMovieSets erstellt
    • dRow übernimmt die Items aus newRow
    • danach wird die dRow in die dtMovieSets gespeichert

    Vielleicht kann man ein zwei Schritte kürzen, für die Überprüfung der einzelnen Schritte ist die aktuelle Lösung aber ganz gut.
    Für die Beantwortung meiner Frage ist es aber auch gar nicht relevant, ob man den Vorgang optimieren könnte...

    Meine Frage lautet immer noch: wie kann anhand einer DataRow in der DataTable, welche als Quelle der BindingSource dient, den verknüpften Eintrag im DGV suchen?


    @VB1963
    ...stimmt, wobei es scheint, er überhaupt
    keine BS zwischengeschaltet hat und das eigentlich sein Problem ist
    (warum er da einen Zeilenindex braucht ?)...[/quote]

    Doch, steht doch da:

    VB.NET-Quellcode

    1. Private dtMovieSets As New DataTable
    2. Private bsMovieSets As New BindingSource
    3. Private dgvMovieSets As New DataGridView
    4. 'dtMovieSets = Abfrage aus der Datenbank
    5. bsMovieSets.DataSource = dtMovieSets
    6. dgvMovieSets.DataSource = bsMovieSets


    Zitate entfernt. ~Thunderbolt

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

    noch einmal: Lass doch das arme DGV in Frieden, und suche deine Daten in der BindingSource.
    Ist etwas lausig, da musste mit einer For i - Schleife alle DataRowViews der BindingSource angucken, ob die Row darinnen den gewünschten Primkey aufweist.
    Aber im Dgv drin rumzupuhlen ist noch lausiger.



    Ich finde du hast da auch ein Benamungs-Problem - "AddRow" sagt zuwenig darüber aus, was die Methode eiglich macht.
    Die Methode added ja nur eine Row, wenn sie in der Db eine Vorlage findet, die sie quasi klonen kann.

    Das ist schon ein sehr eigentümliches Vorgehen - wieso ist das erforderlich?


    Mal abgesehen davon, dass du deine Anwendung für Sql-Injection -Angriffe zur Verfügung stellst.
    Diese Funktion ist ein Teil des GUI. Dort werden eingentlich hauptsächlich die DGVs aktualisiert oder irgendwelche Controls verändert. Deshalb reicht hier (zumindest für mich) die Benennung "AddRow_Contenttype".

    Grund für das eigentümliche Vorgehen ist, dass ich grundsätzlich nicht bei jedem Hinzufügen, Editieren oder Löschen von DB Einträgen die ganze DataTable neu laden will. Viele Änderungen an der DB werden ausserhalb der GUI durch andere Module und Klassen vorgenommen, die dann nur ein Trigger mit ID und entsprechender Content-Angabe an das GUI senden. Das GUI aktualisiert dann entsprechende DGVs wenn nötig.

    Ich will auch gar nicht im DGV rumspulen. Was ich in diesem Fall aber benötige, ist der Index im DGV, nachdem ich einen neuen Eintrag der DataTable hinzugefügt habe. Ziel der Sache ist es, dass der Eintrag im DGV nach dem Hinzufügen automatisch selektiert wird. Das soll aber auch nur bei genau in einem Fall geschehen. Dafür benötige ich aber erstmal den Index. Ich möchte wenn möglich nicht Funktionen wie dgv.RowAdded usw. benutzen.

    Was wird als Primekey bezeichnet?

    ErfinderDesRades schrieb:

    DanCooper schrieb:

    Eigentlich ziemlich einfach, so den Eintrag in der DGV zu finden, wenn ich so darüber nachdenke
    Du meinst natürlich, den Eintrag in der BindingSource zu finden, nicht im DGV.
    /Klugsch...


    Äääähmm... nein!? Ich verstehe nicht, wieso ich das in der BindingSource suchen soll, wenn ich doch den Index im DGV benötige. Stehe ich hier auf dem Schlauch? Der Code siehts nun so aus:

    VB.NET-Quellcode

    1. Private Sub cmnuMovieSetNew_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles cmnuMovieSetNew.Click
    2. dgvMovieSets.ClearSelection()
    3. ClearInfo()
    4. Dim tmpDBMovieSet = New Database.DBElement(Enums.ContentType.MovieSet) With {.MovieSet = New MediaContainers.MovieSet}
    5. Using dNewSet As New dlgNewSet()
    6. If dNewSet.ShowDialog(tmpDBMovieSet) = DialogResult.OK Then
    7. tmpDBMovieSet = Master.DB.Save_MovieSet(dNewSet.Result, False, False)
    8. Dim iNewRowIndex = AddRow_MovieSet(tmpDBMovieSet.ID)
    9. If Not iNewRowIndex = -1 Then
    10. dgvMovieSets.Rows(iNewRowIndex).Selected = True
    11. End If
    12. Edit_MovieSet(tmpDBMovieSet)
    13. End If
    14. End Using
    15. End Sub
    16. Private Function AddRow_MovieSet(ByVal lngID As Long) As Integer
    17. If lngID = -1 Then Return -1
    18. Dim myDelegate As New Delegate_dtListAddRow(AddressOf dtListAddRow)
    19. Dim newRow As DataRow = Nothing
    20. Dim newTable As New DataTable
    21. Master.DB.FillDataTable(newTable, String.Format("SELECT * FROM setslist WHERE idSet={0}", lngID))
    22. If newTable.Rows.Count = 1 Then
    23. newRow = newTable.Rows.Item(0)
    24. End If
    25. Dim dRow = dtMovieSets.NewRow()
    26. dRow.ItemArray = newRow.ItemArray
    27. If newRow IsNot Nothing Then
    28. RemoveHandler dgvMovieSets.CellEnter, AddressOf dgvMovieSets_CellEnter
    29. If InvokeRequired Then
    30. Invoke(myDelegate, New Object() {dtMovieSets, dRow})
    31. Else
    32. dtMovieSets.Rows.Add(dRow)
    33. End If
    34. AddHandler dgvMovieSets.CellEnter, AddressOf dgvMovieSets_CellEnter
    35. currRow_MovieSet = -1
    36. For Each drvRow As DataGridViewRow In dgvMovieSets.Rows
    37. If CLng(drvRow.Cells("idSet").Value) = lngID Then
    38. Return drvRow.Index
    39. End If
    40. Next
    41. End If
    42. Return -1
    43. End Function
    ich glaub mir dämmert der Sinn hinter diesem paradoxen Vorgehen:
    du hast einen Dialog, der das MovieSet anlegen sollst, aber da du kein formübergreifendes Databinding hinkriegst, machst du den RoundTrip, dass der Dialog den Datensatz in die Db schreibt, und das MainForm den Datensatz von der Db wieder abrufen muss.

    Jo, wie wolle - jdfs. prinzipiell ginge das auch einfacher.



    Ja, und der Index der BindingSource ist identisch mit dem Index des DGVs. Nur täte ich empfehlen, sich an die BindingSource zu halten, denn da kannst du unabhängig von was für ein Control dran hängt denselben Code schreiben.

    ErfinderDesRades schrieb:

    ich glaub mir dämmert der Sinn hinter diesem paradoxen Vorgehen:
    du hast einen Dialog, der das MovieSet anlegen sollst, aber da du kein formübergreifendes Databinding hinkriegst, machst du den RoundTrip, dass der Dialog den Datensatz in die Db schreibt, und das MainForm den Datensatz von der Db wieder abrufen muss.

    Jo, wie wolle - jdfs. prinzipiell ginge das auch einfacher.



    Ja, und der Index der BindingSource ist identisch mit dem Index des DGVs. Nur täte ich empfehlen, sich an die BindingSource zu halten, denn da kannst du unabhängig von was für ein Control dran hängt denselben Code schreiben.


    Die Klasse Database.DBElement beinhaltet alle Properties, die für das Handling der Movies/MovieSets/TVShows/Seasons/Episodes nötig sind. Grundsätzlich schiebe ich nur diese Klasse durch alle Funktionen. Im Gegensatz zu Filmen, die lokal als Videodatei vorliegen und beim Einlesen in die Datenbank eingefügt werden, sind MovieSets (z.B. "Herr der Ringe Collection") ja nur "virtuelle" Einträge ohne lokale Daten. Alle "Edit" Dialog sind darauf aus, dass ein bereits existierendes DBElement (mit Primarykey) bearbeitet wird. Deshalb ist das manuelle Erstellen eines MovieSets ein Spezialfall, denn dort würde der Edit Dialog gezeigt werden, bevor ein DB-Eintrag besteht. Deshalb der Umweg über einen, einfach gesagt, Inputbox-Dialog, der erstmal ein leeres MovieSet nur mit Titel anlegt. Danach wird das MovieSet ganz normal im Edit Dialog geladen und unterscheidet sich im Handling nicht mehr von der normalen Vorgehensweise.




    Danke, das war mir nicht bewusst. Ich habs nun die Schleife gelöscht und nutze am Schluss einfach das hier (geht in der Tat viel einfacher):

    VB.NET-Quellcode

    1. Return bsMovieSets.Find("idSet", lngID)