Press "Enter" to skip to content

Datagrid WPF perchè usare un componente di terzi

Ho deciso che scriverò alcuni articoli utilizzando dei componenti di terzi per la precisione i componenti di Syncfusion.com per mostrare quello che un componente “Pro” permette di fare e fornisce ad una applicazione. Partirò con la DataGrid ma sicuramente mostrerò anche altri componenti.

Per compilare il codice di questi esempi e poterlo usare dovete scaricare la versione Trial dei componenti di Syncfusion, Trovate i riferimenti su Questo Post.

Per fare il test ho utilizzato il progetto della breve serie di articoli sulle DatagridWPF ed ho semplicemente sostituito la Datagrid standard con la SfDataGrid di Syncfusion, lasciando invariata la struttura del progetto e modificando solo le parti pertinenti. Pertanto non descriverò tutto il codice ma solo la porzione che riguarda l’uso del componente Syncfusion al posto di quello standard.

Vediamo pertanto il codice modificato nella MainWindow e come funziona questa DataGrid.

La definizione dell’oggetto SfDataGrid

<syf:SfDataGrid
	Name="dgStat"
	Grid.Row="1"
	Margin="4,2,4,2"
	AllowDraggingColumns="True"
	AllowEditing="False"
	AllowFiltering="True"
	AllowResizingColumns="True"
	AllowSorting="True"
	AllowTriStateSorting="True"
	AlternatingRowStyle="{DynamicResource alternatingRowStyle}"
	AutoExpandGroups="False"
	AutoGenerateColumns="False"
	AutoGenerateColumnsMode="ResetAll"
	ColumnSizer="None"
	CurrentCellActivated="DgStats_CurrentCellActivated"
	FilterRowPosition="None"
	GridCopyOption="CopyData"
	GridPasteOption="None"
	HeaderRowHeight="36"
	HeaderStyle="{DynamicResource headerStyle}"
	HideEmptyGridViewDefinition="True"
	ItemsSource="{Binding Statistics, Mode=OneWay, 
		UpdateSourceTrigger=PropertyChanged}"
	LiveDataUpdateMode="AllowDataShaping"
	RowHeaderWidth="36"
	RowHeight="36"
	RowStyle="{DynamicResource rowStyle}"
	SelectedItem="{Binding CurrentStatistic, Mode=TwoWay, 
		UpdateSourceTrigger=PropertyChanged}"
	SelectionMode="Single"
	SelectionUnit="Row"
	ShowGroupDropArea="True"
	ShowRowHeader="True"
	ShowSortNumbers="True">

Partiamo con la dichiarazione della grid, e possiamo vedere subito come il numero di Attributi che sono usati per definire la grid sono molti di più di quelli di una datagrid standard. Questo è dovuto al fatto che la grid Syncfusion tramite questi parametri ci permette di configurare con le semplici dichiarazioni come la grid si comporta e che servizi fornisce a chi la utilizzerà, senza la necessità di scrivere codice a supporto.

Quelli che vedete qui sopra sono quelli che solitamente utilizzo io, ma ce ne sono probabilmente di ulteriori, pertanto vi invito a dare un occhiata approfondita alla documentazione che si trova sul sito ed è approfondita e completa di esempi d’uso.

