Technology Experience

.NET World

Programmazione, libri, snippet di codice, articoli tecnici

.NET World

Il mio VivendoByte Emoticons plug-in

Siccome molti me lo chiedono via e-mail, e comunque ne aprofitto per riproporlo alla platea, comunico che ho reso nuovamente disponibile il download del mio plug-in per Windows Live Writer per inserire le emoticon.

Invece di metterlo banalmente su un sito ftp e darvi l’url, ho pensato di sfruttare la piattaforma Sharepoint per mettere on-line un po’ di roba. Ne parlerò in seguito. Dato che WH4L ogni anno si becca i miei soldini, vediamo di spendere ed usare per bene le cose che ho. 🙂

Detto questo, questa è una lista Sharepoint che ho denominato .NET Download, sulla quale pubblicherò i vari progetti freeware su cui ho lavorato in passato e magari mi capiterà di lavorare in futuro. Il download è un semplice file zip che contiene la dll da copiare in C:ProgrammiWindows Live WriterPlugins per fare in modo che WLW se la carichi all’avvio. Ci pensa lei a copiare qualche decina di files GIF per le emoticon in C:Documents and SettingsAll UsersDati applicazioniVivendoByte Emoticons.

Un commento tecnico a caldo: ho ripreso stamattina in mano il progetto proprio perchè volevo pubblicarlo. Sono inorridito, perchè creai quel plug-in mentre leggevo il libro di Petzold e quindi non sapevo bene come usare data-binding in primis, più un altro cumulo di caratteristiche di WPF. Stamattina, dicevo, ho riaperto il progetto, e ho eliminato praticamente il 95% del codice, praticamente ottenendo le stesse identiche funzionalità. Incredibile. Il tutto principalmente bindando un controllo all’altro, usando i template e cose del genere. Bello, son contento.

Per scaricare il plug-in cliccare qui

Technorati tags : , , ,

Send to Kindle
.NET World

What Technologies Should I Focus On?

Riprendo il titolo dal post di un certo Kirk Allen che si chiede appunto quali sono le tecnologie che bisognerebbe padroneggiare meglio per dominare il mercato. Non sono per nulla d’accordo con Kirk, perchè mettere Sharepoint 2007 al primo post, e Silverlight e ASP.NET rispettivamente al 2° ed al 3° posto è da folli, così come includere Virtual Earth nell’elenco. Sono più in sintonia invece con il post originario, che comunque rivoluziono come segue:

1) Windows Presentation Foundation
2) Sharepoint 2007
3) Windows Workflow Foundation
4) ORM
5) Windows Communication Foundation
6) ADO.Net e LINQ
7) Mobile (.NET CF, WM6.0)
8) Community
9) DSL (Domain Specific Language)
10) Silverlight

Le prime cinque sono nell’ordine in cui mi interessano. Dalle altre forse bisognerebbe rivedere qualcosa, ma almeno ho centrato le 10 che volevo elencare. Non ditemi che community non va bene, perchè altri hanno messo learning o security, perciò… 🙂

Send to Kindle
.NET World

[5] Il ritorno dello HockeyPlayer (data-binding con WPF)

Proseguo la mia serie di articoli con una piccola riflessione che ho fatto fra me e me ieri sera mentre stavo lavorando. Per spiegarla, riprendo un blocco XAML del post [3] Il ritorno dello HockeyPlayer (data-binding con WPF), nel punto in cui definisco la ListBox che contiene i vari HockeyPlayer:

<ListBox Name="lstPlayers" ItemsSource="{Binding Source={StaticResource players}, Path=HockeyPlayer}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Margin="4" Orientation="Horizontal"> <TextBox Text="{Binding Path=Name}" Margin="4" Width="120" /> <TextBox Margin="4" Text="{Binding Path=Weight}" Background="LightCoral" Width="30" /> <TextBlock Margin="4" Text="{Binding ElementName=sldHeight, Path=Value}" Width="60" /> <Slider Name="sldHeight" Value="{Binding Path=Height}" Minimum="140" Maximum="230" Width="120" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>

