Con WPF finalmente il databinding diventa parte integrante dell'architetturà anzichè essere una realtà di contorno come lo è tutt'ora nel mondo WinForm (anche se con la versione 2.0 del FX ci sono stati parecchi improvements).
Tra le svariate novità mi piace molto quella di poter agganciare tra loro i vari controlli semplicemente usando l'attributo ElementName per indicare la fonte di Binding.
<CheckBox x:Name="chkEnable" Content="Advanced..." />
<GroupBox Header="Options" IsEnabled="{Binding ElementName=chkEnable, Path=IsChecked}">
<Button Height="75" Width="100">Apply</Button>
</GroupBox>
In questo caso l'abilitazione del GroupBox (IsEnabled) dipende dal valore della proprietà IsChecked del controllo chkEnable, il tutto si ottiene mettendo in Binding la proprietà IsChecked del controllo Target, indicando attraverso ElementName la sorgente e usando Path per indicare quale proprietà della sorgente fornisce le informazioni.
L'altro aspetto sicuramente innovativo è il concetto di DataTemplate, ovvero la possibilità di indicare come deve essere rappresentato graficamente un determinato oggetto fonte di Binding.
User è una classe che rappresenta un utente con alcune proprietà (Name,Surname,Role e BirthDate) Users è una ObservableCollection di oggetti User così definita:
class Users:ObservableCollection<User>
{
public Users () {...Add some dummy Users...}
}
ObservableCollection è una collezione che ci informa quando la lista viene modificata e/o uno degli items contenuti viene modificato a patto che implementi l'interfaccia INotifyPropertyChanged.
Volendo visualizzare in una listbox il contenuto della collection quello che possiamo fare è:
xmlns:c="clr-namespace:Code21_Binding"
...
<Window.Resources>
<c:Users x:Key="users" />
<Window.Resources>
...
<ListBox Name="lb1" ItemsSource="{StaticResource users}" Height="300" />
Ovvero, importare il namespace, creare nelle risorse un istanza della collection e associarla alla proprietà ItemSource della listbox ottenendo il risultato qui a fianco.
A questo punto quello che possiamo fare è creare un DataTemplate e indicare come l'oggetto User deve essere rappresentato graficamente.
<DataTemplate DataType="{x:Type c:User}">
<Border Name="itemBorder" BorderBrush="LightGreen" Padding="5" Margin="0,3" BorderThickness="3" Width="350">
<Grid>
<Grid.RowDefinitions>
<RowDefinition/>
<RowDefinition/>
<RowDefinition/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto"/>
<ColumnDefinition/>
</Grid.ColumnDefinitions>
<Image Name="img" Source="{x:Null}" Margin="0,0,3,0" Width="16" Height="16" Grid.Column="0" Grid.Row="0" Grid.RowSpan="3" HorizontalAlignment="Left" />
<TextBlock FontSize="24" FontWeight="Bold" Text="{Binding Path=Surname}" Grid.Column="1" Grid.Row="0" />
<TextBlock FontSize="14" FontWeight="Bold" Text="{Binding Path=Name}" Grid.Column="1" Grid.Row="1" />
<TextBlock Name="tbDate" FontStyle="Italic" Text="{Binding Path=BirthDate}" Grid.Column="1" Grid.Row="2"/>
</Grid>
</Border>
<DataTemplate.Triggers>
<DataTrigger Binding="{Binding Path=Role}">
<DataTrigger.Value>
<c:Role>Architect</c:Role>
</DataTrigger.Value>
<DataTrigger.Setters>
<Setter Property="BorderBrush" Value="Blue" TargetName="itemBorder" />
<Setter Property="Source" Value="images\architect.gif" TargetName="img" />
</DataTrigger.Setters>
</DataTrigger>
</DataTemplate.Triggers>
</DataTemplate>
In questo esempio ho definito un DataTemplate associato al tipo User (DataType="{x:Type c:User}") indicando che questo deve essere rappresentato attraverso dei TextBlocks ognuno dei quali ha la proprietà Text associata alla rispettiva proprietà dell'istanza associata ed inoltre attraverso dei DataTrigger ho indicato che gli User con Role=Architect devono avere il Bordo Blue e una determinata immagine a fianco.
Senza fare nient'altro la listbox ora visualizza le varie istanze come indicato nel nostro DataTemplate, il quale, volendo, può essere cambiato dinamicamente a run-time.
Il tutto senza avere scritto codice (ma solo qualche centinaia di righe di XAML... :-) )