Lavorando su progetti Silverlight mi sto rendendo conto sempre di più come il pattern Model-View-Viewmodel sia in grado di risolvere situazioni che a prima vista possono sembrare irrisolvibili o perlomeno non così immediate come lo sono in WPF.
l’ultimo caso in cui mi sono imbattuto è a prima vista banale: Customizzare una listbox affinchè visualizzi delle informazioni evidenziando l’elemento selezionato con un colore diverso, in pratica quello che vedete qui sotto:
image

In WPF grazie ai Triggers la cosa si risolve in poco tempo, ma in Silverlight come si fa?
Partiamo col definire il DataTemplate da associare all’ItemTemplate della listbox, il tutto ovviamente usando Blend 3:

image

Il mio DataTemplate è composto da un Border con all’interno uno StackPanel il quale ospita tre TextBlocks, grazie al fatto che ho esposto il ViewModel come proprietà di una classe istanziata come risorsa ho il grosso vantaggio di avere dei dati a design time e vi assicuro che quando disegnate dei templates questo aiuta parecchio.

image

Ovviamente è importante ricordarsi di evitare di cablare qualsiasi dimensione all’interno degli elementi presenti nel DataTemplate se vogliamo che il datatemplate si ridimensioni correttamente quando la listbox cambia le proprie dimensioni.
Conclude l’opera di definizione del DataTemplate, un aggiustamento allo stile dell’ ItemTemplate per far si che il nostro DataTemplate occupi tutto lo spazio messogli a disposizione.

image
Con lo stile selezionato impostiamo la proprietà HorizontalContentAlignment a Stretch.

image
A questo punto rimane il problema “vero”: Come evidenziare in maniera diversa l’elemento selezionato.
Editiamo il nostro DataTemplate aggiungendo due stati: Normal e Selected cambiando il Background del border nel secondo stato.

Stato Normal Stato Selected
image image

A questo punto la domanda è: Chi informa il VisualStateManager di passare da Normal a Selected, ovvero: “Chi invoca il fatidico GotoState(“x”)” 
Visto che il codice vorrei evitarlo (in fondo siamo in un ambito design) la risposta sta in quella fantastica risorsa che sono gli Expression Blend Samples (come? non li avete ancora installati?) e nello specifico nel behavior chiamato: DataStateBehavior il quale cambia due stati in base al valore di una proprietà accessibile via Databinding
Primo step: Selezionare il behavior in Blend3 e trascinarlo sul border presente nel DataTemplate

image

A questo punto impostiamo le relative proprietà sempre all’interno di Blend, ovviamente mi sono preoccupato in precedenza di far esporre una proprietà IsSelected al mio ViewModel.

image

Rimane ora da Bindare la proprietà SelectedItem al ViewModel che rappresenta il DataContext associato alla listbox, operazione che nel mio caso ho dovuto fare editando lo XAML in quanto la dialog di selezione di Blend non mostra  il mio ViewModel principale bensì il quello associato alla proprietà ItemsSource (è un bug di Blend…), ecco lo XAML:

   1: <ListBox HorizontalAlignment="Left"
   2:                Margin="40,80,0,104"
   3:                Width="192"
   4:                SelectedItem="{Binding SelectedDocument, Mode=TwoWay}"
   5:                ItemsSource="{Binding Items, Mode=OneWay}"
   6:                ItemTemplate="{StaticResource MyItemTemplate}"
   7:                Background="{x:Null}"
   8:                ItemContainerStyle="{StaticResource ListBoxItemStyle1}" />

Per finire, un poco di sano codice per valorizzare la proprietà IsSelected del ViewModel rappresentato visivamente dal DataTemplate creato in precedenza.

   1: public DocumentViewModel SelectedDocument
   2: {
   3:    get
   4:    {
   5:       return this.selectedDocument;
   6:    }
   7:  
   8:    set
   9:    {
  10:       if (value != this.selectedDocument)
  11:       {
  12:          if (this.selectedDocument != null) this.selectedDocument.IsSelected = false;
  13:          this.selectedDocument = value;
  14:          this.selectedDocument.IsSelected = true;
  15:          this.RaisePropertyChanged("SelectedDocument");
  16:       }
  17:    }
  18: }

Qualche piccola modifica negli stati del ItemContainerStyle per eliminare quel fastidioso alone azzurrino tipico di Silverlight:

image

E abbiamo portato a casa il risultato.
Francamente senza appoggiarmi al DataStateBehavior e usare M-V-VM non ho idea di quali soluzioni alternative avrei potuto usare.

M-V-VM Rocks!