WPF, LINQ e Binding
Con questo articolo voglio giocare con un paio di nuone tecnologie offerte da Microsoft.
Seguendo l’esempio offerto da Beatriz Costa al TechEd 2008, ho sviluppato qualcosa di analogo. Librerie e Libri
L’esempio in se non è nulla di complicato ma è bello poter vedere in azione queste nuove tecnologie.
Il progetto che andremo a realizzare con VS2008 sarà un WPF Browser Application.
La nostra pagina principale sarà Page1.xaml e aggiungeremo solamente 3 files in più al progetto:
XMLFile1.xml
Questo sarà il nostro file d’appoggio, il quale conterrà le informazioni che vogliamo presentare all’utente.
Parte del contenuto di questo file si presenta così:
<ListBooks>
<BookObjectTitle="Extreme Programming Explained: Embrace Change">
<ImageFileName>Images\xp_guide_small.jpg</ImageFileName>
<Author>Kent Beck</Author>
<Published>25 Nov 2004</Published>
</BookObject> Notate, avrete bisogno di una directory Images dove dovrete andare a inserire l’immagine da caricare relativa al libro.
BookObject
Questa è una semplicissima classe contenitore che useremo per definire un libro all’interno della nostra libreria.
public class BookObject
{
public string Title { get; set; }
public string ImageFileName { get; set; }
public string Author { get; set; }
public string Published { get; set; }
public override string ToString()
{
return Title + " " + Author;
}
}
Library
Finalmente un pò di codice.
public class Library
{
public Library()
{
Uri res = new Uri(@"pack://application:,,,/XMLFile1.xml", UriKind.Absolute);
// The Application object wraps a set of XAML files and creates a shared data environment between the
// pages. The Application object loads with the initial load and provides shared variables at that time.
StreamResourceInfo sri = App.GetResourceStream(res);
StreamReader reader = new StreamReader(sri.Stream);
XDocument booksXml = XDocument.Load(reader);
_libraryObjects = new List<BookObject>();
var book = from books in booksXml.Descendants("BookObject")
select new BookObject
{
Title = books.Attribute("Title").Value,
ImageFileName = books.Element("ImageFileName").Value,
Author = books.Element("Author").Value,
Published = books.Element("Published").Value
};
foreach (var bd in book)
_libraryObjects.Add(bd);
}
private List<BookObject> _libraryObjects;
public List<BookObject> LibraryObjects
{
get { return _libraryObjects; }
}
}
Analizziamo nel dettaglio il lavoro di questa classe.
Per prima cosa dovrà aprire il nostro file XMLFile1.xml e leggerne il contenuto.
Per motivi di sicurezza i progetti di questo tipo, Browser Application, non hanno diritti di lettura e scrittura sul nostro file system se non nelle directory utilizzate da IsolatedStorageFile. Per questo motivo dovremo specificare nelle proprietà del XMLFile1.xml:
· Build Action: Resource
In questa maniera il file verrà inserito all’interno del nostro eseguibile e sarà disponibile per la lettura.
Per poter leggere il file, WPF, mette a disposizione le URI (uniform resource identifiers). Quindi specificheremo un nuovo URI, puntando sul file all’interno della nostra applicazione.
Avendo adesso il riferimento al nostro file, dobbiamo farlo leggere a LINQ che non è a conoscenza di come sono organizzate le nostre risorse.
Quindi andremo a prendere la risorsa tramite la classe Application.
La classe Application è un’interfaccia tra il nostro sistema e l’applicazione. È l’entry point delle nostre applicazioni WPF.
Nel nostro caso specifico, troveremo questa nel file App.xaml.cs
Per ulteriori informazioni quì. Acquisito il riferimento al nostro file e creati gli Streams opportuni, lasceremo a LINQ l’arduo compito di leggere i dati all’interno del file.
Questo procedimento è alquanto semplice, almeno nel nostro caso.
Creando una query come quella all’interno del nostro codice, LINQ si occuperà di tirar fuori tutti i dati relativi ai libri presenti nel file XML e inserirà questi dati all’interno della variabile book.
Adesso che abbiamo a disposizione tutti i dati che sono all’interno del nostro file xml, ne inseriremo i riferimenti all’interno della nostra collection:
List<BookObject> _libraryObjects;
Page1.xaml
A questo punto la nostra applicazione è pronta all’80%.
Ci rimane solamente da bindare i dati all’interno della nostra pagine XAML e definire come vogliamo visualizzarli.
Per quanto concerne il concetto di bindare i dati all’interno di un’applicazione WPF vi consiglio di vedere il video di Beatriz Costa, il quale vi potrà chiarire le idee. Sappiate solamente che il binding è una relazione che creiamo tra la nostra UI e la nostra sorgente di dati.
Quello che vedremo in questo tutorial è come bindare dei dati presenti in memoria che vengono modificati durante l’esistenza dell’applicazione.
<StackPanel x:Name="LayoutRoot" HorizontalAlignment="Center" VerticalAlignment="Top">
<ListBox ItemsSource="{Binding Source={StaticResource myLibrary}, Path=LibraryObjects}"
ScrollViewer.VerticalScrollBarVisibility="Auto" Height="480" Width="500" />
<!--<ComboBox ItemsSource="{Binding Source={StaticResource myLibrary}, Path=LibraryObjects}" SelectedIndex="0"/>-->
</StackPanel>
Con queste poche righe di XAML noi stiamo già bindando i nostri dati e potrebbero essere già disponibili.
Se non ci credere, provate a completare in questa maniera:
· Inserite il seguente codice sopra lo StackPanel poc’anzi definito:
<Page.Resources>
<local:Library x:Key="myLibrary" />
Ecco adesso la vostra ListBox con i dati bindati.
Già così abbiamo ottenuto ciò che volevamo ma, per concludere, diamo un’aspetto migliore ai nostri dati.
Per far ciò dobbiamo solamente dire a WPF come vogliamo, appunto, visualizzarli.
Per farlo bisogna utilizzare un DataTemplate.
Il nostro DataTemplate è stato definito nella seguente maniera:
<DataTemplate DataType="{x:Type local:BookObject}">
<StackPanel Orientation="Horizontal" Margin="3" TextBlock.FontSize="12" TextBlock.FontFamily="Tahoma">
<Image Source="{Binding Path=ImageFileName}" Margin="0,0,3,0" />
<StackPanel Width="500">
<TextBlock FontWeight="Bold" Text="{Binding Title}" />
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Author}" />
<TextBlock Text="{Binding Published}" />
</StackPanel>
</StackPanel>
</StackPanel>
</DataTemplate>
</Page.Resources>
In pratica stiamo definendo i StackPanel che conterranno i nostri dati, cosa dovranno contenere e come visualizzarli.
Esempio, il primo StackPanel definisce come il testo verrà visualizzato e al suo interno ha un Controllo di tipo Image che viene bindato all’oggetto ImageFileName presente all’interno della classe BookObject.
Nel codice ho lasciato commentato un Control di tipo ComboBox.
Provate a decommentare la ComboBox e a commentare la ListBox, e potrete capire come le WPF vi aiuteranno a relazionarvi e come sono strettamente relazionate a pattern quali MVP.
Il progetto completo lo trovate quì.