Vediamo di listare cosa ho usato e a cosa serve:

  1.  Name = dgStat questa è una property standard di WPF e mi serve per potermi agganciare alla grid da codice in determinati punti del codice.
  2.  Grid.Row = questa è la property standard che mi permette di posizionare la SfDataGrid sulla Grid WPF che disegna il Layout della Window.
  3. Margin = 4,2,4,2 questo è un altro degli attributi standard WPF dei controlli e mi permette di definire il margine attorno al controllo rispetto alla Grid contenitore.
  4. AllowDraggingColumns = True, questa è la prima delle property proprie della SfDataGrid, e permette di attivare o disattivare la possibilità di spostare le colonne della grid all’interno del suo layout.
  5. AllowEditing = False, questa property permette di attivare o disattivare a livello globale la modifica dei campi all’interno della SfDataGrid. Una volta disattivato come nel mio caso, è però possibile attivarlo selettivamente su alcune delle colonne.
  6. AllowFiltering = True, questa è una delle funzionalità che nella SfDataGrid sono già implementate, infatti True, come nel mio caso attiva automaticamente la possibilità di applicare un filtro su qualsiasi colonna (pensate ai filtri di Excel), ed i filtri possono essere applicati in cascata. (su più colonne contemporaneamente).
  7. AllowResizingColumns = True, questa è un altra property per attivare o disattivare la possibilità di modificare la larghezza delle colonne da parte degli utenti. Nel nostro caso è attiva così come lo spostamento, visto che abbiamo conservato il codice per memorizzare la forma della Grid da parte dell’utente.
  8. AllowSorting = True, questa property permette se necessario di disattivare l’ordinamento delle colonne al click dell’Header.
  9. AllowTriStateSorting = True, questo è un aggiunta sulla funzionalità di Sort che oltre al sort ascendente e discendente al terzo click sull’header ripristina l’ordinamento fisico della lista caricata sulla grid.
  10. AlternatingRowStyle = DynamicResource alternatingRowStyle, questa property permette di creare uno stile per le righe alternate solitamente per evidenziare righe Pari e Dispari in modo da agevolare la lettura (ricordate quando esisteva la carta a lettura facilitata negli anni ottanta e novanta? no vero, perché non siete dinosauri come me).
  11. AutoExpandGroups = False, questa property è legata ad un’altra delle feature che questa SfDataGrid ci fornisce “gratis”, infatti, tramite un altra property che vedremo fra poco, che attiva la presenza della “Group Drop Area” la SfDataGrid permette di trascinare lo Header di una colonna nell’area suddetta e automaticamente raggruppare i dati secondo i valori del campo suddetto. Vi mostrerò gli screenshot al termine dell’articolo. In questo caso quando posta a True, i gruppi una volta generati vengono automaticamente espansi.
  12. AutoGenerateColumns = False, questo flag che credo sia comune alla Grid standard, permette di disattivare la creazione automatica delle colonne della Grid e quindi di definire manualmente forma, posizione, tipo ecc. per le colonne.
  13. AutoGenerateColumnsMode = ResetAll, questo flag, quando il precedente è True permette di specificare come devono essere rigenerate le colonne nel caso sia modificata la sorgente dati della grid stessa.
  14. ColumnSizer=”None” questa property permette di modificare il comportamento delle colonne rispetto alle dimensioni della Window e della grid al suo interno. Ha varie opzioni per permettere di definire come le colonne devono essere modificate, nel mio caso, None non modifica la dimensione delle colonne quando la grid si allarga o si stringe.
  15. CurrentCellActivated=DgStats_CurrentCellActivated questo è uno degli eventi che espone la grid ed io intercetto ed utilizzo per leggere il contenuto della cella correntemente attivata e visualizzarlo nella TextBox predisposta e per prepararlo in modo da poter copiare ed incollare il valore della cella ovunque.
  16. FilterRowPosition = None, questa property permette di ottenere “gratis” una riga di filtro che può apparire in cima o in fondo alla Grid permettendo di applicare un filtro sulle varie colonne semplicemente digitando del testo invece di utilizzare il pulsantino di filtro che viene generato sul titolo dall’opzione AllowFiltering. Questa è la seconda opzione per fornire i servizi di filtro agli utenti, in un successivo articolo vi mostrerò un ulteriore possibilità avanzata fornita “gratis” o quasi dalla SfDataGrid.
  17. GridCopyOption = “CopyData”, un altra property che ci fornisce un servizio “gratis” infatti, questa opzione permette di selezionare cosa fa la SfDataGrid quando premiamo Ctrl+C nel mio caso, ho usato il CopyData che mi mette in clipboard i dati delle celle o righe selezionate in base al tipo di selezione che imposteremo con una successiva property.
  18. GridPasteOption = “None”, in questo caso, la grid non è modificabile, ma se avete la necessità di creare grid modificabili ed avete 2 grid contenenti dati con la stessa forma potete usare il Ctrl+C e Ctrl+V per copiare una o più righe selezionate da una grid ad un altra con le opzioni di questa proprietà.
  19. HeaderRowHeight = 36, ci permette di stabilire arbitrariamente l’altezza dei titoli della grid, se necessario.
  20. HeaderStyle = DynamicResources headerStyle, anche questo è uno degli stili che possiamo applicare alla grid, per definire come deve apparire la riga dei titoli. Gli stili che vedete sulla mia grid, si trovano tutti in App.Xaml. Nella documentazione troverete una descrizione estesa di tutti gli stili applicabili ai vari oggetti che compongono la SfDataGrid.
  21. HideEmptyGridViewDefinition = True, la SfDataGrid permette di definire grid gerarchiche (tipicamente Master Detail come ad esempio gli ordini), questa property permette di nascondere il segno [+] che espande il livello detail se tale detail è vuoto per una riga Master.
  22. ItemsSource = Binding…, si tratta della property primaria per assegnare i dati alla SfDataGrid, la sorgente dati può essere una collection oppure una DataTable o un DataSet. Credo sia semplicemente necessario che l’oggetto implementi la property IEnumerable per essere valido come sorgente dati.
  23. LiveDataUpdateMode=AllowDataShaping, questa è una delle proprietà più utili della SfDataGrid, in quanto permette di attivare o disattivare la visualizzazione in tempo reale dei dati aggiornati sulla Grid, per esempio, il valore che ho usato io se aggiungo dei record alla collection li visualizza immediatamente, è molto utile ad esempio se implementiamo una grid che riceve dati in tempo reale (ad esempio un log di qualche tipo o magari una lista di dati che varia velocemente come i messaggi di una chat o gli incassi di un sito web). Ovviamente bisogna fare attenzione al numero di righe contenute nel recordset anche se WPF non si fa problemi neppure con le decine o centinaia di migliaia di righe.
  24. RowHeaderWidth = 36, questa property ci permette di visualizzare un intestazione di riga governandone la larghezza, normalmente contiene un segnalino di selezione ma può anche essere personalizzata con poche righe di codice per visualizzare ad esempio il numero di riga se ci serve.
  25. RowHeight=36, questa property ci permette di decidere quanto alte sono le righe. Si può implementare, con poche righe di codice, il necessario perché le righe abbiano una altezza che dipende dal loro contenuto, soprattutto se abbiamo dei campi che possono contenere più righe di testo.
  26. RowSyle = DynamicResource rowStyle, questo assieme all’AlternatingRowStyle permette di cambiare lo stile visuale delle righe, se lo stile AlternatingRowStyle non è usato, questo è lo stile assunto da tutte le righe.
  27. SelectedItem = Binding…, questa property ci permete di agganciare l’oggetto correntemente selezionato ad una classe del nostro ViewModel, nel nostro caso la riga della collezione che fa da ItemSource.
  28. SelectionMode=Single, questa property ci permette di decidere se vogliamo selezionare i dati in modo singolo o multiplo (piu righe o più celle).
  29. SelectionUnit=Row, questa property ci permette di decidere se selezionare Righe come nel mio caso oppure celle oppure entrambe le cose. Nel caso si usi “entrambe” le celle si selezionano con click e drag o Ctrl+Click e le righe si selezionano utilizzando il RowHeader e anche in questo caso con Shift oppure con Ctrl+Click.
  30. ShowGroupDropArea=True, questa è la property legata all’AutoExpandGroups, come indicato in precedenza, questa property se True visualizza un’area in cima alla SfDataGrid dove possiamo trascinare e mollare i titoli delle colonne per raggruppare i dati usando le colonne trascinate e “droppate” in questa area. Questo è un metodo molto potente per capire come variano i dati e quanti elementi con un certo valore sono presenti nelle varie colonne. E’ anche possibile definire dei totalizzatori per i gruppi e quindi ottenere dei subtotali per gruppi di dati ed è possibile pilotare i raggruppamenti da codice. Strumento estremamente versatile per creare report e statistiche veloci.
  31. ShowRowHeader = True, permette di nascondere l’intestazione di riga.
  32. ShowSortNumbers = True, permette di mostrare l’ordine in cui le colonne sono state cliccate in un ordinamento multi colonna, infatti la SfDataGrid, permette utilizzando il tasto Ctrl premuto quando clicchiamo il titolo delle colonne per riordinarle, per selezionare un ordinamento multicolonna. L’ordinamento può essere anche prestabilito e variato da codice.