Osserviamo come la definizione del template col quale deve essere disegnato ogni Item della ListBox è fatto in corrispondenza della definizione della ListBox stessa. All’interno del tag <ListBox></ListBox> si definisce il DataTemplate, che è composto da uno StackPanel e via dicendo. Questo è quello che sono sempre stato abituato a vedere.

La mia domanda è: posso spostare la definizione del template in uno XAML dedicato? In altre parole, posso creare uno UserControl separato e staccato e poi referenziarlo e riutilizzarlo nel template? La risposta è sì. I motivi possono essere molteplici: se creo uno UserControl, posso utilizzare la stesso oggetto in più punti della mia applicazione, per favorire ad esempio una certa omogeinità nell’interfaccia. Se creo uno UserControl, questo è più facilmente debuggabile. Se creo uno UserControl, il codice XAML è meno prolisso, più leggibile e più facilmente modificabile.

Per farlo, è sufficiente aggiungere al progetto un nuovo UserControl (WPF), dargli il nome che vogliamo (ad esempio, HockeyPlayerTemplate) e copiare all’interno del blocco <UserControl></UserControl> lo XAML che definisce i controlli:

<UserControl x:Class="MyItemTemplate.HockeyPlayerTemplate" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:a="clr-namespace:MyItemTemplate"> <StackPanel Margin="4" Orientation="Horizontal"> <TextBox Margin="4" Width="120" Style="{StaticResource mandatory}" Text="{Binding Path=Name}" /> <TextBox Margin="4" Text="{Binding Path=Weight}" Background="LightCoral" Width="30" /> <TextBlock Margin="4" Text="{Binding ElementName=sldHeight, Path=Value, Converter={StaticResource conv}}" Width="60" /> <Slider Name="sldHeight" Minimum="50" Maximum="200" Width="120" Value="{Binding Path=Height}" /> </StackPanel> </UserControl>

Da notare che il resto è rimasto invariato. Continuo a fare uso del binding, anche se in questo contesto WPF non sa bene a cosa bindare. Non ha importanza, perchè poi a runtime ha tutte le informazioni che gli servono: in particolare, siamo noi che diciamo che ogni elemento della ListBox appartiene alla classe HockeyPlayer. Di conseguenza, il binding sulle proprietà Weight, Height e via dicendo funziona senza alcun problema.

Detto questo, la ListBox può essere definita come segue:

<ListBox Name="lstPlayers" ItemsSource="{Binding Source={StaticResource players}, Path=HockeyPlayer}"> <ListBox.ItemTemplate> <DataTemplate> <a:HockeyPlayerTemplate /> </DataTemplate> </ListBox.ItemTemplate> </ListBox>

Il DataTemplate fa riferimento grazie al prefisso a alla classe HockeyPlayerTemplate, che è proprio lo UserControl che abbiamo appena creato. Questo UserControl, lo ripeto, non solo può essere utilizzato come template per la ListBox, ma in ogni altro punto della nostra applicazione, qualora ci servisse.

Una precisazione. Nello UserControl va aggiunta una risorsa, il converter che abbiamo creato l’ultima volta. Questo converter viene utilizzato nel TextBlock per permettere di leggere il valore dello Slider senza decimali. Non è un problema, basta aprire lo XAML dello UserControl ed aggiungere tra le sue risorse la classe che fa da converter:

<UserControl.Resources> <a:SliderValueConverter x:Key="conv"/> </UserControl.Resources>

Domanda: perchè non dobbiamo inserire anche la risorsa denominata mandatory? Perchè, lo abbiamo visto precedentemente, questa risorsa è stata inserita fra quelle a livello di applicazione, e quindi è globale.

Send to Kindle
.NET World

Ma perchè alla fin fine è sempre Javascript???

Che io odi la programmazione Web ormai è risaputo. Ci riflettevo l’altro giorno: man mano che passa il tempo, per noi che sviluppiamo applicazioni Windows Forms, WPF, smart-client le cose evolvono davvero, la programmazione Web lato client rimane sempre la stessa. Non importa se viene creato AJAX o Silverlight, alla fin fine dall’altra parte del cavo c’è sempre il buon vecchio classico tradizionale e stupido browser e tutto gira semplicemente perchè c’è un runtime che interpreta un buon vecchio classico tradizionale e stupido Javascript.

