Fichiers de styles et erreur "Cannot find a Resource with the Name/Key xxxx" en Silverlight

Lorsque votre application grossit (son style aussi), il est intéressant de séparer vos styles dans différents fichiers pour gagner en clareté. Il vous suffit ainsi de déclarer vos fichiers dans le fichier App.xaml comme ceci :

<Application.Resources>
  <ResourceDictionary>
    <ResourceDictionary.MergedDictionaries>
      <ResourceDictionary Source="Color.xaml" />
      <ResourceDictionary Source="Control.xaml" />
    </ResourceDictionary.MergedDictionaries>
  </ResourceDictionary>
</Application.Resources>

Ceci ne fonctionne que si le fichier Control.xaml n’utilise pas de ressources déclarées dans Color.xaml et inversement. Contrairement à WPF qui tient compte de l’ordre de déclaration (Color.xaml est avant Control.xaml), Silverlight charge les ressources de manière indépendante interdisant ainsi les références entre les diffèrents dictionnaires.

Dans notre exemple, Color.xaml contient :

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Color x:Key="MyColor">#FFF98110</Color>
</ResourceDictionary>

Alors que Control.xaml contient :

<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <Style TargetType="TextBlock">
    <Setter Property="Foreground"
    Value="{StaticResource MyColor}" />
  </Style>
</ResourceDictionary>

Control.xamlfait donc référence à une ressource déclarée dans Color.xaml. Pour faire fonctionner ce code, il faut déclarer Color.xamldans Control.xaml comme ceci :

<ResourceDictionary.MergedDictionaries>
  <ResourceDictionary Source="Color.xaml" />
</ResourceDictionary.MergedDictionaries>

Imaginer ceci dans 10 autres fichiers de style faisant référence à MyColor déclarée dans Color.xaml. Le contenu de Color.xaml serait duppliqué et inclus 10 fois (1 fois par fichier). Inutile de dire que cette pratique est plutôt mauvaise d’un point de vue performance. K.M s’est basé sur cet article pour créer une classe fonctionnant en Silverlight :

public class SharedResourceDictionary : ResourceDictionary
{
    public static Dictionary<Uri, ResourceDictionary> _sharedDictionaries =
        new Dictionary<Uri, ResourceDictionary>();

    private Uri _sourceUri;
    public new Uri Source
    {
        get { return _sourceUri; }
        set
        {
            _sourceUri = value;
            if (!_sharedDictionaries.ContainsKey(value))
            {
                Application.LoadComponent(this, value);
                _sharedDictionaries.Add(value, this);
            }
            else
            {
                CopyInto(this, _sharedDictionaries[value]);
            }
        }
    }
    
    private static void CopyInto(ResourceDictionary copy, 
        ResourceDictionary original)
    {
        foreach (var dictionary in original.MergedDictionaries)
        {
            var mergedCopy = new ResourceDictionary();
            CopyInto(mergedCopy, dictionary);
            copy.MergedDictionaries.Add(mergedCopy);
        }
        foreach (DictionaryEntry pair in original)
        {
            copy.Add(pair.Key, pair.Value);
        }
    }
}

Explications :

Au lieu de recharger le code de Color.xaml dans chaque fichier, il sera chargé la 1re fois puis stocké dans un Dictionaire (Dictionary<> C#). Lorsqu’un autre fichier essayera d’inclure Color.xaml, son contenu sera directement récupéré depuis le Dictionary<>.

Le code précédemment ajouté à Control.xaml devient :

<ResourceDictionary.MergedDictionaries>
  <shared:SharedResourceDictionary 
    Source="/MergedDictionary;Component/Color.xaml" />
</ResourceDictionary.MergedDictionaries>

sans oublier la déclaration du namespace shared :

xmlns:shared="clr-namespace:MergedDictionary"

Attention, dans la propriété Source du SharedResourceDictionary, “Color.xaml” ne fonctionne plus, il faut impérativement mettre “/MergedDictionary;Component/Color.xaml".

Code et explications originals : ici.


Voir également