Vi ricordate la mia piccola serie di post su NHibernate che parlava di giocatori di hockey? In breve: si trattava di persistere una classe HockeyPlayer su database, usando NHibernate come DAL. E’ sufficiente googlare per raggiungere il primo post della serie. Voglio riprendere le stesse classi viste allora per introdurre i concetti base che ruotano attorno al meccanismo di data-binding di WPF e soprattutto per confermare (più a me stesso che a voi) che Corrado ha pienamente ragione quando dice che le proprietà di una classe non devono per forza essere dependency properties per fungere da sorgente per il binding.
Vediamo di sviscerare pian piano il mistero. Supponiamo di avere un domain model il cui SuperType (a type that acts as the supertype for all types in its layer) è rappresentato dalla classe AbstractEntity, che è implementata nel modo seguente:
public abstract class AbstractEntity : INotifyPropertyChanged
{
private int id;
public int ID
{
get { return id; }
set { id = value; NotifyPropertyChanged("ID"); }
}
// Definizione del public event
// e del metodo NotifyPropertyChanged
}
Questa è una classe astratta, dalla quale derivano le classi concrete che compongono il domain model della nostra applicazione. Per questo piccolo esempio, supponiamo di avere una sola semplice classe, HockeyPlayer appunto, che deriva da AbstractEntity e aggiunge qualche proprietà pubblica. Il codice è il seguente:
public class HockeyPlayer : AbstractEntity, INotifyPropertyChanged
{
private string name;
private int height, weight, number;
public string Name
{
get { return name; }
set { name = value; NotifyPropertyChanged("Name"); }
}
public int Height
{
get { return height; }
set { height = value; NotifyPropertyChanged("Height"); }
}
// Definizione delle altre proprietà (Weight e Number),
}
Per brevità, ho omesso parte del codice. Per adesso, ci interessa sapere che HockeyPlayer espone quattro proprietà: Name, Height, Weight, Number (rispettivamente: nome, altezza, peso e numero di maglia). La cosa che voglio sottolineare è che queste proprietà sono proprietà normalissime, non hanno alcuna dipendenza con WPF. Se dovessimo sviluppare questa applicazione per console, per Windows Form, per ASP.Net, per un Windows Service, la implementeremmo nella stessa maniera. Altra cosa da sottolineare è che non è stato fatto l’override di ToString(). Per comodità ho implementato anche una classe HockeyPlayers (notare la ‘s’ finale), che non è nient’altro che una List<HockeyPlayer>: ci servirà con WPF più avanti.
Ok, passiamo ad aggiungere al progetto una Window molto molto semplice. Lo XAML è:
<Window x:Class="MyItemTemplate.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MyItemTemplate" Loaded="Window1_Loaded">
<StackPanel>
<ListBox Name="lstPlayers" />
</StackPanel>
</Window>
In pratica, è una Window che contiene uno StackPanel che a sua volta contiene una ListBox lstPlayers. Nel code-behind buttiamo giù due linee di codice per popolare la ListBox con un po’ di HockeyPlayer sparati a caso:
void Window1_Loaded(object sender, RoutedEventArgs e)
{
HockeyPlayer pl;
for (int i = 0; i < 10; i++)
{
pl = new HockeyPlayer("Name " + i, 180 + i, 96 + i, 14 + i);
lstPlayers.Items.Add(pl);
}
}
Sull’evento Loaded della Window faccia partire un ciclo for che crea 10 istanze di HockeyPlayer, con un po’ di valori a caso. Utilizzo il costruttore con quattro parametri per brevità di scrittura (l’ho fatto apposta!). Se eseguiamo il codice apparirà una Window fatta nel modo seguente:
Veramente brutta, vero? E poi, perchè appare quella schifezza invece di qualcosa di più sensato? Questo comportamento non è proprio di WPF, perciò non date la colpa a lui. La responsabilità è di .NET. Sta di fatto che avremmo ottenuto esattamente la stessa cosa se avessimo realizzato una tradizionale Windows Form. Il CLR non sa come convertire una nostra classe in stringa, perchè nessuno gli dice come fare: in preda al dubbio, .NET se la cava mostrando banalmente il nome completo della classe, inteso come namespace.classname. Io non ve l’ho detto, ma tutto il progetto si trova nel namespace MyItemTemplate, da cui il motivo dei 10 Items che appaiono qui sopra. Ci sono diversi modi per risolvere questo punto: il più breve (che non è quasi mai il più divertente) consiste nel fare l’override di ToString() all’interno della nostra classe HockeyPlayer. Se per esempio volessimo visualizzare il nome del giocatore, è sufficiente scrivere quanto segue:
public override string ToString()
{
return name;
}
Se rilanciassimo l’applicazione, al posto di MyItemTemplate.HockeyPlayer, vedremmo il nome di ciascun giocatore, nel nostro caso 10 stringhe che vanno da “Name 0” a “Name 9”. Non è il massimo comunque, forse un pochino meglio, ma per adesso ci fermiamo qui.