L’altro giorno ho letto per caso il post di Alessandro Ghizzardi che parlava di Silverlight. Mi son detto: cavolo, Silverlight non è nient’altro che la trasposizione Web di WPF, magari ci capisco qualcosa e magari mi piace anche. Basta dare un’occhiata al pezzetto di codice per chiarirsi le idee e capire che sotto sotto il Javascript la fa ancora da padrone.

Il vero fascino dalla programmazione Web risiede ancora una volta (e oserei dire…sempre di più) sul lato server, con la nascita di linguaggi ed approcci sempre nuovi ed innovativi. Ma anche per quello ormai mi sono stufato!

Viva WPF, Viva gli smart-client, Viva le Windows Forms!!!

Send to Kindle
.NET World

Impostare un trigger quando TextBox.Text == string.Empty

Stamattina mi è venuta una piccola idea facilmente realizzabile in WPF. Immaginatevi una TextBox su una Window nella quale l’utente può digitare una qualsivoglia informazione. Supponiamo adesso di voler evidenziare in qualche modo quando la TextBox è vuota, magari per dire all’utente che quella casella è obbligatoria e che va riempita per forza.

La prima cosa che ho pensato è di impostare un trigger sulla TextBox stessa. Lo XAML iniziale è:

<TextBox Height="20" VerticalAlignment="Top" Margin="6" Name="txtUsername" />

Il trigger va impostato nelle risorse associate alla TextBox, per cui lo XAML va spezzato in questo modo:

<TextBox Height="20" VerticalAlignment="Top" Margin="6" Name="txtUsername"> <TextBox.Resources> </TextBox.Resources> </TextBox>

Nel blocco <TextBox.Resources></TextBox.Resources> inseriamo lo stile che contiene il trigger:

<Style TargetType="TextBox"> <Style.Triggers> <Trigger Property="Text.Length" Value="0"> <Setter Property="Background" Value="Red"/> </Trigger> </Style.Triggers> </Style>

La logica è: quando la proprietà Text.Length è 0, allora il Background della TextBox deve diventare rosso. Ma il codice non compila: l’errore è : Value ‘Text.Length’ cannot be assigned to property ‘Property’. Ma possiamo girare la frittata in un’altra maniera. Se riusciamo a far scatenare il trigger quando la proprietà Text == string.Empty, otteniamo esattamente lo stesso risultato. Ok, ragioniamo.

Empty è una proprietà statica della classe String. String è definita dentro il namespace System. L’assembly è mscorlib.dll. Ok, detto questo, procediamo. Importiamo nello XAML l’assembly che ci serve:

xmlns:s="clr-namespace:System;assembly=mscorlib.dll"

Il blocco qui sopra va incollato nella definizione della Window. A questo punto il trigger può essere impostato in questo modo (riporto l’intero blocco XAML che definisce la TextBox, che così potete copiare ed incollare facilmente):

<TextBox Height="20" VerticalAlignment="Top" Margin="6" Name="txtUsername"> <TextBox.Resources> <Style TargetType="TextBox"> <Style.Triggers> <Trigger Property="Text" Value="{x:Static s:String.Empty}"> <Setter Property="Background" Value="Red"/> </Trigger> </Style.Triggers> </Style> </TextBox.Resources> </TextBox>

In pratica, utilizzo x:Static per accedere alla proprietà statica Empty della classe String. Quando la condizione è verificata, il trigger si scatena ed il Background della TextBox diventa rosso. Ricordo che ci pensa WPF a riportare il Background allo stato originario nel momento in cui la condizione del trigger diventa falsa.

La cosa più comoda, IMHO, è spostare la definizione dello stile dentro il blocco <Application.Resources></Application.Resources> del file App.xaml, in modo tale che lo stile sia accessibile non solo a quella particolare TextBox, ma a qualsiasi TextBox della nostra applicazione alla quale decidiamo di associare quello stile. Mi spiego meglio: apriamo il file App.xaml e fra le risorse incolliamo questo blocco XAML:

