Press "Enter" to skip to content

14 – Lavorare con i dati – User Interface costruiamo la Window ed il suo ViewModel

In questo articolo (finalmente direte voi) andremo a creare la Window per inserire e modificare i dati della tabella Users, e ovviamente per fare tutto ciò costruiremo il ViewModel che collega i dati alla Window.

Come abbiamo più volte indicato, oltre ad averlo realizzato in molti degli esempi WPF che si trovano sugli articoli pubblicati nel corso dello scorso 2016, è possibile che il ViewModel di una window o di uno user control WPF siano generati direttamente nel code behind della stessa Window o dello User Control, oppure possono essere creati su una classe diversa.

C’è un pattern specifico per decidere se fare una cosa o un altra? Usualmente il buon senso, anche se sono certa che vi siano articoli scritti da esperti molto più bravi di me che ve lo spiegano. Per quel che mi riguarda, il ragionamento da fare è il solito:

Keep it Simple, Keep it Clear – La semplicità e la chiarezza prima di tutto, per essere in grado di modificare un applicazione anche a distanza di anni senza trovarsi a perdere giornate per cercare le cose.

Nella maggior parte dei casi un View Model su una classe separata, io lo creo quando la Window o lo User Control è molto complesso. Ad esempio la finestra principale di una applicazione, uno User control che gestisce una tabella complessa e così via. Ma è solo una scelta personale, ognuno di voi può decidere diversamente, valutate sempre caso per caso.

Visto che stiamo scrivendo un esempio di applicazione “complessa” e Multi Tier, il view model della finestra che gestisce il CRUD degli User sarà una classe a se stante. Pertanto andiamo a crearla e demoliamo quanto abbiamo fatto nelle puntate precedenti per fare il test delle librerie fino a qui costruite.

Per prima cosa nel progetto UsersDb, sotto la cartella Windows andiamo ad aggiungere una nuova cartella Models (menu contestuale Add folder).

Aggiunta la cartella, posizioniamo la selezione sulla cartella Models e aggiungiamo una classe:

13_usersdb_09_01_AddViewModel

La chiamiamo UsersWindowModel, perché in questo caso è il ViewModel di una singola Window. Ricordate che anche il nome degli oggetti in un progetto vi aiuterà in seguito a trovarli quando li modificherete dopo mesi o anni pertanto scegliete i nomi in modo che siano facilmente comprensibili. Questo è il motivo per cui le mie Window terminano con il nome Window e i miei Model con la parola Model, ma come sempre nella nomenclatura siete voi a decidere.

13_usersdb_09_02_viewmodel_class

Il solution explorer risulterà come illustrato qui sopra, il motivo per cui metto la cartella dei Models sotto alla cartella Windows e non sulla cartella root è perché in una applicazione complessa dove i viewmodels potrebbero essere molti preferisco tenerli vicino al luogo d’uso. Perciò quelli per le Window sotto alla cartella Windows, quelli per gli User control sotto la cartella Controls, ma anche in questo caso si tratta di scelte arbitrarie, dovete decidere voi in base all’applicazione che state creando.

namespace Dnw.Users.Windows.Models
{
	public class UsersWindowModel : INotifyPropertyChanged
	{
 
		public UsersWindowModel()
		{
 
		}
 
		public event PropertyChangedEventHandler PropertyChanged;
 
		protected virtual void OnPropertyChanged(string propertyName)
		{
			if (PropertyChanged != null)
				PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
		}
 
	}
}

La prima cosa che facciamo nella nostra classe viewmodel è implementare l’interfaccia INotifyPropertyChanged, che ci permetterà di sollevare l’evento PropertyChanged in tutte le property della nostra classe in modo da far reagire automaticamente la User Interface alle modifiche ai dati.

Fatto questo, ci spostiamo in UsersWindow.xaml.cs e spostiamo tutte le property con le variabili e le costanti collegate all’interno della classe ViewModel, spostiamo anche l’inizializzazione di Users e quella di UseSqlServer che si trovano nel costruttore della Window all’interno del costruttore di UsersWindowModel. Visto che entrambe le property si trovano in tale classe.

Creiamo poi all’interno del ViewModel due metodi pubblici:

public void TestTheEntity()
{
}
public void TestTheservices()
{
}

All’interno di questi due metodi copiamo il contenuto dello statement try degli event handler corrispondenti nel code behind.

Fatto questo modifichiamo la nostra classe del code behind in questo modo:

private UsersWindowModel mModel;
private UsersWindowModel Model
{
	get
	{
		return mModel;
	}
	set
	{
		mModel = value;
	}
}

Definiamo la property che conterrà il ViewModel come potete notare è private, perché non sarà direttamente utilizzata dallo xaml.

public UsersWindow()
{
	InitializeComponent();
	this.Icon = BitmapFrame.Create(new Uri("pack://application:,,,/icon_2016.ico", UriKind.RelativeOrAbsolute));
	Model = new UsersWindowModel();
	DataContext = Model;
}

Modifichiamo il costruttore della classe in modo da eliminare quello che non è più contenuto nella window e istanziamo il Model per poi assegnarlo alla variabile DataContext della Window. Questo farà in modo che il Binding che abbiamo già creato in precedenza sui controlli della window funzioni esattamente come prima.

private void TestDataProvider_Click(object sender, RoutedEventArgs e)
{
	try
	{
		Model.TestTheservices();
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
	}
}
 
private void TestEntity_Click(object sender, RoutedEventArgs e)
{
	try
	{
		Model.TestTheEntity();
	}
	catch (Exception ex)
	{
		MessageBox.Show(ex.Message, "ERROR", MessageBoxButton.OK, MessageBoxImage.Error);
	}
}

Modifichiamo i due event handler dei button in modo tale che chiamino i metodi che abbiamo trasferito nel model. fatto questo, spostando opportunamente anche le clausole using necessarie nel ViewModel, la nostra applicazione di test dei tre diversi database dovrebbe funzionare esattamente come in precedenza.

Ma come presumo si possa notare osservando le due classi, il Code Behind è molto più pulito, ed il View Model non ha alcun tipo di riferimento ai controlli che si trovano sulla window, tutto funziona solo grazie al Binding delle property della classe viewmodel sugli attributi dello XAML.

Abbiamo quindi cambiato il paradigma di programmazione della User Interface da Event driven a Data Driven.

Riepilogo

Mi fermo qui per darvi modo di avere una versione del codice pulito e con il ViewModel perché adesso inizieremo a inserire nello xaml i controlli per gestire la tabella e quindi modificheremo quello che abbiamo fatto per i test in modo estensivo.

Cosa abbiamo spiegato in questo articolo:

  • Come creare un ViewModel per una Window
  • Come collegarlo al DataContext perché possa essere usato dallo XAML
  • Come trasferire nel viewmodel quello che è di sua pertinenza
  • Come lasciare nel code behind solo quello che lo riguarda

Potete scaricare il codice del progetto al seguente link:

Per qualsiasi domanda, curiosità o per segnalare un errore, usate pure il link al modulo di contatto che trovate a inizio pagina.