mercoledì 2 novembre 2011

[VB.NET] Spostamento Arbitrario Rows in DataTable DataGridView

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.

Un Click su "Mi Piace" è il modo migliore per ringraziare l'autore di questo articolo.



0 commenti:

Posta un commento

 
Design by Free WordPress Themes Modificato da MarcoGG