<Style x:Key="mandatory" TargetType="TextBox"> <Style.Triggers> <Trigger Property="Text" Value="{x:Static s:String.Empty}"> <Setter Property="Background" Value="Red"/> </Trigger> </Style.Triggers> </Style>

Da notare che ho associato una Key allo stile, oltre che definire il Type sui quali possiamo associare lo stile stesso. Lo stile ha una Key = “mandatory”. Adesso possiamo semplicemente prendere tutte le TextBox che vogliamo rendere obbligatorie (perlomeno visivamente) e dire loro di usare quello stile particolare. Ad esempio:

<TextBox Height="20" VerticalAlignment="Top" Margin="6" Name="txtUsername" Style="{StaticResource mandatory}">

Ed il gioco è fatto. Ricordo che non c’è alcuna logica di validazione: l’utente può spostarsi da un controllo all’altro e via dicendo. Quello che abbiamo fatto ha solo un riscontro visivo e nulla di più.

Send to Kindle
.NET World

[4] Il ritorno dello HockeyPlayer (data-binding con WPF)

Ci siamo lasciati l’ultima volta con il seguente screenshot:

Per sapere come ho ottenuto un risultato simile con WPF è sufficiente leggere i miei post precedenti (questo, questo e quest’altro). Impostando la proprietà ItemTemplate della ListBox, abbiamo fatto in modo che ogni riga venga visualizzata esattamente come vogliamo noi. Su ciascuna riga, vediamo tutti i dati che ci interessa vedere: nome del giocatore, peso ed altezza. Possiamo modificare quest’ultima agendo su ciascuno Slider. Divertente, no?

La cosa brutta è – come si vede sopra – il fatto che il binding tra lo Slider e la TextBox produce un effetto che non mi piace. La proprietà Value dello Slider è di tipo double, e quindi la proprietà Text della TextBox mostra un double, con tutti i decimali del caso. Se vogliamo fare le cose per bene, dobbiamo mettere mano al codice…oserei dire, finalmente. Dobbiamo realizzare una classe che implementa l’interfaccia IValueConverter: lo scopo di tale classe è quello di convertire i valori che vengono utilizzati nel processo di Binding. Nel nostro caso, nei due sensi:

  1. dallo Slider alla TextBox (quindi, da double a string)
  2. dalla TextBox allo Slider (quindi, da string a double)

Siccome mi piace l’idea che l’utente utilizzi lo Slider, eliminiamo la seconda possibilità: trasformiamo la TextBox in TextBlock. Immaginate la TextBlock come la classica Label delle Windows Form: visualizza qualcosa ma ovviamente non permette alcuna modifica. Lo vediamo fra un attimo. Detto questo, possiamo tranquillamente eliminare la conversione del punto (2): scriveremo lo stesso il codice perchè non siamo pigri, ma sappiamo a priori che non verrà utilizzato.

Arriviamo al dunque. Implementiamo una classe SliderValueConverter:

1 [ValueConversion(typeof(double), typeof(int))] 2 public class SliderValueConverter : IValueConverter 3 { 4 public object Convert(object value, Type targetType, 5 object parameter, 6 System.Globalization.CultureInfo culture) 7 { 8 double input = (double)value; 9 int output = (int)input; 10 return (output); 11 } 12 13 public object ConvertBack(object value, Type targetType, 14 object parameter, 15 System.Globalization.CultureInfo culture) 16 { 17 int input = (int)value; 18 double output = (double)input; 19 return (output); 20 } 21 }

L’implementazione dell’interfaccia IValueConverter è molto semplice: c’è un metodo Convert ed un metodo ConvertBack. Il primo converte da double ad integer. Il secondo da integer a double. In parole semplici: quando il Value dello Slider sta per essere spedito alla TextBox, passa prima dalla nostra classe, subìsce la conversione in intero ed arriva alla Text della TextBox. Basta impostare un breakpoint alla linea (8) per avere la conferma. 🙂