Ho solo descritto le principali proprietà della SfDataGrid e spero che iniziate a capire perché affidarsi ad un componente sviluppato da terzi invece di dover scrivere tutto il codice necessario a svolgere funzioni che in questo caso sono fornite “gratis”.

Metto le virgolette sul “gratis” perchè ovviamente i componenti di terzi non sono gratuiti, però facendo il conto del numero di ore per sviluppare le funzionalità da essi fornite credo possiamo stabilire che probabilmente il costo dei componenti si recupera con il tempo di sviluppo molto più breve per progetti complessi.

Definiamo alcuni dei comportamenti della SfDataGrid

Nel codice che vedremo ora utilizziamo alcune delle funzionalità della grid per definire alcuni comportamenti, come l’Ordinamento iniziale, L’uso di Titoli “impilati” sopra al titolo standard delle colonne, e definiremo anche qui il menu contestuale in cui inseriremo 2 opzioni implementate, una per copiare la cella corrente nella clipboard e l’altra per aprire il nostro ColumnChooser e decidere che colonne vedere o nascondere.

<syf:SfDataGrid.SortColumnDescriptions>
	<syf:SortColumnDescription ColumnName="Year" 
		SortDirection="Descending" />
	<syf:SortColumnDescription ColumnName="CustomerName" 
		SortDirection="Ascending" />
