Différences de namescopes et DataContext entre WPF et Silverlight

Lors de l’implémentation d’un UserControl, vous aurez très souvent à databinder un objet déclaré en XAML à une ou plusieurs propriétés du UserControl lui-même. La façon la plus élégante de procéder consiste à utiliser un binding ElementName.

<UserControl x:Class="NameScopeExample.MyUserControl"… x:Name="ucName">
<TextBlock Foreground="White" Text="{Binding MyProperty.Description,ElementName=ucName}"/>

Ce scénario fonctionne sans accroc avec WPF mais n’est à ce jour pas supporté par Silverlight, comme la solution attachée à ce post le démontre.

Cette différence de comportement est dûe au fait que Silverlight ne supporte qu’un seul namescope, alors que WPF permet l’utilisation de namescopes imbriqués. Cela signifie qu’un contrôle utilisateur Silverlight ne peut disposer que d’une seule valeur x:Name par instance. Dans l’exemple, deux noms différents sont donnés à une même instance: une fois dans le fichier XAML du contrôle lui-même, et une autre par le biais de MainPage.xaml.

MainPage.xaml
<local:MyUserControl … x:Name="ucNameInMainPage" MyProperty="{Binding MainPageProperty}"/>

MyUserControl.xaml
<UserControl x:Class="NameScopeExample.MyUserControl" … x:Name="ucName">

Dans Silverlight, MyUserControl ne gardera que le nom donné par le contrôle le moins imbriqué, dans notre cas celui de MainPage (ucNameInMainPage), causant ainsi une erreur de résolution de nom d’élément dans le premier binding du UserControl. La solution de contournement est assez simple: utiliser le DataContext implicite et définir via le code le DataContext de l’élément racine (souvent une Grid nommée LayoutRoot). Ceci est possible après un appel à InitializeComponent, et en modifiant le binding en supprimant la propriété ElementName.

void MyUserControl_Loaded(object sender, RoutedEventArgs e)

{

    // Implicit DataContexts work on both frameworks 

    // Les DataContexts implicites fonctionnent sur les deux frameworks

    this.LayoutRoot.DataContext = this;

}

 

<UserControl x:Class="NameScopeExample.MyUserControl"

   xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"

   xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"

   Loaded="MyUserControl_Loaded"

   Width="450" Height="300" x:Name="ucName">

    … <TextBlock Foreground="White" Text="{Binding MyProperty.Description}"/>

La solution exemple démontre également deux astuces qu’il m’arrive parfois d’utiliser :

  • Utiliser un lien afin qu’un fichier source puisse être partagé entre une application Silverlight et une application .NET (WPF dans notre cas). Dans l’exemple, MyUserControl et MyClass sont compilés indépendamment pour les deux plateformes.
  • Vous pouvez insérer une accolade ouvrante dans une valeur d’attribut XAML en le préfixant de la séquence d’échappement {}
    <TextBlock Foreground="LightGray" Text="{}{Binding MyProperty,ElementName=ucName}"/>

NameScopeExample.zip