L’implementazione della classe SliderValueConverter da sola non è sufficiente per compiere la magia. Occorre modificare anche lo XAML, per fornire qualche informazione in più. Vediamo come e dove.

Inseriamo tra le risorse dello XAML un’istanza della classe SliderValueConverter:

<Window.Resources> <a:HockeyPlayers x:Key="players" /> <a:SliderValueConverter x:Key="conv"/> </Window.Resources>

Ricordo che con il prefisso a facciamo riferimento al namespace MyItemTemplate, che è il namespace col quale stiamo sviluppando l’applicazione. Poi prendiamo lo XAML che definisce la TextBox che viene bindata allo Slider e modifichiamolo come segue:

<TextBlock Margin="4" Text="{Binding ElementName=sldHeight, Path=Value, Converter={StaticResource conv}}" Width="60" />

Nel Binding abbiamo adesso forniamo l’ElementName, il Path ed infine il Converter, che per l’appunto fa uso della risorsa identificata dalla key conv. Compiliamo ed eseguiamo. Se tutto è ok, adesso quando spostiamo lo Slider vediamo qualcosa di un po’ più carino e leggibile (soprattutto per l’utente finale):

Stay tuned, perchè mica è finita qua! 🙂

Send to Kindle
.NET World

[3] Il ritorno dello HockeyPlayer (data-binding con WPF)

Ci sono diversi metodi per rendere un po’ più gradevole l’interfaccia della nostra applicazione. Adesso la ListBox mostra ogni HockeyPlayer mostrando solo il nome – comportamento, abbiamo detto, dovuto al fatto che abbiamo fatto l’override del metodo ToString() della nostra classe. Con WPF possiamo andare ben oltre. Chi sta leggendo la mia serie di post su Flickr ne sa qualcosa. 🙂

Questo diventa realizzabile fornendo un template per ogni item che vogliamo visualizzare sulla ListBox. Questo template può essere un Panel di qualche tipo, che contiene altri controlli, tutti bindati a determinate proprietà della classe HockeyPlayer. Vediamo bene e con calma come farlo in pratica. Lo XAML è il seguente:

<Window x:Class="MyItemTemplate.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:a="clr-namespace:MyItemTemplate" Title="MyItemTemplate" Loaded="Window1_Loaded" Width="390" Height="430"> <Window.Resources> <a:HockeyPlayers x:Key="players" /> </Window.Resources> <StackPanel> <ListBox Name="lstPlayers" ItemsSource="{Binding Source={StaticResource players}, Path=HockeyPlayer}" /> </StackPanel> </Window>

Alla Window ho aggiunto una risorsa la cui key è players, che viene utilizzata come ItemsSource della ListBox. Nell’evento Loaded ottengo il riferimento alla risorsa, che è di tipo HockeyPlayers. Trattandosi di una lista, la popolo con 10 HockeyPlayer a caso e la riassegno alla proprietà ItemsSource della ListBox per trovamela popolata. Notare l’utilizzo del Binding: imposto il Source – dicendo che si tratta della risorsa players – ed imposto anche Path, che serve a specificare il tipo che viene bindato su ciascun Item. In questo caso, si specifica che ogni Item appartiene al tipo HockeyPlayer.

E’ arrivata l’ora di assegnare un template alla ListBox. Per farlo, è sufficiente lavorare sulla proprietà ItemTemplate della ListBox, impostandola ad esempio così:

<ListBox Name="lstPlayers" ItemsSource="{Binding Source={StaticResource players}, Path=HockeyPlayer}"> <ListBox.ItemTemplate> <DataTemplate> <StackPanel Margin="4" Orientation="Horizontal"> <TextBox Text="{Binding Path=Name}" Margin="4" Width="120" /> <TextBox Margin="4" Text="{Binding Path=Weight}" Background="LightCoral" Width="30" /> <TextBlock Margin="4" Text="{Binding ElementName=sldHeight, Path=Value}" Width="60" /> <Slider Name="sldHeight" Value="{Binding Path=Height}" Minimum="140" Maximum="230" Width="120" /> </StackPanel> </DataTemplate> </ListBox.ItemTemplate> </ListBox>