</syf:SfDataGrid.SortColumnDescriptions>

Per prima cosa definiamo quali sono le colonne su cui i dati sono ordinati al caricamento. è un’operazione semplice, usiamo il nome della colonna (o meglio il nome del campo della colonna) e la direzione Ascendente o Discendente. In questo caso ordiniamo per Anno e nome del Cliente.

<syf:SfDataGrid.StackedHeaderRows>
	<syf:StackedHeaderRow>
		<syf:StackedHeaderRow.StackedColumns>
			<syf:StackedColumn 
			ChildColumns="CustomerName,Year,SalesTotal,BudgetTotal,PercentSales,PercentBudget,Trend" 
			HeaderText="Statistiche Clienti" />
		</syf:StackedHeaderRow.StackedColumns>
	</syf:StackedHeaderRow>
</syf:SfDataGrid.StackedHeaderRows>

L’oggetto StackedHeaderRows è interessante perché ci permette di inserire dei titoli ulteriori sopra al titolo delle colonne, è un tipo di effetto solitamente usato in Excel per la presentazione dei dati, per aggiungere dei “raggruppamenti” con un titolo sopra alle colonne. Nel nostro caso ho semplicemente messo un titolo sopra a tutte le colonne, la lista delle “ChildColumns” deve contenere i nomi di tutte le colonne su cui il titolo deve apparire. (c’è un piccolo problemino per cui è indispensabile non mettere spazi prima o dopo la virgola a parte ciò è molto semplice).

 

<syf:SfDataGrid.RecordContextMenu>
<ContextMenu>
<MenuItem
	Click="CopyCell_Click"
	CommandParameter="Received"
	Header="Copia cella corrente"
	ToolTip="Copia il contenuto della cella corrente nella clipboard">
	<MenuItem.Icon>
		<Image
		MaxWidth="24"
		MaxHeight="24"
		Margin="0"
		Source="pack://application:,,,/Images/btn_064_1323.png" />
	</MenuItem.Icon>
</MenuItem>
 
<Separator Margin="4" />
<MenuItem
	Click="ChooseColumns_Click"
	CommandParameter="Window"
	Header="Mostra Nascondi colonne"
	ToolTip="Permette di rendere visibili o invisibili le colonne della lista.">
	<MenuItem.Icon>
		<Image
		MaxWidth="24"
		MaxHeight="24"
		Margin="0"
		Source="pack://application:,,,/Images/btn_064_1553.png" />
	</MenuItem.Icon>
</MenuItem>
</ContextMenu>
</syf:SfDataGrid.RecordContextMenu>

