2013-02-22 10 views
5

Ho il seguente scenario: Dispongo di un enum e desidero associarlo a un oggetto DataGridView (databound) su un oggetto DataGridViewTextBoxColumn.Associazione di un TypeConverter direttamente a un'enumerazione

Ecco il mio enum:

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

E qui è una semplice proprietà che lo utilizza:

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

Nella situazione che ho sopra, il TypeConverter funziona bene. Fa la conversione per me.

Tuttavia, questo è più complesso della mia soluzione ideale. Se metto il typeconverter sull'Enum stesso (rimuovi il codice sopra) e commenta il typeconverter sulla proprietà, il typeconverter non viene più chiamato!

Ho fatto questa convenzione su altre classi e funziona perfettamente.

Perché non mettere un tipoconvertitore direttamente su una enumerazione non funziona?

Per riferimento, ecco la mia 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

Forse dovresti provare questo metodo: http://en.wikipedia.org/wiki/Rubber_duck_debugging – espais

+0

possibile duplicato di [Come sovrascrivo ToString in en # C?] (Http://stackoverflow.com/questions/796607/how-do-i-override-tostring-in-c-sharp-enums) –

+0

Fondamentalmente non correlato. La soluzione alla risposta in quel thread è già stata implementata in precedenza. Questa domanda sta andando molto oltre. – greggorob64

risposta

2

Non sono sicuro di cosa si intende per "questo è più complessa di quanto la mia soluzione ideale". Ho un modo per farlo diverso dal tuo, ma potrebbe non essere meno complesso. Direi che il mio modo coinvolge più overhead in primo piano, ma paga sempre di più lo si utilizza nella vostra applicazione. Prepara la tua applicazione per essere localizzabile e significa che non devi aggiungere attributi a ciascun valore enum.

1) Fare un cache Resource Manager

Questa parte è facoltativa; tuttavia, se si utilizzano più file di risorse, molte volte, come faccio io, questo potrebbe aumentare le prestazioni riducendo la quantità di riflessione eseguita.

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) Creare una localizzata Descrizione Attributo

Questo è l'attributo che si applica al tipo Enum. La cosa interessante di questo è che non devi aggiungere un attributo a ogni enumerazione, solo una volta sopra la dichiarazione del tipo 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) Creare localizzati Descrizione Converter

Questo converte il valore enum nella stringa si darà nel risorsa stringhe (RESX) file.

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) Creazione di una risorsa (.resx) sul file String

Ora si desidera creare un file di risorse che conterrà le descrizioni desiderate per il vostro stile di valore della chiave Enums. Quello che intendo con questo è che nella colonna "Nome" delle risorse stringa si inserisce il nome esatto delle singole enumerazioni e nella colonna "Valore" si inserisce la stringa che si desidera ottenere quando si converte l'enumerazione.
Ad esempio, supponiamo di avere i seguenti Enum.

public enum MyColors 
{ 
    Black, 
    Blue, 
    White 
} 

Allora il vostro file di risorse stringa sarebbe simile a questa ...

Nome | Valore

Nero | A Dark Color
Blu | Un bel colore
Bianco | Un colore luminoso

5) Creare enumerazioni con attributi

Ora abbiamo finalmente fare la dichiarazione Enum con l'LocalizedDescription. Il parametro che si passa all'attributo LocalizedDescription è il tipo del file di risorse stringa. Ora, quando viene utilizzato il convertitore, vedrà l'attributo del tipo di enum, otterrà il file di risorse, cercherà la stringa di chiavi che corrisponde al valore di stringa del particolare valore enum e restituirà il valore dal file di risorse come stringa convertita.

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

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

Il principale svantaggio di questo approccio è che esso funziona solo se i tasti "Nome" nel file di risorse corrispondono ai nomi dei vostri valori enum. Questo è l'unico modo per fare riferimento a valori stringa in un file di risorse senza assegnare a ciascun enum un attributo di descrizione. Quindi, come lo usi per visualizzare i valori? Ecco un esempio ...

Nel codice xaml, si desidera creare un fornitore di dati per ottenere i valori dell'enumerazione sul proprio elemento dell'interfaccia utente (sto utilizzando un ComboBox qui ...). Quindi vorrai rendere disponibile il convertitore e template il tuo elemento UI per usare il convertitore enum. Quindi ecco qui ...

 <!-- 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, scusa, è così lungo. Non sono sicuro che sia troppo complesso per te, ma è un'altra opzione. Spero che sia d'aiuto!

+1

Il mio esempio è molto simile al tuo (fai un passo avanti nell'utilizzo di un file di risorse, che uso anche in altri posti per ottenere supporto multilingue). La differenza di complessità che ho menzionato è che il mio esempio richiede un typeconverter * sulla proprietà * invece che sull'enum stesso. E sfortunatamente, non penso che i nostri esempi si combinino. Stai usando xaml e im usando pure. Winforms .net. Stai prendendo la tua casella combinata. Il tuo xaml sta legando specificamente la combobox al convertitore di cui hai bisogno, che è quello che sto cercando di evitare (non credo che avrò successo). Grazie per la risposta! – greggorob64

+0

Oh capisco cosa intendi. Mi dispiace, non ho potuto aiutare. – akagixxer