File this under "I can't believe this was so hard".
I have a requirement to allow the user to swap two rows in an Infragistics XamDataGrid. The grid is bound to a data table so I have to swap two data rows in the data table. This is generic code so I cannot make any assumptions about the data. I thought this would be trivial until I had to do this on a grid with child layouts. The child rows were getting disconnected from the parent.
There is no Move method on a data row or data table. You have to delete and insert. But as soon as you remove a row from a data table it changes to an unattached state and all its item data is lost. The same happens to all of its child records. So before removing the row, you have to store off all its data and all its child data.
It gets complicated. Here's my solution.
Protected Function MoveDataRow(OldRow As DataRow, Delta As Integer) As DataRow
Dim dt As DataTable= OldRow.Table
Dim newRow As DataRow = dt.NewRow()
Dim oldRowState As DataRowState = OldRow.RowState
Dim ChildRows As New Dictionary(Of DataRelation, List(Of cRow))()
Dim InsertAt As Integer = dt.Rows.IndexOf(OldRow) + Delta
For Each dataRelation As DataRelation In dt.DataSet.Relations
If dataRelation.ParentTable.Equals(dt) Then
ChildRows.Add(dataRelation, New List(Of cRow))
For Each childDataRow As DataRow In OldRow.GetChildRows(dataRelation)
ChildRows(dataRelation).Add(New cRow(childDataRow))
dataRelation.ChildTable.Rows.Remove(childDataRow)
Next
End If
Next
newRow.ItemArray = OldRow.ItemArray
dt.Rows.Remove(OldRow)
dt.Rows.InsertAt(newRow, InsertAt)
newRow.AcceptChanges()
Select Case oldRowState
Case DataRowState.Added : newRow.SetAdded()
Case DataRowState.Modified : newRow.SetModified()
End Select
For Each kvp As KeyValuePair(Of DataRelation, List(Of cRow)) In ChildRows
For Each row As cRow In kvp.Value
Dim newChildRow As DataRow = kvp.Key.ChildTable.NewRow()
newChildRow.ItemArray = row.ItemArray
kvp.Key.ChildTable.Rows.Add(newChildRow)
newChildRow.AcceptChanges()
Select Case row.RowState
Case DataRowState.Added : newChildRow.SetAdded()
Case DataRowState.Modified : newChildRow.SetModified()
End Select
Next
Next
Return newRow
End Function
Private Class cRow
Public Sub New(row As DataRow)
Me.ItemArray = row.ItemArray
Me.RowState = row.RowState
End Sub
Public ItemArray As Object()
Public RowState As DataRowState
End Class
You probably notice some repetition of code and that this only handles one level of child row. It's ripe for recursion. Please, feel free to do that for me 😀
If you have a better algorithm, please let me know. Surely it doesn't have to be this complicated!