Press "Enter" to skip to content

Datagrid WPF standard un caso d’uso reale (parte 1)

Definire una classe dati per fare da Datasource ad una Datagrid

Questo post è collegato due diversi thread sul forum Microsoft  che trovate ai seguenti indirizzi:
Visualizzare il contenuto della cella corrente di una Datagrid 
Visualizzare le celle con un colore diverso in base ad una condizione legata al valore
Siccome non ho trovato molto materiale online sulla DataGrid standard WPF e zero materiale in italiano, ho deciso di creare un mini progetto WPF che usa la DataGrid ed implementa una serie di funzionalità relative ad un caso d’uso che va oltre il tutorial base ma può essere utile nello sviluppo di progetti reali.

Vediamo quindi che cosa illustreremo riguardo la DataGrid standard WPF:

  • Creazione di un oggetto entity base che sarà la riga della nostra grid
  • Creazione e Collegamento ad una Datasource che è una collection di oggetti
  • Uso di colonne di tipo DataGridTextColumn e DataGridTemplateColumn
  • Formattazione delle colonne numeriche con l’uso del parametro StringFormat
  • Implementazione del Sort sulle colonne di tipo DataGridTemplateColumn
  • Formattazione condizionale del background delle celle usando un Converter
  • Creazione di una colonna che mostra un immagine al posto di un valore enumerato usando i Converter
  • Visualizzazione su una TextBox del contenuto della cella correntemente selezionata
  • Salvataggio e ripristino configurazione posizione, ampiezza, visibilità delle colonne di una grid
  • Bonus 1: perché usare una Template column per una CheckBox
  • Bonus 2: helper serializzazione Xml della classe per la gestione di configurazione colonne
  • Bonus 3: Come creare stili application wide usando DynamicResource e App.xaml
  • Bonus 4: Ovviamente il codice completo a corredo.

L’ordine in cui i punti qui sopra listati saranno spiegati non sarà necessariamente quello qui sopra indicato perché le cose devono essere implementate in base ad un filo logico per lo sviluppo del codice.

Preparativi da effettuare per poter seguire il tutorial scrivendo il codice

  1. Aprire Visual Studio 2017 o 2019 in qualsiasi versione
  2. Cliccare Create New Project
  3. Selezionare Opportunamente il template WPF App (.NET Framework)
  4. Generare il progetto e chiamarlo WpfGridColors e salvarlo su una opportuna cartella del vostro disco.

La solution generata, in questo caso usando la preview di VS 2019 contiene solution, progetto e alcuni file che, non mostro visto che ho già tutto il codice e voglio mantenere la sorpresa.

Collegamento ad una Datasource che è una collection di oggetti

Per non perderci nei meandri della progettazione e uso di database, che riserviamo magari ad un prossimo articolo, per creare l’ItemsSource della mia DataGrid ho creato una classe che rappresenta il mio oggetto “riga” della DataGrid, una volta creato, all’interno della MainWindow del progetto genero una collezione di questi oggetti inserendoli in una Collection che viene usata come ItemsSource della DataGrid.

La classe CustomerStats.cs

Questa classe, rappresenta le statistiche di vendita di un cliente, con due dati ovvero il Budget (fatturato previsto) e il Fatturato totale annuali, inoltre implementa due property dove inseriremo il valore percentuale calcolato dell’incidenza del fatturato e dell’ordinato di ogni cliente sul fatturato complessivo. (non vi spaventate che è più difficile a dirsi che a farsi).

Per creare la classe, posizionatevi sul progetto e premete Tasto destro selezionate Add… e nel sottomenu New Folder…

Aggiungiamo una cartella che ci permetterà di organizzare le nostre classi per tipologie è utile impararlo subito per creare una logica nei nostri progetti che saranno sicuramente più complessi di questo. La cartella che creiamo sarà la cartella Entities, ove metteremo le classi che forniscono dati, quelle che nella versione semplificata di MVVM che utilizziamo sono il “Model”.

Scusate i quadratini grigi, ma vorrei ci concentrassimo sulle cose che facciamo passo passo, anche se ovviamente il mio progetto contiene già il codice completo. L’uso del menu precedente genera una cartella chiamata New Folder e modifichiamo il nome in Entities

Poi ci posizioniamo sul nodo Entities e usiamo l’opzione Add… e nel sottomenu New Item…

Nella finestra di creazione delle classi, selezioniamo class e sul nome digitiamo CustomerStats.cs