Dopo aver ordinato la SfDataGrid, aggiungiamo due opzioni di menu, una per copiare il contenuto della cella corrente nella Clipboard e una per attivare la finestra del ColumnChooser e attivare o disattivare la visualizzazione delle colonne.

<syf:SfDataGrid.Columns>
	<syf:GridTemplateColumn
		Width="50"
		HeaderText="Trend"
		MappingName="Trend"
		ShowHeaderToolTip="True"
		ShowToolTip="True">
		<syf:GridTemplateColumn.CellTemplate>
			<DataTemplate>
				<Grid>
					<Image
						Grid.Column="0"
						MaxWidth="24"
						MaxHeight="24"
						Margin="0"
						Source="{Binding Path=Trend, UpdateSourceTrigger=PropertyChanged, Converter={lconv:TrendToImageConverter}}"
						ToolTip="Customer's Buying Trend" />
				</Grid>
			</DataTemplate>
		</syf:GridTemplateColumn.CellTemplate>
	</syf:GridTemplateColumn>
	<syf:GridTextColumn
		Width="80"
		FilterRowCondition="Contains"
		HeaderText="Year"
		ImmediateUpdateColumnFilter="True"
		MappingName="Year"
		ShowHeaderToolTip="True"
		ShowToolTip="True" />
	<syf:GridTextColumn
		MinimumWidth="200"
		FilterRowCondition="Contains"
		HeaderText="Customer Name"
		ImmediateUpdateColumnFilter="True"
		MappingName="CustomerName"
		ShowHeaderToolTip="True"
		ShowToolTip="True" />
	<syf:GridCurrencyColumn
		Width="90"
		CurrencyDecimalDigits="2"
		CurrencyDecimalSeparator=","
		CurrencyGroupSeparator="."
		CurrencyGroupSizes="3"
		CurrencySymbol="€"
		FilterRowCondition="Contains"
		HeaderText="Sales"
		ImmediateUpdateColumnFilter="True"
		MappingName="SalesTotal"
		ShowHeaderToolTip="True"
		ShowToolTip="True"
		TextAlignment="Right">
		<syf:GridCurrencyColumn.CellStyle>
			<Style TargetType="syf:GridCell">
				<Setter Property="Background" Value="{Binding SalesTotal, Converter={lconv:DecimalAmountToColorConverter}}" />
			</Style>
		</syf:GridCurrencyColumn.CellStyle>
	</syf:GridCurrencyColumn>
	<syf:GridCurrencyColumn
		Width="90"
		CurrencyDecimalDigits="2"
		CurrencyDecimalSeparator=","
		CurrencyGroupSeparator="."
		CurrencyGroupSizes="3"
		CurrencySymbol="€"
		FilterRowCondition="Contains"
		HeaderText="Budget Total"
		ImmediateUpdateColumnFilter="True"
		MappingName="BudgetTotal"
		ShowHeaderToolTip="True"
		ShowToolTip="True"
		TextAlignment="Right">
		<syf:GridCurrencyColumn.CellStyle>
			<Style TargetType="syf:GridCell">
				<Setter Property="Background" Value="{Binding BudgetTotal, Converter={lconv:DecimalAmountToColorConverter}}" />
			</Style>
		</syf:GridCurrencyColumn.CellStyle>
	</syf:GridCurrencyColumn>
	<syf:GridPercentColumn
		Width="90"
		FilterRowCondition="Contains"
		HeaderText="Sales %"
		ImmediateUpdateColumnFilter="True"
		MappingName="PercentSales"
		PercentDecimalDigits="2"
		PercentDecimalSeparator=","
		PercentSymbol="%"
		ShowHeaderToolTip="True"
		ShowToolTip="True"
		TextAlignment="Right">
		<syf:GridPercentColumn.CellStyle>
			<Style TargetType="syf:GridCell">
				<Setter Property="Background" Value="{Binding PercentSales, Converter={lconv:DecimalPercentToColorConverter}}" />
			</Style>
		</syf:GridPercentColumn.CellStyle>
	</syf:GridPercentColumn>
	<syf:GridPercentColumn
		Width="90"
		FilterRowCondition="Contains"
		HeaderText="Budget %"
		ImmediateUpdateColumnFilter="True"
		MappingName="PercentBudget"
		PercentDecimalDigits="2"
		PercentDecimalSeparator=","
		PercentSymbol="%"
		ShowHeaderToolTip="True"
		ShowToolTip="True"
		TextAlignment="Right">
		<syf:GridPercentColumn.CellStyle>
			<Style TargetType="syf:GridCell">
				<Setter Property="Background" Value="{Binding PercentBudget, Converter={lconv:DecimalPercentToColorConverter}}" />
			</Style>
		</syf:GridPercentColumn.CellStyle>
	</syf:GridPercentColumn>
