2013-02-22 2 views
5

J'ai le scénario suivant: J'ai une énumération, et je veux lier l'afficher dans un DataGridView (databound) sur un DataGridViewTextBoxColumn.Liaison directe d'un TypeConverter à une énumération

Voici mon ENUM:

//[TypeConverter(typeof(EnumStringConverter))] 
    public enum YesNoNA 
    { 
     [EnumDescription("Yes")] 
     Yes, 
     [EnumDescription("No")] 
     No, 
     [EnumDescription("N/A")] 
     NA 
    } 

Et voici une propriété simple qui l'utilise:

[TypeConverter(typeof(EnumStringConverter))] 
    public YesNoNA HighLimitWithinBounds { get; protected set; } 

Dans la situation que je ci-dessus, le TypeConverter fonctionne très bien. Il fait la conversion pour moi.

Cependant, ceci est plus complexe que ma solution idéale. Si je mets le typeconverter sur l'Enum lui-même (décommentez le code ci-dessus), et commentez le typeconverter sur la propriété, le typeconverter n'est plus appelé!

J'ai fait cette convention sur d'autres classes, et ça marche très bien. Pourquoi ne pas mettre un typeconverter directement sur une énumération ne fonctionne pas?

Pour référence, voici mon TypeConverter:

public class EnumStringConverter : TypeConverter 
    { 
     public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, Object value, Type destinationType) 
     { 
     if (value != null && destinationType == typeof(string)) 
     { 
      return "Edited to protect the innocent!"; 
     } 
     return TypeDescriptor.GetConverter(typeof(Enum)).ConvertTo(context, culture, value, destinationType); 
     } 
     public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) 
     { 
     if (destinationType == typeof(string)) 
     { 
      return true; 
     } 
     return base.CanConvertTo(context, destinationType); 
     } 
    }; 
+1

Vous devriez peut-être essayer cette méthode: http://en.wikipedia.org/wiki/Rubber_duck_debugging – espais

+0

duplication possible de [Comment remplacer ToString dans C# enums?] (Http://stackoverflow.com/questions/796607/how-do-i-override-tostring-in-c-sharp-enums) –

+0

Defairement non lié. La solution à la réponse dans ce thread est déjà implémentée ci-dessus. Cette question va beaucoup plus loin. – greggorob64

Répondre

2

Je ne sais pas ce que vous entendez par « ce qui est plus complexe que ma solution idéale ». J'ai une façon de faire qui est différente de la vôtre mais elle ne peut pas être moins complexe. Je dirais que mon chemin implique plus de frais généraux à l'avant mais paye de plus en plus vous l'utilisez dans votre application. Il prépare votre application à être localisable et signifie que vous n'avez pas besoin d'ajouter d'attributs à chaque valeur d'énumération.

1) Faire un gestionnaire de ressources Cache

Cette partie est facultative; Cependant, si vous utilisez plusieurs fichiers de ressources plusieurs fois comme je le fais, cela pourrait augmenter les performances en réduisant la quantité de réflexion effectuée.

using System; 
using System.Collections.Generic; 
using System.Reflection; 
using System.Resources; 

namespace AppResourceLib.Public.Reflection 
{ 
    internal static class ResourceManagerCache 
    { 
    private static Dictionary<Type, ResourceManager> _resourceManagerMap = 
     new Dictionary<Type, ResourceManager>(); 

    public static ResourceManager GetResourceManager(Type resourceType) 
    { 
     ResourceManager resourceManager = null; 

     // Make sure the type is valid. 
     if (null != resourceType) 
     { 
     // Try getting the cached resource manager. 
     if (!ResourceManagerCache._resourceManagerMap.TryGetValue(resourceType, out resourceManager)) 
     { 
      // If it is not in the cache create it. 
      resourceManager = resourceType.InvokeMember(
      "ResourceManager", 
      (BindingFlags.GetProperty | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic), 
      null,             
      null,             
      null) as ResourceManager; 

      // If it was created, add the resource manager to the cache. 
      if (null != resourceManager) 
      { 
      ResourceManagerCache._resourceManagerMap.Add(resourceType, resourceManager); 
      } 
     } 
     } 

     return resourceManager; 
    } 
    } 
} 

