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.
0 commenti:
Posta un commento