</syf:SfDataGrid.Columns>

L’ultima parte della definizione della SfDataGrid riguarda la definizione delle colonne. Come potete notare, abbiamo utilizzato la TemplateColumn, presente anche nella SfDataGrid solo per la colonna con l’immagine del Trend. Invece per le colonne numeriche esistono delle apposite colonne che hanno già le funzionalità necessarie alla formattazione e possono essere modificate per rispondere agli stili dei numeri delle varie culture, abbiamo mantenuto l’uso dei converter per modificare il colore delle celle, ma abbiamo usato uno stile per costruire il colore corretto. Non è necessaria alcuna modifica speciale per l’ordinamento e le colonne hanno 2 property molto utili che sono:

ShowHeaderToolTip
ShowToolTip

Queste due property poste a True fanno in modo che automaticamente la SfDataGrid mostri nel tooltip del titolo la stringa completa del titolo (utile per le colonne piccole) e allo stesso modo che nel tooltip di una cella sia mostrato il suo valore, utile soprattutto sulla colonna Trend, per vedere anche il suo valore.

Le colonne GridCurrencyColumn e GridPercentColumn ci permettono di definire il numero e la forma dei decimali, se utilizzare i punti delle migliaia, quale carattere usare per la virgola e quale per le migliaia ed anche il simbolo della valuta ed il simbolo della percentuale.

Tutti i dati possono essere collegati in Binding e quindi inizializzati in modo dinamico in base all’installazione.

L’evento CurrentCellActivated

Vediamo ora il codice dell’Event handler che usiamo per rilevare il contenuto della cella corrente.

private void DgStats_CurrentCellActivated(object senderCurrentCellActivatedEventArgs e)
{
	try
	{
	
		SfDataGrid grid = sender as SfDataGrid;
		if (grid != null)
		{
			int rowindex = e.CurrentRowColumnIndex.RowIndex;
			int columnIndex = grid.ResolveToGridVisibleColumnIndex(e.CurrentRowColumnIndex.ColumnIndex);
			if (columnIndex < 0)
			{
				SelectedCellValue = null;
				return;
			}
			CurrentActiveColumnName = grid.Columns[columnIndex].MappingName;
 
			int recordIndex = grid.ResolveToRecordIndex(rowindex);
 
			if (grid.View.TopLevelGroup != null)
			{
				var record = grid.View.TopLevelGroup.DisplayElements[recordIndex];
				if (!record.IsRecords)
				{
					SelectedCellValue = null;
					return;
				}
				var data = (record as RecordEntry).Data;
				SelectedCellValue = data.GetType().GetProperty(CurrentActiveColumnName).GetValue(data).ToString();
			}
			else
			{
				if (recordIndex < 0 || recordIndex > grid.View.Records.Count)
				{
					SelectedCellValue = null;
					return;
				}
 
				string mappingName = grid.Columns[columnIndex].MappingName;
				if (recordIndex < grid.View.Records.Count)
				{
					var currentRecord = grid.View.Records.GetItemAt(recordIndex);
					var prop = currentRecord.GetType().GetProperty(mappingName).GetValue(currentRecord);
					if (prop != null)
					{
						SelectedCellValue = prop.ToString();
					}
				}
			}
		}
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message"Errore"MessageBoxButton.OKMessageBoxImage.Error);
	}
}

