WPF : Réaliser un inflecteur de clefs de dictionnaires de ressources

.NET WPF XAML

.NET WPF XAMLD’après le dictionnaire (pas celui de ressources smiley clin d'oeil , mais plutôt celui de la langue Anglaise: the freed dictionary, « un inflecteur est la façon dont un mot est changé ou modifié dans la forme afin d’atteindre un nouveau sens spécifique« .

Dans le cas d’un dictionnaire de ressources WPF, un mécanisme d’inflection des clefs permet de subsituer une ressource par une autre. Si la règle d’inflection est constante, la ressource pourra être partagée (mise en cache comme dans l’usage standard du dictionnaire.

La mise en oeuvre consiste à ajouter une classe de comportement à un dictionnaire de ressources. La solution repose sur la méthode OnGettingValue de la classe ResourceDictionary. Cette méthode est appelée lorsque le dictionnaire reçoit une demande de resource :

public partial class BaseMap : ResourceDictionary
{
    /// <summary>
    /// l'inflecteur de clefs de dictionnaire de resources
    /// </summary>
    IResourceKeyMapper RscKeyMapper;

    /// <summary>
    /// fabrique une nouvelle instance
    /// </summary>
    public BaseMap()
        : base()
    {
        RscKeyMapper = new SampleKeyMapper();
        InitializeComponent();
    }

    /// <summary>
    /// le dictionnaire reçoit une demande de resource
    /// </summary>
    /// <param name="key">La clé de la ressource à obtenir</param>
    /// <param name="value">La valeur de la ressource demandée</param>
   /// <param name="canCache">true si la ressource peut être enregistrée et utilisée ultérieurement;sinon, faux</param>
    protected override void OnGettingValue(object key, ref object value, out bool canCache)
    {            
        RscKeyMapper.OnGettingValue(key,ref value,out canCache);
    }
}

La méthode d’inflection est définie dans une classe qui répond à l’interface IResourceKeyMapper qui est découplée du dictionnaire, permettant sa réutilisation pour d’autres dictionnaires. Ce dernier est défini comme suit :

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
                    xmlns:sys="clr-namespace:System;assembly=mscorlib"
                    x:Class="BaseMap"
>

    <!-- menu bar -->

    <FontFamily x:Key="Menu_FontFamily"/>
    <sys:Double x:Key="Menu_FontSize"/>
    <FontStyle x:Key="Menu_FontStyle"/>
    <FontWeight x:Key="Menu_FontWeight"/>

    <!-- Context Menu -->

    <FontFamily x:Key="ContextMenuItem_FontFamily"/>
    <FontWeight x:Key="ContextMenuItem_FontWeight"/>
    <FontStyle x:Key="ContextMenuItem_FontStyle"/>
    <sys:Double x:Key="ContextMenuItem_FontSize"/>

</ResourceDictionary>

Dans cet exemple de dictionnaire, on définit des clefs sans valeurs pour styler les fontes de contrôles Menu et ContextMenu. L’idée de l’inflecteur dans une telle situation est de permettre de fournir des valeurs identiques de fontes pour les différents contrôles (harmonisation) ou des valeurs différentes, choix qui peut être fait dynamiquement par l’application. Personnellement j’utilise ce système également pour résoudre les cartes de couleurs pour les thèmes.
L’inflecteur est alors décrit dans la classe suivante, qui doit répondre à l’interface IResourceKeyMapper :

/// <summary>
/// map les clefs demandées selon une fonction de transformation
/// implémente la méthode OnGettingValue de ResourceDictionary
/// </summary>
public class SampleKeyMapper : IResourceKeyMapper
{
    /// <summary>
    /// fabrique une nouvelle instance
    /// </summary>
    public SampleKeyMapper()
    {

    }

    /// <summary>
    /// le dictionnaire reçoit une demande de resource
    /// </summary>
    /// <param name="key">La clé de la ressource à obtenir</param>
    /// <param name="value">La valeur de la ressource demandée</param>
    /// <param name="canCache">true si la ressource peut être enregistrée et utilisée ultérieurement;sinon, faux</param>
    public void OnGettingValue(object key, ref object value, out bool canCache)
    {
        canCache = false;
        try
        {
            var s = GetKeyInflection((string)key);
            var r = Application.Current.TryFindResource(s);
            if (r != null)
            {
                value = r;
                canCache = true;
            }
        }
        catch (Exception ex)
        {
            System.Diagnostics.Debug.WriteLine(ex);
        }
    }

    /// <summary>
    /// inflection de key vers un autre clef selon une heuristique
    /// </summary>
    /// <param name="key">clef de la resource demandée</param>
    /// <returns>clef inflectée</returns>
    protected virtual string GetKeyInflection(string key)
    {
        var newKey = ... ;  /* ICI ON DEFINIT LA FONCTION D'INFLECTION */
        System.Diagnostics.Debug.WriteLine($"{key} -> {newKey}");
        return newKey;
    }
}

L’interface est définie comme suit :

public interface IResourceKeyMapper
{
    /// <summary>
    /// le dictionnaire reçoit une demande de resource
    /// </summary>
    /// <param name="key">La clé de la ressource à obtenir</param>
    /// <param name="value">La valeur de la ressource demandée</param>
  /// <param name="canCache">true si la ressource peut être enregistrée et utilisée ultérieurement;sinon, faux</param>
    void OnGettingValue(object key, ref object value, out bool canCache);
}

Laisser un commentaire