2) Création d'une description Localisée Attribute

Ceci est l'attribut que vous appliquerez au type Enum. Ce qui est génial, c'est que vous n'avez pas besoin d'ajouter un attribut à chaque énumération, juste une fois au-dessus de la déclaration de type enum.

using System; 
using System.ComponentModel; 

namespace AppResourceLib.Public.Reflection 
{ 
    /// <summary> 
    /// A resource type attribute that can be applied to enumerations. 
    /// </summary> 
    [AttributeUsage(AttributeTargets.Enum)] 
    public sealed class LocalizedDescriptionAttribute : Attribute 
    { 
    /// <summary> 
    /// The type of resource associated with the enum type. 
    /// </summary> 
    private Type _resourceType; 

    public LocalizedDescriptionAttribute(Type resourceType) 
    { 
     this._resourceType = resourceType; 
    } 

    /// <summary> 
    /// The type of resource associated with the enum type. 
    /// </summary> 
    public Type ResourceType 
    { 
     get 
     { 
     return this._resourceType; 
     } 
    } 
    } 
} 

3) Créer une description Localized Converter

Ceci convertit le fichier valeur ENUM dans la chaîne que vous allez donner dans la ressource de chaînes (.resx).

using System; 
using System.Globalization; 
using System.Linq; 
using System.Reflection; 
using System.Resources; 
using System.Windows.Data; 

namespace AppResourceLib.Public.Reflection 
{ 
    [ValueConversion(typeof(Object), typeof(String))] 
    public class LocalizedDescriptionConverter : IValueConverter 
    { 
    public Object Convert(Object value, Type targetType, Object param, CultureInfo cultureInfo) 
    { 
     String description = null; 

     if (null != value) 
     { 
     // If everything fails then at least return the value.ToString(). 
     description = value.ToString(); 

     // Get the LocalizedDescriptionAttribute of the object. 
     LocalizedDescriptionAttribute attribute = 
      value.GetType().GetCustomAttribute(typeof(LocalizedDescriptionAttribute)) 
      as LocalizedDescriptionAttribute; 

     // Make sure we found a LocalizedDescriptionAttribute. 
     if (null != attribute) 
     {   
      ResourceManager resourceManager = 
      ResourceManagerCache.GetResourceManager(attribute.ResourceType); 

      if (null != resourceManager) 
      { 
      // Use the ResourceManager to get the description you gave the object value. 
      // Here we just use the object value.ToString() (the name of the object) to get 
      // the string in the .resx file. The only constraint here is that you have to 
      // name your object description strings in the .resx file the same as your objects. 
      // The benefit is that you only have to declare the LocalizedDescriptionAttribute 
      // above the object type, not an attribute over every object. 
      // And this way is localizable. 
      description = resourceManager.GetString(value.ToString(), cultureInfo); 

      String formatString = (param as String); 

      // If a format string was passed in as a parameter, 
      // make a string out of that. 
      if (!String.IsNullOrEmpty(formatString)) 
      { 
       formatString = formatString.Replace("\\t", "\t"); 
       formatString = formatString.Replace("\\n", "\n"); 
       formatString = formatString.Replace("\\r", "\r"); 

       description = String.Format(formatString, value.ToString(), description);    
      }   
      } 
     } 
     } 

     return description;  
    } 

    public Object ConvertBack(Object value, Type targetType, Object param, CultureInfo cultureInfo) 
    { 
     throw new NotImplementedException(); 

     return null; 
     } 
    } 
} 

4) Créer une ressource (.resx) Fichier Chaîne

Maintenant, vous voulez créer un fichier de ressources qui contiendra les descriptions que vous voulez pour votre style de valeur clé énumérations. Ce que je veux dire par là, c'est que dans la colonne "Nom" des ressources de chaîne, vous mettrez le nom exact des énumérations individuelles et dans la colonne "Valeur", vous mettrez la chaîne que vous voulez obtenir lors de la conversion de cette énumération.Par exemple, disons que vous avez les Enums suivants.