In realtà questo codice è abbastanza complesso, non c’è un semplice elemento della SfDataGrid che mappa il contenuto della cella corrente, ma invece dobbiamo andare a “scavare” nei meandri dell’oggetto che la costituisce. Ci sono però alcune motivazioni logiche per cui dobbiamo farlo, e sono le seguenti:

  1. Il contenuto della SfDataGrid viene modificato quando cambiamo l’ordinamento delle colonne.
  2. Il contenuto della SfDataGrid viene modificato ancor più radicalmente se utilizziamo la funzione di raggruppamento per colonna. 

Da qui la necessità di codice non banale per andare a cercare qual’è il dato che si trova nella cella corrente. Ovviamente chi ha scritto la SfDataGrid l’ha dotata di una serie di metodi helper per darci una mano nella ricerca. Vediamo quindi cosa facciamo nel codice sopra incollato.

  1. Prima di tutto dall’event handler riconosciamo qual’è l’oggetto SfDataGrid che ha scatenato l’evento non usiamo direttamente l’oggetto grid per Nome, questo può suggerirvi il fatto che l’event handler che abbiamo scritto sia generico e possa essere quindi utilizzato anche su una Window che contiene più di una SfDataGrid.
  2. Ricaviamo l’indice della riga corrente della grid dai dati passati dalla stessa via Event Arguments.
  3. Ricaviamo l’indice della colonna correntemente selezionata utilizzando un Extension dell’oggetto SfDataGrid: ResolveToGridVisibleColumnIndex. Se quello che l’utente ha selezionato non fosse una colonna dati, usciamo subito e azzeriamo il contenuto della cella selezionata.
  4. Se invece la colonna dati è tale, ricaviamo il nome del campo della colonna usando la property MappingName. Infatti, rispetto a quanto abbiamo fatto sul progetto con la DataGrid standard, abbiamo modificato leggermente la gestione degli oggetti per memorizzare la configurazione della grid ed abbiamo sostituito il Testo dell’intestazione di colonna con il nome della Property dell’oggetto visualizzato che gli oggetti Colonna della SfDataGrid espongono.
  5. Utilizziamo l’Indice della riga corrente e tramite l’extension ResolveToRecordIndex andiamo a cercare l’indice del record sottostante.
  6. Trovato l’indice del record, andiamo a fare una ulteriore verifica, infatti controlliamo se stiamo operando su una Grid con un singolo livello, per cui l’indice del record è semplice da ricavare oppure se la grid è stata Raggruppata, nel qual caso dobbiamo fare qualcosa di diverso.
  7. Se la grid è a singolo livello dati ricaviamo il record utilizzando l’oggetto View della SfDataGrid e gli oggetti TopLevelGroup e DisplayElements. 
  8. Controlliamo che la riga selezionata non sia la riga del titolo o altro tipo di riga non contentente dati (nel caso azzeriamo il campo contenente il valore della cella corrente) altrimenti abbiamo ricavato il record dati e tramite il nome colonna recuperiamo il valore.
  9. Nel caso fossimo invece su una grid in cui è stato effettuato un raggruppamento andiamo per prima cosa a verificare che il record selezionato sia parte dei record della vista corrente, se non lo fosse (sempre perché possiamo selezionare una riga di intestazione o una riga di totali) azzeriamo il valore della cella selezionata.
  10. Se invece il record è compreso in quelli parte della View corrente, ricaviamo il nome della colonna e poi andiamo a recuperare il Record utilizzando il metodo GetItemAt dell’oggetto records della View corrente.
  11. Recuperiamo il valore della property utilizzando il nome della colonna dal record ricavato.
  12. Se il valore non è nullo, lo convertiamo in stringa e lo mettiamo sulla nostra variabile SelectedCellValue.

Vi sembra complicato? In realtà lo è perché le funzionalità aggiuntive della SfDataGrid complicano molto l’oggetto dati, però tutta la complessità della gestione dei raggruppamenti non dobbiamo scriverla noi, perché è già stata scritta per noi.

 

 

private void CopyCell_Click(object senderRoutedEventArgs e)
{
	try
	{
		Clipboard.SetText(SelectedCellValue);
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message"Errore"MessageBoxButton.OKMessageBoxImage.Error);
	}
}

