[Flickr.4] Usare la ListBox di WPF con binding ed ItemTemplate
Questa volta arrivo subito al sodo. Nell’ultimo post avevamo ottenuto l’elenco dei set fotografici associati ad un particolare utente. Lo scopo è quello di elencare questi set in qualche modo e di farli vedere in maniera gradevole all’utente che sta usando la nostra applicazione. La window principale si chiama MainWindow e questo è il suo XAML (non completo):
<Grid> <Grid.RowDefinitions> <RowDefinition Height="50" /> <RowDefinition Height="*" /> </Grid.RowDefinitions> <StackPanel Orientation="Horizontal" Grid.Row="0" VerticalAlignment="Center"> <TextBox Name="UsernameTextBox" Text="IgorDamiani" Width="150" Margin="4" /> <b:FlickrButton x:Name="ListPhotoSets" Content=" List Photo Sets " Margin="4" Click="listPhotosets" /> <b:FlickrButton x:Name="WebToUserPhotos" Content=" See User's Photos " Margin="4" Click="webToUserPhotos" IsEnabled="False" /> <b:FlickrButton x:Name="WebToUserWorldMap" Content=" See User's World Map " Margin="4" Click="webToUserWorldMap" IsEnabled="False" /> </StackPanel> <ListBox Name="PhotoSetListBox" Grid.Row="1" MouseDoubleClick="PhotoSetListBox_MouseDoubleClick" ItemsSource="{Binding Source={StaticResource sets}, Path=FlickrBrowserPhotoset}" /> </Grid>
Lo XAML è concettualmente piuttosto semplice. In pratica, contiene una Grid con due sole righe. La prima ha un’altezza fissa e contiene uno StackPanel orizzontale che a sua volta contiene una TextBox e tre FlickrButton. La TextBox serve per ricercare un utente attraverso il suo username. Il primo FlickrButton è quello che ci interessa di più, perchè una volta cliccato ricerca i set fotografici dell’utente e li visualizza nella ListBox che si trova nella seconda riga della Grid. Gli altri due FlickrButton raggiungono due Url differenti: per adesso possiamo bellamente ignorarli.
Quello che ci interessa in questo momento sono tre cose: la TextBox UsernameTextBox, il FlickrButton ListPhotoSets e la ListBox PhotoSetListBox. Dall’interazione di questi tre controlli nasce l’applicazione vera e propria.
Se diamo un’occhiata al post precedente, vediamo come il popolamento della ListBox avviene attraverso l’impostazione della sua proprietà ItemsSource. Il valore assunto da questa proprietà è un oggetto di tipo FlickrBrowserPhotosetCollection, che è una classe che semplicemente è una List<FlickrBrowserPhotoset>. La classe FlickrBrowserPhotoSet espone una proprietà Title (titolo del set fotografico), NumberOfPhotos (Numero di foto nel set), PrimaryPhotoUrl (url della foto che fa da copertina al set), PhotosetSquareThumbnailUrl e Photos. Queste ultime due per adesso non sono utilizzate.
Questo per dire che possiamo lavorare sulla proprietà ItemTemplate della ListBox, specificando l’aspetto che ogni riga della ListBox deve assumere. Con aspetto intendo per esempio un pannello, che contiene altri pannelli, altri controlli e sui quali possiamo definire stili, triggers e tutto il resto. Diamo ancora una volta un’occhiata allo XAML sopra, nel punto in cui viene dichiarata la ListBox: viene specificato che la sorgente di ItemsSource è la risorsa statica sets, e viene specificato che ogni elemento aggiunto alla ListBox sarà di tipo FlickrBrowserPhotoset. Questo è importante, perchè dentro il template faremo un po’ di binding indicando la proprietà che ogni controllo deve visualizzare. Ultima nota: la risorsa sets viene popolato dal code-behind in C#, l’abbiamo visto l’altra volta. Un piccolo rimando è necessario per ricordarcelo:
FlickrBrowserPhotosetCollection setFoto = (FlickrBrowserPhotosetCollection) this.FindResource("sets"); // Popolo setFoto // ... // Assegno ItemsSource PhotoSetListBox.ItemsSource = setFoto;
Con i metodo FindResource ottengo l’istanza di FlickrBrowserPhotosetCollection. Poi popolo l’oggetto setFoto con tutti i set fotografici che mi servono e poi assegno ItemsSource. Ne abbiamo parlato l’altra volta, perciò chiudo qui.
L’ultima cosa che voglio far vedere è lo XAML che definisce il template che il runtime di WPF utilizza per disegnare ogni Item aggiunto alla ListBox:
<Style TargetType="{x:Type ListBox}"> <Setter Property="ItemTemplate"> <Setter.Value> <DataTemplate> <StackPanel Margin="4" Orientation="Horizontal"> <Image Margin="4"> <Image.Source> <Binding Path="PrimaryPhotoUrl" /> </Image.Source> </Image> <StackPanel Margin="4" Orientation="Vertical"> <TextBlock Margin="4" FontSize="14pt" Text="{Binding Path=Title}"/> <StackPanel Margin="4" Orientation="Horizontal"> <TextBlock Margin="4" FontSize="10pt" Text="Number of Photos :"/> <TextBlock Margin="4" FontSize="10pt" Text="{Binding Path=NumberOfPhotos}"/> </StackPanel> <StackPanel Margin="4" Orientation="Horizontal"> <TextBlock Margin="4" FontSize="10pt" Text="Thumbnail Url :"/> <TextBlock Margin="4" FontSize="10pt" Text="{Binding Path=PhotosetSquareThumbnailUrl}"/> </StackPanel> <TextBox Text="Drag here to upload new images" Background="LightCoral" Name="txtUpload" IsReadOnly="True" PreviewDragOver="ehDragOver" PreviewDrop="ehDrop" PreviewDragLeave="ehDragLeave" /> </StackPanel> </StackPanel> </DataTemplate> </Setter.Value> </Setter> </Style>
Lo XAML è lungo, ma vi posso assicurare che fare la stessa cosa con normale C# sarebbe un incubo. Ogni item viene disegnato con uno StackPanel che contiene un Image ed un altro StackPanel sulla sua destra. La morale è: visualizzo un certo numero di TextBox che vengono bindate alle diverse proprietà pubbliche esposte dalla classe FlickrBrowserPhotoset di cui parlavamo prima. Il risultato è inquietante…eccolo…
Farà anche schifo, ma una volta un impatto simile nelle applicazioni non riuscivo a darlo nemmeno se sudava quattro camicie. In realtà sto già lavorando ad altre features interessanti, come la possibilità di uploadare nuove foto e di inserirle dentro un set (nuovo o già esistente) tramite drag’n’drop, di ricercare foto le foto di un utente dato un particolare tag e roba del genere.
A presto, metterò disponibile il download di codice ed applicazione. Stay tuned!