Fantastico! Dunque, vediamo cosa è successo. Assegniamo la proprietà ItemTemplate della ListBox: il template è composto da uno StackPanel che contiene 3 TextBox ed uno Slider. La prima TextBox visualizza il nome del giocatore, mentre la seconda il suo peso. La terza TextBox e lo slider lavorano assieme: lo slider è bindato all’altezza del giocatore, mentre la TextBox visualizza il valore effettivo, cosa che lo slider non può fare. In pratica, mentre prima potevamo modificare l’altezza di uno HockeyPlayer solo dopo averlo selezionato dalla ListBox, adesso lo slider compare su ogni riga ed abbiamo subito un impatto un po’ più immediato. Ecco un esempio:

Il tutto senza scrivere una sola linea di codice e, come disse una volta Corrado, con una mano sola. 🙂

Adesso posso tranquillamente agire su ciascuno Slider per modificare l’altezza di ogni giocatore. Vogliamo fare la prova del 9 come alle elementari? Aggiungiamo un pulsante allo StackPanel

<Button Margin="6" Name="btnShowCurrentHeight" Content="Show Height" Click="btnShowCurrentHeight_Click" />

e scriviamo l’event handler specificato:

void btnShowCurrentHeight_Click(object sender, RoutedEventArgs args) { HockeyPlayer pl = lstPlayers.SelectedItem as HockeyPlayer; if (pl != null) { MessageBox.Show(pl.Height.ToString()); } }

Lanciamo l’applicazione, spostiamo un po’ gli Slider a sinistra e a destra, selezioniamo una riga a caso della ListBox e clicchiamo sul pulsante che abbiamo appena creato. Visualizza l’altezza che abbiamo determinato con lo Slider. Se con un DAL riuscissimo a salvare ogni istanza di HockeyPlayer (oppure serializzando su file), salveremmo i valori così come li vediamo sulla UI.

C’è ancora qualcosa da sistemare, primo fra tutti fare in modo che il binding sulla TextBox non visualizzi tutti quei decimali. Ma sarà l’occasione per un successivo post.

Send to Kindle
.NET World

[2] Il ritorno dello HockeyPlayer (data-binding con WPF)

Se avete seguito le indicazioni del post precedente, vi ritroverete fra le mani un’applicazione WPF che mostra una cosa simile a quella riportata qui sotto:

Fino ad adesso, non abbiamo in alcun modo fatto uso di data-binding. Cominciamo adesso. Invece di aggiungere manualmente uno ad uno ogni oggetto alla ListBox, possiamo utilizzare la proprietà ItemsSource della ListBox. Modifichiamo l’event handler associato all’evento Loaded della Window:

void Window1_Loaded(object sender, RoutedEventArgs e) { HockeyPlayers coll = new HockeyPlayers(); HockeyPlayer pl; for (int i = 0; i < 10; i++) { pl = new HockeyPlayer("Name " + i, 180 + i, 96 + i, 14 + i); coll.Add(pl); } lstPlayers.ItemsSource = coll; }

Creiamo un’istanza di HockeyPlayers ed aggiungiamo a questa lista 10 istanze di HockeyPlayer, con gli stessi valori di prima. Alla fine, assegniamo alla proprietà ItemsSource della ListBox l’oggetto coll che abbiamo creato via codice. Questa cosa è tanto interessante che a questo punto possiamo modificare lo XAML aggiungendo un po’ di controlli in questo modo:

<TextBox Text="{Binding ElementName=lstPlayers, Path=SelectedItem.Weight}" /> <Slider Name="sdrHeight" Value="{Binding ElementName=lstPlayers, Path=SelectedItem.Height}" Minimum="140" Maximum="230" SmallChange="1" LargeChange="5" /> <TextBox Text="{Binding ElementName=sdrHeight, Path=Value}" />