Il metodo che abbiamo aggiunto per copiare in clipboard il contenuto della cella corrente è molto semplice, infatti una volta che il Cell Activated ha trovato il testo della cella corrente, riportarlo in Clipboard per un successivo uso è questione di una riga di codice.

Fatto salvo le piccole modifiche che ho applicato a causa della diversità degli oggetti della SfDataGrid rispetto alla DataGrid standard questo progetto è praticamente identico al precedente, pertanto vi invito a guardare gli articoli dedicati al progetto con la Datagrid Standard per la discussione sul resto del codice, invece, inserisco alcuni screenshots per evidenziare le varie funzionalità della SfDataGrid che vengono fornite grazie all’uso degli attributi di configurazione che abbiamo discusso all’inizio di questo articolo.

Gli Screenshot e le funzionalità “gratuite” della SfDataGrid

Nell’immagine precedente un piccolo tour della SfDataGrid in cui ho evidenziato alcune delle cose che ci fornisce. Nei prossimi screenshot li vediamo in azione.

Un particolare del Sort MultiColonna, come potete vedere oltre al segnalino “^” o contrario per indicare la direzione ascendente o discendente dell’ordinamento abbiamo anche il numero che indica in quale direzione l’ordinamento viene effettuato. Per ordinare su più colonne bisogna Cliccare il titolo di colonna tenendo premuto il tasto [Ctrl], usare più click per ciclare fra ascendente e discendente. 

In questa immagine un esempio dell’interfaccia di filtro fornita cliccando il button a forma di Imbuto sul titolo. Assomiglia (volutamente) alla ricerca sui fogli di Excel, automaticamente sono forniti tutti i valori Distinct della colonna con la possibilità di selezionarli con le checkbox.

Cliccando sull’opzione di menu TextFilters possiamo andare ad attivare dei filtri più complessi. come Vedremo nello screenshot successivo.

 

I filtri di testo estesi permettono di cambiare il tipo di filtro e se necessario scegliere più filtri in And o in Or.

La lista permette di scegliere anche il tipo di inclusione o esclusione del valore che indicheremo tramite la combobox collegata.

Passiamo alla funzione più potente fra quelle fornite senza dover scrivere codice, ovvero il raggruppamento per colonne. apriamo l’area di raggruppamento, ma si apre sola se trasciniamo un titolo di colonna su di essa.

Ho trascinato e mollato il titolo della colonna Year sull’area e come vedete è apparso un pulsante contenente il titolo della colonna e tutta la grid si è Chiusa mostrandomi che ci sono 6 oggetti per ciascuno dei 2 valori della colonna anno.

Il titolo della colonna raggruppata non è solo un placeholder ma un oggetto attivo, il fatto che permetta di cambiare l’ordinamento è dovuto al fatto che volendo esiste un opzione per cui quando la colonna di raggruppamento viene trascinata in area di raggruppamento sparisce dalla grid, nel qual caso non sarebbe possibile ordinarla.

I pulsanti con la freccia sulla colonna di raggruppamento aprono o chiudono il gruppo mentre la prima colonna segnala il record correntemente selezionato. Come vedete, anche il record di raggruppamento può essere selezionato, da qui la necessità dei controlli sulla selezione della cella corrente.

Facciamo un ulteriore step, ho trascinato sull’area di raggruppamento anche la colonna Trend e adesso ho due serie di gruppi concentrici.

I record sono gli stessi ma suddivisi nei due gruppi, ora andrò a trascinare il pulsante Year sull’area di raggruppamento e invertirò i gruppi.

Ed ora ho i raggruppamenti prima su Trend e poi su Anno, e come potete notare, la cella attiva San Pietro Srl viene correttamente visualizzata.

Vi ho fatto vedere alcune delle cose che un componente avanzato come la SfDataGrid vi permette di fare, ma credo parleremo ancora di questa grid, indagando su come aggiungere ulteriori funzionalità con la scrittura di poco codice.

Mi fermo qui, per ora, ovviamente c’è il codice a Corredo e aggiungo nuovamente il link al Post in cui sono inseriti i link per poter andare a scaricare la versione trial dei componenti Syncfusion.

Dove trovare i componenti Syncfusion