martedì 7 febbraio 2012

[C#] 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 una Form "FormMain" con i seguenti controlli :
--> DataGridView : DGV
--> Button : btn_movesinglerow
--> Button : btn_movemultirows

--> Codice FormMain :

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq;
using System.Windows.Forms;
 
namespace SpostamentoArbitrarioRows
{
    public partial class FormMain : Form
    {
        public FormMain()
        {
            InitializeComponent();
        }
 
        private DataTable DT = new DataTable();
 
        private void FormMain_Load(object sender, EventArgs e)
        {
            //Colonna PK
            DT.Columns.Add("ID");
            DT.PrimaryKey = new DataColumn[] { DT.Columns["ID"] };
  
            //Altre Colonne
            DT.Columns.Add("Cognome");
            DT.Columns.Add("Nome");
            //...
 
            //Inserimento Rows
            for (int i = 0; i <= 9; i++)
            {
                DT.Rows.Add(i, "Cognome_" + i, "Nome_" + i);
            }
 
            DGV.DataSource = DT;
 
            //Disabilita l'ordinamento manuale e lo rende attivo solo via codice
            for (int i = 0; i < DGV.Columns.Count; i++)
            {
                DGV.Columns[i].SortMode = DataGridViewColumnSortMode.Programmatic;
            }
        }
 
        private void DGV_ColumnHeaderMouseClick(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (DGV.Columns[e.ColumnIndex].HeaderCell.SortGlyphDirection == SortOrder.Ascending)
            {
                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";
            }
 
            DT = DT.DefaultView.ToTable();
        }
 
        private void btn_movesinglerow_Click(object sender, EventArgs e)
        {
            int prevIndex = 0;
            int newIndex = 2;
 
            if (prevIndex < 0 | prevIndex >= DT.Rows.Count)
            {
                MessageBox.Show("Indice di origine non corretto.", "Azione annullata.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
            if (newIndex < 0 | newIndex >= DT.Rows.Count)
            {
                MessageBox.Show("Indice di destinazione non corretto.", "Azione annullata.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
 
            DataRow drTemp = DT.NewRow();
            drTemp.ItemArray = DT.Rows[prevIndex].ItemArray;
            DT.Rows.RemoveAt(prevIndex);
            DT.Rows.InsertAt(drTemp, newIndex);
 
            DGV.DataSource = DT;
        }
 
        private void btn_movemultirows_Click(object sender, EventArgs e)
        {
            List<DataRow> dRowsTemp = new List<DataRow>();
            DataRow dRowTemp = null;
            Dictionary<int, int> indices = new Dictionary<int, int>();
 
            indices.Add(1, 2);
            indices.Add(6, 1);
            indices.Add(4, 3);
            indices.Add(9, 5);
 
            if (indices.Keys.Min() < 0 | indices.Keys.Max() >= DT.Rows.Count)
            {
                MessageBox.Show("Indice di origine non corretto.", "Azione annullata.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
            if (indices.Values.Min() < 0 | indices.Values.Max() >= DT.Rows.Count)
            {
                MessageBox.Show("Indice di destinazione non corretto.", "Azione annullata.", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                return;
            }
 
            //Copia
            indices = indices.OrderBy((I) => I.Value).ToDictionary((i) => i.Key, (I) => I.Value);
            for (int i = 0; i < indices.Count; i++)
            {
                dRowTemp = DT.NewRow();
                dRowTemp.ItemArray = DT.Rows[indices.ElementAt(i).Key].ItemArray;
                dRowsTemp.Add(dRowTemp);
            }
            //Eliminazione
            indices = indices.OrderByDescending((I) => I.Key).ToDictionary((i) => i.Key, (I) => I.Value);
            for (int i = 0; i < indices.Count; i++)
            {
                DT.Rows.RemoveAt(indices.ElementAt(i).Key);
            }
            //Re-Inserimento
            indices = indices.OrderBy((I) => I.Value).ToDictionary((i) => i.Key, (I) => I.Value);
            for (int i = 0; i < indices.Count; i++)
            {
                DT.Rows.InsertAt(dRowsTemp[i], indices.ElementAt(i).Value);
            }
 
            DGV.DataSource = DT;
        }
  
    }
}

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