Descrizione :
Una tecnica valida per modificare l'indice di una o più righe in un DataTable / DataGridView, mantenendo la possibilità di usare l'ordinamento manuale sui ColumnHeaders.
+ Articolo :
Caso singolo : per completezza ho lasciato anche il caso di spostamento di una singola Row, dal suo indice di partenza x all'indice arbitrario desiderato y.
Caso generico : spostare N Rows in DataTable / DGV dagli indici di partenza ( x1, x2, ..., xn ) ai nuovi indici arbitrari ( y1, y2, ..., yn ).
La corrispondenza per ciascuna Row in gioco è chiaramente :
x1, y1
x2, y2
..., ...
xn, yn
Per fare ciò definisco un insieme ordinabile di coppie di valori Integer, in cui il primo valore ( Key ) è xi ( indice iniziale della Row ), e il secondo valore ( Value ) è yi ( indice arbitrario di destinazione della Row ). Ho scelto un Dictionary.
Per replicare l'esempio seguente basta :
- 1 Form "FormMain"
- 1 DataGridView "DGV"
- 1 Button "cmd_movesinglerow"
- 1 Button "cmd_movemultirows"
--> Codice FormMain :
Public Class FormMain Private DT As New DataTable Private Sub FormMain_Load(sender As System.Object, e As System.EventArgs) Handles MyBase.Load 'Colonna PK DT.Columns.Add("ID") DT.PrimaryKey = New DataColumn() {DT.Columns("ID")} 'In VB 2010 può essere : 'DT.PrimaryKey = {DT.Columns("ID")} 'Altre Colonne DT.Columns.Add("Cognome") DT.Columns.Add("Nome") '... 'Inserimento Rows For i As Integer = 0 To 9 DT.Rows.Add(i, "Cognome_" & i, "Nome_" & i) Next DGV.DataSource = DT 'Disabilita l'ordinamento manuale e lo rende attivo solo via codice For i As Integer = 0 To DGV.Columns.Count - 1 DGV.Columns(i).SortMode = DataGridViewColumnSortMode.Programmatic Next End Sub Private Sub DGV_ColumnHeaderMouseClick(sender As Object, e As System.Windows.Forms.DataGridViewCellMouseEventArgs) Handles DGV.ColumnHeaderMouseClick If DGV.Columns(e.ColumnIndex).HeaderCell.SortGlyphDirection = SortOrder.Ascending Then DGV.Sort(DGV.Columns(e.ColumnIndex), System.ComponentModel.ListSortDirection.Descending) DT.DefaultView.Sort = DGV.Columns(e.ColumnIndex).Name & " DESC" Else DGV.Sort(DGV.Columns(e.ColumnIndex), System.ComponentModel.ListSortDirection.Ascending) DT.DefaultView.Sort = DGV.Columns(e.ColumnIndex).Name & " ASC" End If DT = DT.DefaultView.ToTable End Sub Private Sub cmd_movesinglerow_Click(sender As System.Object, e As System.EventArgs) Handles cmd_movesinglerow.Click Dim prevIndex As Integer = 0 Dim newIndex As Integer = 2 If prevIndex < 0 Or prevIndex >= DT.Rows.Count Then MessageBox.Show("Indice di origine non corretto.", "Azione annullata.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub End If If newIndex < 0 Or newIndex >= DT.Rows.Count Then MessageBox.Show("Indice di destinazione non corretto.", "Azione annullata.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub End If Dim drTemp As DataRow = DT.NewRow drTemp.ItemArray = DT.Rows(prevIndex).ItemArray DT.Rows.RemoveAt(prevIndex) DT.Rows.InsertAt(drTemp, newIndex) DGV.DataSource = DT End Sub Private Sub cmd_movemultirows_Click(sender As System.Object, e As System.EventArgs) Handles cmd_movemultirows.Click Dim dRowsTemp As New List(Of DataRow) Dim dRowTemp As DataRow Dim indices As New Dictionary(Of Integer, Integer) indices.Add(1, 2) indices.Add(6, 1) indices.Add(4, 3) indices.Add(9, 5) If indices.Keys.Min < 0 Or indices.Keys.Max >= DT.Rows.Count Then MessageBox.Show("Indice di origine non corretto.", "Azione annullata.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub End If If indices.Values.Min < 0 Or indices.Values.Max >= DT.Rows.Count Then MessageBox.Show("Indice di destinazione non corretto.", "Azione annullata.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation) Exit Sub End If 'Copia indices = indices.OrderBy(Function(I) I.Value).ToDictionary(Function(i) i.Key, Function(I) I.Value) For i As Integer = 0 To indices.Count - 1 dRowTemp = DT.NewRow dRowTemp.ItemArray = DT.Rows(indices.ElementAt(i).Key).ItemArray dRowsTemp.Add(dRowTemp) Next 'Eliminazione indices = indices.OrderByDescending(Function(I) I.Key).ToDictionary(Function(i) i.Key, Function(I) I.Value) For i As Integer = 0 To indices.Count - 1 DT.Rows.RemoveAt(indices.ElementAt(i).Key) Next 'Re-Inserimento indices = indices.OrderBy(Function(I) I.Value).ToDictionary(Function(i) i.Key, Function(I) I.Value) For i As Integer = 0 To indices.Count - 1 DT.Rows.InsertAt(dRowsTemp(i), indices.ElementAt(i).Value) Next DGV.DataSource = DT End Sub End Class
Nell'esempio generico ( cmd_movemultirows ) l'operazione viene eseguita su tutte le coppie aggiunte in indices :
- La Row ad indice 1 passa all'indice 2.
- La Row ad indice 6 passa all'indice 1.
- La Row ad indice 4 passa all'indice 3.
- La Row ad indice 9 passa all'indice 5.
Trattandosi di un Dictionary viene già assicurato che i valori degli indici di partenza ( Keys ) possano essere inseriti una volta sola ( due o più coppie con stessa Key non avrebbero senso ).
Nel caso ci siano due o più coppie con stesso Value, quello "che vince" ( e che si troverà all'indice desiderato ) è l'ultimo inserito. Gli altri ovviamente non potranno rispettare la regola. Basta comunque un semplice controllo aggiuntivo su indices.Values.Contains() in fase di inserimento per scongiurare il problema.
+ Fine Articolo.
0 commenti:
Posta un commento