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 ClassNell'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.



10:47
MarcoGG

Posted in:
0 commenti:
Posta un commento