Drag&Drop Rows in einem an ein DataSet gebundenes DatagridView

  • VB.NET
  • .NET (FX) 4.0

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von FormFollowsFunction.

    Drag&Drop Rows in einem an ein DataSet gebundenes DatagridView

    Nabend.

    Ich versuche, in einem an ein DataSet gebundenem DataGridView (Custom Control), Drag&Drop von Rows zu realisieren.
    Leider will es mir nicht gelingen. :(
    Das ganze soll wenn möglich so gestalltet sein, daß nacher am DataGridView nicht mer rumeditiert werden muss,
    es soll also alles innerhalb des Controls von statten gehen.
    Gut währe es auch, wenn ich über eine Property, eine DataTable angeben könnte.

    Die Update Methode sieht jetzt so aus:

    VB.NET-Quellcode

    1. Private Sub Update(ByVal _removeAt As Integer, ByVal _dropIndex As Integer)
    2. 'Bis jetzt am vielversprechensten (Keine Fehlermeldung), allerdings gehen die daten verloren. :(
    3. Dim dataTable As DataTable = DirectCast(DirectCast(DataSource, BindingSource).DataSource, DataSet).Tables().Item(0)
    4. With dataTable
    5. Dim tempDataRow As DataRow = .Rows(_removeAt)
    6. .Rows(_removeAt).Delete()
    7. .Rows.InsertAt(tempDataRow, _dropIndex)
    8. End With
    9. ' Währe ja zu schöhn gewesen. :)
    10. 'DirectCast(DataSource, BindingSource).RemoveAt(_removeAt)
    11. 'DirectCast(DataSource, BindingSource).Insert(_dragIndex, _dataBoundItem)
    12. End Sub


    Ganze Drag&Drop Routine

    VB.NET-Quellcode

    1. Partial Class DataGridViewDragAndDrop
    2. Private Sub Me_MouseDown(sender As Object, e As MouseEventArgs) Handles Me.MouseDown
    3. ButtonPressed = e.Button = MouseButtons.Right
    4. End Sub
    5. Private Sub Me_MouseMove(sender As Object, e As MouseEventArgs) Handles Me.MouseMove
    6. If Not DragAndDrop OrElse AllRowsSelected Then Exit Sub
    7. If ButtonPressed Then
    8. If MouseDownLocation = Point.Empty Then
    9. MouseDownLocation = e.Location
    10. Exit Sub
    11. End If
    12. End If
    13. If ButtonPressed AndAlso MouseDownLocation <> Point.Empty Then
    14. Dim hitTestInfo As HitTestInfo = HitTest(e.X, e.Y)
    15. If hitTestInfo IsNot Nothing AndAlso
    16. Rows(hitTestInfo.RowIndex).Selected Then
    17. DragIndex = hitTestInfo.RowIndex
    18. If Rows(DragIndex).IsNewRow Then Exit Sub
    19. If MultiSelect Then
    20. DoDragDrop(SelectedRows, DragDropEffects.Move)
    21. Else
    22. DoDragDrop(Rows(DragIndex), DragDropEffects.Move)
    23. End If
    24. End If
    25. End If
    26. End Sub
    27. Private Sub Me_DragOver(sender As Object, e As DragEventArgs) Handles Me.DragOver
    28. e.Effect = DragDropEffects.Move
    29. End Sub
    30. Private Sub Me_MouseUp(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
    31. If ButtonPressed AndAlso e.Button = MouseButtons.Right Then
    32. ButtonPressed = False
    33. End If
    34. End Sub
    35. Private Sub Me_DragDrop(sender As Object, e As DragEventArgs) Handles Me.DragDrop
    36. Dim clientPoint As Point = PointToClient(New Point(e.X, e.Y))
    37. Dim dropIndex As Integer = HitTest(clientPoint.X, clientPoint.Y).RowIndex
    38. If e.Effect = DragDropEffects.Move Then
    39. If MultiSelect Then
    40. Dim dragRows As DataGridViewSelectedRowCollection =
    41. DirectCast(e.Data.GetData(GetType(DataGridViewSelectedRowCollection)), DataGridViewSelectedRowCollection)
    42. For i As Integer = dragRows.Count - 1 To 0 Step -1
    43. Dim dragRow As DataGridViewRow = dragRows(i)
    44. Update(dragRow.Index, dropIndex)
    45. Rows(dropIndex).Selected = True
    46. Next
    47. Rows(DragIndex).Selected = False
    48. Else
    49. Dim dropRow As DataGridViewRow = DirectCast(e.Data.GetData(GetType(DataGridViewRow)), DataGridViewRow)
    50. Update(dropRow.Index, dropIndex)
    51. Rows(dropIndex).Selected = True
    52. End If
    53. ButtonPressed = False
    54. MouseDownLocation = Point.Empty
    55. End If
    56. End Sub
    57. Private Sub Update(ByVal _removeAt As Integer, ByVal _dropIndex As Integer)
    58. 'Bis jetzt am vielversprechensten (Keine Fehlermeldung), allerdings gehen die daten verloren. :(
    59. Dim dataTable As DataTable = DirectCast(DirectCast(DataSource, BindingSource).DataSource, DataSet).Tables().Item(0)
    60. With dataTable
    61. Dim tempDataRow As DataRow = .Rows(_removeAt)
    62. .Rows(_removeAt).Delete()
    63. .Rows.InsertAt(tempDataRow, _dropIndex)
    64. End With
    65. ' Währe ja zu schöhn gewesen. :)
    66. 'DirectCast(DataSource, BindingSource).RemoveAt(_removeAt)
    67. 'DirectCast(DataSource, BindingSource).Insert(_dragIndex, _dataBoundItem)
    68. End Sub
    69. End Class


    Bin ich total auf dem Holzweg ?
    Dateien
    Holzhammer, aber möglich:

    VB.NET-Quellcode

    1. Private Sub Update(ByVal _removeAt As Integer, ByVal _dropIndex As Integer)
    2. 'Bis jetzt am vielversprechensten (Keine Fehlermeldung), allerdings gehen die daten verloren. :(
    3. Dim dataTable As DataTable = DirectCast(DirectCast(DataSource, BindingSource).DataSource, DataSet).Tables().Item(0)
    4. With dataTable
    5. Dim DS As DataSetMain = DirectCast(DirectCast(DirectCast(DataSource, BindingSource).DataSource, DataSet), DataSetMain)
    6. Dim TempRow As DataSetMain.DataTableMainRow = DS.DataTableMain.NewDataTableMainRow
    7. Dim tempDataRow As DataSetMain.DataTableMainRow = DirectCast(.Rows(_removeAt), DataSetMain.DataTableMainRow)
    8. TempRow.DataColumn1 = tempDataRow.DataColumn1
    9. TempRow.DataColumn2 = tempDataRow.DataColumn2
    10. TempRow.DataColumn3 = tempDataRow.DataColumn3
    11. .Rows(_removeAt).Delete()
    12. .Rows.InsertAt(TempRow, _dropIndex)
    13. End With

    Das Delete macht die Row unbenutzbar. Daher eine Kopie anfertigen, die nach Ableben des Originals noch nutzbar ist.
    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.
    @FormFollowsFunction Weiß nicht ob das auf deinen Anwendungsfall anwendbar ist...

    Ich arbeite meist mit Objektlisten (List (Of T)), welche als Datasource an eine DataGridView gebunden sind. Diese Listen sind mittels Index (0 basiert) sortiert. Möchte ich nun einen Datensatz verschieben, so ändere ich den Index (= Property des Objekts) des zu verschiebenden und des nachfolgenden Objekts. Danach sortiere ich meine Liste neu (nach Index) und aktualisiere das DataGridView.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    @ErfinderDesRades: Doch, macht es. Hat etwas gedauert, bis ich rausfand, dass etwas uninstinktiv nach dem Einfügen mehrerer Rows dann innerhalb des DGVs die Reihenfolge duch DnD geändert werden soll, indem die rechte Maustaste benutzt wird.
    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
    Klasse, dat funzt ! :thumbsup:

    Habe das ganze noch etwas eingedampft:

    VB.NET-Quellcode

    1. Private Sub UpdateDataSource(ByVal _removeAt As Integer, ByVal _dropIndex As Integer)
    2. With DirectCast(DirectCast(DataSource, BindingSource).DataSource, DataSetMain).DataTableMain
    3. Dim originalDataRow As DataSetMain.DataTableMainRow = DirectCast(.Rows(_removeAt), DataSetMain.DataTableMainRow)
    4. Dim newDataRow As DataSetMain.DataTableMainRow = .NewDataTableMainRow
    5. With newDataRow
    6. For i As Integer = 0 To .ItemArray.Count - 1
    7. .Item(i) = originalDataRow.Item(i)
    8. .Item(i) = originalDataRow.Item(i)
    9. .Item(i) = originalDataRow.Item(i)
    10. Next
    11. End With
    12. .Rows(_removeAt).Delete()
    13. .Rows.InsertAt(newDataRow, _dropIndex)
    14. End With
    15. End Sub

    Es generisch zu gestallten, so daß bei weitere Verwendung, nicht mer am Control rumeditiert werden muß, geht fermutlich nicht.
    Ist aber ok so, man kann nicht alles haben.

    @ErfinderDesRades
    DataRowCollection.Remove() werde ich auch noch ausprobieren und bescheit geben, wenn es funktioniert.

    @mrMo
    Mit einer List(Of T) hatte ich es schon realisiert, das hat auch wunderbar funktioniert.
    Ich will aber auch noch eine Detailnsicht einbauen, daher das DataSet.

    Danke an alle !
    Die Zeilen#8 und #9 können weg ;)
    Aber welches Control meinst Du? Die BindingSource?
    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.

    FormFollowsFunction schrieb:

    DataRowCollection.Remove() werde ich auch noch ausprobieren und bescheit geben, wenn es funktioniert.
    jo, bitte - meine Hoffnung wäre, dass Zeilen #5 - 10 wegkönnten

    Weiters kriegst du ja Probleme mit .Delete(), wenn untergeordnete Datensätze existieren.
    (Na, wenn du diese Probleme nicht sowieso kriegst)
    Ich weiß, not my job/business, aber ich hab's probiert. Leider wird auch mit Remove der Row-Inhalt unbrauchbar.
    Bilder
    • RemovingRows.png

      57,34 kB, 644×455, 174 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.
    Ich hab jetzt diesen üblen Hack verzapft:

    VB.NET-Quellcode

    1. Private Sub Update(ByVal _removeAt As Integer, ByVal _dropIndex As Integer)
    2. Dim dataTable As DataTable = DirectCast(DirectCast(DataSource, BindingSource).DataSource, DataSet).Tables().Item(0)
    3. Dim tempDataRow As DataRow = dataTable.Rows(_removeAt)
    4. Dim rwNew = dataTable.NewRow
    5. rwNew.ItemArray = tempDataRow.ItemArray
    6. dataTable.Rows.RemoveAt(_removeAt)
    7. dataTable.Rows.InsertAt(rwNew, _dropIndex)
    8. End Sub
    Wie gesagt: Mit AutoIncrement-PrimKeys und mit ChildRows wirds vermutlich Ärger geben.

    Vielleicht ist einfacher, mit Reflection die DataRowCollection zu hacken, und ihre Reihenfolge durcheinanderzubringen.
    Irgendwo tief im Herzen ist jede Auflistung eigentlich ja doch ein Array.

    FormFollowsFunction schrieb:

    Es generisch zu gestallten, so daß bei weitere Verwendung, nicht mer am Control rumeditiert werden muß, geht fermutlich nicht.
    hABE da nun so einen, generischen, LösungsAnsatz gebastelt.
    Die Rows verändern nicht ihre Position, sondern tauschen stattdessen ihr ItemArray - also ihre Roh-Daten:

    VB.NET-Quellcode

    1. Private Shared Sub SwapRows(twoRows() As DataRow)
    2. Dim rw0 = twoRows(0)
    3. Dim rw1 = twoRows(1)
    4. Dim itms = rw0.ItemArray
    5. 'während des Tauschens müssen die ChildRelations auf UpdateRule.None stehen
    6. Dim ruleStore = Aggregate rel In rw0.Table.ChildRelations.Cast(Of DataRelation) Select rel, rel.ChildKeyConstraint.UpdateRule Into ToArray
    7. ruleStore.ForEach(Sub(inf) inf.rel.ChildKeyConstraint.UpdateRule = Rule.None)
    8. rw0.Table.DataSet.EnforceConstraints = False
    9. rw0.ItemArray = rw1.ItemArray
    10. rw1.ItemArray = itms
    11. rw0.Table.DataSet.EnforceConstraints = True
    12. ruleStore.ForEach(Sub(inf) inf.rel.ChildKeyConstraint.UpdateRule = inf.UpdateRule) ' UpdateRule restaurieren
    13. End Sub

    Dateien

    VaporiZed schrieb:

    Die Zeilen#8 und #9 können weg

    Hi hi, ja logisch. :S

    VaporiZed schrieb:

    Aber welches Control meinst Du?

    Das DataGridView.

    Daß .Delete() und .Remove() das gleiche ergeben, habe ich auch festgesellt, aber danke für deinen Vorstoß.

    ErfinderDesRades schrieb:

    meine Hoffnung wäre, dass Zeilen #5 - 10 wegkönnten

    Hi hi, ja logisch. :S

    Danke auch dir, für die Mühe, werde das mal einbauen.