La prima TextBox è bindata alla ListBox ed accede alla proprietà Weight dello HockeyPlayer correntemente selezionato sulla lista.
Poi ho messo uno slider, che in pratica permette di vedere e modificare l’altezza del giocatore selezionato sulla ListBox: ho bindato la proprietà Value, che accede alla proprietà Height del SelectedItem. Ho impostato altre proprietà dello slider (minimo, massimo, etc.).
L’ultima TextBox riporta il valore dello slider, altrimenti l’utente sposta lo slider ma non capisce il valore dell’altezza che sta impostando. 🙂

Il risultato di tutto ciò è che quando cambiamo la selezione sulla ListBox i controlli riflettono il cambiamento, mostrando peso ed altezza del giocatore selezionato e permettendone la modifica.

Abbiamo dimostrato (e non ci voleva granchè) che per fare data-binding non è assolutamente vero che le proprietà pubbliche che vogliamo bindare debbano essere obbligatoriamente dependency properties (come sostenevo io :-), ma è sufficiente che la classe implementi l’interfaccia INotifyPropertyChanged. Questo vale sia per WPF, di cui abbiamo parlato adesso, che con le classiche Windows Forms.

Ma l’interfaccia rimase decisamente bruttina. Stay tuned!

Send to Kindle
.NET World

[1] Il ritorno dello HockeyPlayer (data-binding con WPF)

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.

Send to Kindle
.NET World

XAML è un XML, non è solo di WPF e soprattutto non è lento…

Con questo piccolo post voglio sfatare diversi miti. Non mi sto inventando nulla, altre persone molto più illustri di me l’hanno già ribadito, ma voglio scrivermelo anche io per dirlo principalmente a chi legge il mio blog. Nel mio blog ho parlato nei giorni scorsi di WPF (Windows Presentation Foundation), perchè è una tecnologia che mi affascina e che ritengo estremamente potente. Parlando di WPF, non si può non parlare di XAML. Ma cos’è davvero XAML?

XAML è un dialetto XML. Come ogni XML, deve essere ben formattato, ogni tag aperto deve avere il suo tag di chiusura e roba del genere. XAML è un XML introdotto nel FX3.0 di Microsoft e non riguarda solo WPF. XAML serve anche a WPF, ma per esempio anche a WF: lo ha chiaramente indicato Raffaele durante il meeting di XeDotNet.

Ma siccome qui stiamo parlando di WPF, cos’è XAML nel contesto di WPF ?

XAML serve a descrivere una gerarchia di oggetti, ma non solo visuali. Perciò dire che XAML serve a descrivere l’interfaccia utente è solo parzialmente vero. XAML può essere utilizzato anche per esprimere altri contenuti, come risorse di ogni tipo (occhio: davvero di ogni tipo), comportamenti, triggers, stili, animazioni, pannelli custom, trasformazioni grafiche, data-binding, altre classi .NET che a run-time saranno – magari – invisibili e via dicendo. XAML serve a definire una Window, una Page od uno UserControl nella sua interezza, quindi, e non solo per quanto riguarda il suo aspetto grafico.

L’ultima cosa che voglio dire è che la lunghezza di un file XAML non ha alcuna correlazione con le dimensioni dell’assembly che verrà generato. Un file XAML può anche essere immenso, perchè l’XML è un po’ prolisso e XAML ha una sintassi che magari non aiuta in questo senso. Però francamente preferisco leggere XAML che almeno è chiaro e semplice. Magari è questione di abitudine. Voglio ricordare che ciascun file XAML viene compilato per ottenere il corrispondente file BAML (Binary Application Markup Language). Come dice Nathan…

BAML is a compressed declarative format that is faster to load and parse (and smaller in size) than plain XAML.

In altre parole, XAML è efficiente quando stiamo sviluppando, perchè è leggibile, chiaro e semplice. BAML è efficiente a run-time, perchè è compresso e più piccolo in termini di bytes (e forse questo è quello che ci interessa di più).

Che poi su determinati PC un’applicazione WPF sia lenta dipende da una miliardata di motivi, dalla scheda grafica (hardware) a cosa fa realmente l’applicazione mentre è aperta e sta girando (software)…

Send to Kindle