public enum MyColors 
{ 
    Black, 
    Blue, 
    White 
} 

Ensuite, votre fichier de ressources de chaîne ressemblerait à ceci ...

Nom | Valeur

Noir | Une couleur foncée
Bleu | Une couleur fraîche
Blanc | Une couleur vive

5) Créer énumérations avec l'attribut

Maintenant, nous avons finalement faire la déclaration Enum avec le LocalizedDescription. Le paramètre que vous transmettez dans l'attribut LocalizedDescription est le type de votre fichier de ressources de chaîne. Maintenant, lorsque le convertisseur est utilisé, il verra l'attribut du type enum, obtiendra le fichier de ressources, recherchera la chaîne de clé qui correspond à la valeur de chaîne de la valeur enum particulière et retournera la valeur du fichier de ressource en tant que chaîne convertie.

using AppResourceLib.Public; 
using AppResourceLib.Public.Reflection; 

namespace MyEnums 
{ 
    [LocalizedDescription(typeof(MyColorStrings))] 
    public enum MyColors 
    { 
    Black, 
    Blue, 
    White 
    } 
} 

L'inconvénient majeur de cette approche est qu'il ne fonctionnera que si les clés « Nom » dans votre fichier de ressources correspondent aux noms de vos valeurs ENUM. C'est le seul moyen de référencer des valeurs de chaîne dans un fichier de ressources sans attribuer à chaque énumération un attribut de description. Alors, comment l'utilisez-vous pour afficher les valeurs? Voici un exemple ...

Dans votre code xaml, vous voudrez faire un fournisseur de données pour obtenir les valeurs des énumérations de votre élément d'interface utilisateur (j'utilise un ComboBox ici ...). Ensuite, vous voudrez rendre le convertisseur disponible et modéliser votre élément d'interface utilisateur pour utiliser le convertisseur enum. Alors voilà ...

 <!-- Enum Colors --> 
    <ObjectDataProvider x:Key="MyColorEnums" 
         MethodName="GetValues" 
         ObjectType="{x:Type sys:Enum}"> 
    <ObjectDataProvider.MethodParameters> 
     <x:Type TypeName="MyColors"/> 
    </ObjectDataProvider.MethodParameters> 
    </ObjectDataProvider> 


    <!-- Enum Type Converter --> 
    <LocalizedDescriptionConverter x:Key="EnumConverter"/> 


    <!-- Dropdown Expand ComboBox Template --> 
    <DataTemplate x:Key="MyColorsComboBoxTemplate"> 
    <Label Content="{Binding Path=., Mode=OneWay, 
     Converter={StaticResource EnumConverter}}" 
      Height="Auto" Margin="0" VerticalAlignment="Center"/> 
    </DataTemplate> 

    <!-- And finally the ComboBox that will display all of your enum values 
    but will use the strings from the resource file instead of enum.ToString() --> 
    <ComboBox Width="80" HorizontalAlignment="Left" 
    ItemTemplate="{StaticResource MyColorsComboBoxTemplate}" 
    ItemsSource="{Binding Source={StaticResource MyColorEnums}}"> 

Wow, désolé, c'est trop long. Je ne sais pas si c'est trop complexe pour vous mais c'est une autre option. J'espère que cela aide!

+1

Mon exemple est très similaire au vôtre (vous allez plus loin en utilisant un fichier de ressources, que j'utilise ailleurs pour obtenir un support multilingue). La différence de complexité que je mentionne est que mon exemple nécessite un convertisseur de type * Sur la propriété * au lieu de sur l'énumération elle-même. Et malheureusement, je ne pense pas que nos exemples correspondent. Vous utilisez xaml et im en utilisant des winforms .net purs. Vous prenez votre combobox. Votre xaml lie spécifiquement la combobox au convertisseur dont vous avez besoin, ce que j'essaie d'éviter (je ne pense pas que j'aurai du succès). Merci pour la réponse! – greggorob64

+0

Oh, je vois ce que vous voulez dire. Désolé, je n'ai pas pu aider. – akagixxer

Questions connexes