using System;
using System.Collections.Generic;
using System.Text;
using System.Linq;
using System.ComponentModel;
 
namespace WpfGridColors.Entities
{
	public class CustomerStats
	{
		public CustomerStats()
		{
 
		}
	}
}

Troveremo qualcosa di simile a questo, fate attenzione alla parola chiave public davanti alla dichiarazione class, se non vi fosse inseritela.

Nella lista delle clausole using, aggiungiamo la seguente:

using System.ComponentModel;

L’aggiunta di questo Namespace è indispensabile per modificare la dichiarazione della classe nel seguente modo

public class CustomerStats : INotifyPropertyChanged

L’aggiunta di questa dichiarazione, indica che implementeremo l’interfaccia INotifyPropertyChanged che se avete letto alcune delle serie di articoli precedenti sapete essere necessaria per fare in modo che le property della nostra classe informino WPF quando vengono modificate in modo che se la modifica viene effettuata da codice l’interfaccia mostri i valori modificati.

Cos’è un interfaccia? Nei libri l’interfaccia viene definita come un “contratto”, che obbliga una classe ad implementare del codice con un determinato formato e una determinata nomenclatura che fa in modo che il resto delle librerie (nel nostro caso specificamente la libreria WPF della user interface), pur non sapendo come le nostre classi sono fatte, possano utilizzarle per le loro necessità.

L’interfaccia INotifyPropertyChanged, ci obbliga ad implementare un Evento che deve essere sollevato da tutte le property della nostra classe che in qualche modo vogliono interagire con la User Interface.

 

public event PropertyChangedEventHandler PropertyChanged;
 
protected virtual void OnPropertyChanged(string propertyName)
{
	if (PropertyChanged != null)
		PropertyChanged(thisnew PropertyChangedEventArgs(propertyName));
}

Qui sopra il codice che implementa la nostra interfaccia. Vedremo poi quando scriveremo le property come sarà utilizzata.

Dopo aver implementato questo codice, aggiungiamo gli elementi fondamentali della classe, ovvero le property che rappresentano i dati della statistica cliente.

 

public const string FLD_CustomerName = "CustomerName";
 
private string mCustomerName;
 
public string CustomerName
{
	get
	{
		return mCustomerName;
	}
	set
	{
		mCustomerName = value;
		OnPropertyChanged(FLD_CustomerName);
	}
}

La prima delle property che implementiamo, il nome cliente, come vedete, quando viene eseguito il metodo set della property, viene sollevato l’evento OnPropertyChanged che abbiamo definito. Questo evento, verrà intercettato da WPF per aggiornare se necessario l’interfaccia utente.

Aggiungiamo in modo identico le altre property della classe che sono:

  • Year, di tipo string, che contiene l’anno di riferimento della riga di statistica cliente
  • SalesTotal, di tipo decimal, che contiene il totale delle vendite per l’anno
  • BudgetTotal, di tipo decimal, che contiene il totale del budget (previsionale) per l’anno
  • PercentSales, di tipo decimal, che contiene l’incidenza percentuale delle vendite del cliente sul totale vendite dell’anno.
  • PercentBudget, di tipo decimal, che contiene l’incidenza percentuale del budget del cliente sul totale budget dell’anno.

Troverete le property definite nel codice a corredo, ma c’è un ulteriore property speciale che vedremo nello specifico:

public const string FLD_Trend = "Trend";
 
public Trends Trend
{
	get
	{
		return BudgetTotal >= SalesTotal ? Trends.Down : Trends.Up;
	}
}

Come potete notare questa property non ha il metodo set, e nel metodo get c’è del codice, non una variabile, questo perché si tratta di un campo calcolato. In questo caso, il codice si traduce in:

Se il Budget totale è Maggiore del totale vendite, il Trend del cliente è Down (giù) altrimenti il Trend è Up (su)

Questa proprietà calcolata, fornirà il supporto ad un segnalino visuale che inseriremo in una colonna della Grid.

I due valori del Trend sono stati generati come Valori enumerati con il seguente codice che trovate nel file della classe, prima della dichiarazione di CustomerStats.

public enum Trends
{
	Up,
	Down
}

L’enumerazione ci permette di creare dei valori numerici (interi) richiamabili con un nome. Trends.Up e Trends.Down (se non diversamente indicato, Up=0, Down=1 ma vedrete che i valori numerici non ci serviranno mai.)

Ci fermiamo qui, per ora, nel prossimo articolo metteremo dei dati in una collezione di classi di questo tipo.