2013-02-22 10 views
5

Ich habe folgendes Szenario: Ich habe eine Enum, und wollen, um sie in einem Datagridview zeigen binden (Databound) auf einem DataGridViewTextBoxColumn.Binden eines Typeconverter direkt an eine Enumeration

Hier ist meine Enum:

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

Und hier ist eine einfache Eigenschaft, die es verwendet:

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

In der Situation, die ich oben haben, die Typeconverter funktioniert gut. Es macht die Konvertierung für mich.

Dies ist jedoch komplexer als meine ideale Lösung. Wenn ich den Typkonverter auf die Enumeration selbst setze (den Code oben auskommentieren) und den Typkonverter auf der Eigenschaft auskommentiere, wird der Typkonverter nicht mehr aufgerufen!

Ich habe diese Konvention auf andere Klassen getan, und es funktioniert gut.

Warum funktioniert ein Typkonverter direkt auf einem Enum nicht funktioniert?

Als Referenz hier ist mein 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

Vielleicht sollten Sie diese Methode ausprobieren: http://en.wikipedia.org/wiki/Rubber_duck_debugging – espais

+0

möglich Duplikat von [Wie überschreibe ich ToString in C# enums?] (Http://stackoverflow.com/questions/796607/how-do-i-override-Zahnring-in-c-sharp-enums) –

+0

Definethely nicht verwandt. Die Lösung für die Antwort in diesem Thread ist bereits oben implementiert. Diese Frage geht viel weiter. – greggorob64

Antwort

2

Ich bin nicht sicher, was Sie unter „Das ist komplexer als meine ideale Lösung“. Ich habe einen Weg, dies anders zu machen als deins, aber es ist vielleicht nicht weniger komplex. Ich würde sagen, dass mein Weg mehr Aufwand im Voraus bedeutet, aber zahlt sich aus, je mehr Sie es in Ihrer Anwendung verwenden. Es bereitet Ihre Anwendung so vor, dass sie lokalisierbar ist, und bedeutet, dass Sie nicht jedem Enum-Wert Attribute hinzufügen müssen. Machen

1) einen Resource Manager Cache

Dieser Teil ist optional; Wenn Sie jedoch mehrere Ressourcendateien mehrmals verwenden, kann dies die Leistung erhöhen, indem Sie die Anzahl der Reflektionen verringern.

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) Erstellen Sie ein Lokalisierte Beschreibung Attribut

Dies ist das Attribut, das Sie auf den Enum-Typen gelten. Das coole daran ist, dass Sie nicht jedem Enum ein Attribut hinzufügen müssen, nur einmal über der Enum-Typ-Deklaration.

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) erstellen Lokalisierte Beschreibung Converter

Dieser wandelt den Enum-Wert in den String werden Sie es in der Strings Ressource geben (RESX-Datei).

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) Erstellen Sie eine Ressource (RESX) String Datei

Jetzt können Sie eine Ressource-Datei erstellen möchten, die die Beschreibungen Sie für Ihre Aufzählungen Schlüsselwert Stil enthält. Was ich damit meine, ist, dass Sie in der "Name" -Spalte der String-Ressourcen den genauen Namen der einzelnen Enums eingeben und in der "Value" -Spalte die Zeichenfolge angeben, die Sie beim Konvertieren dieser Enumeration erhalten möchten.
Zum Beispiel sagen, Sie hatten die folgenden Enums.

public enum MyColors 
{ 
    Black, 
    Blue, 
    White 
} 

Dann würde die Zeichenfolge-Ressource-Datei wie folgt aussehen ...

Name | Wert

Schwarz | Eine dunkle Farbe
Blau | Eine coole Farbe
Weiß | Eine helle Farbe

5) Erstellen Sie Aufzählungen mit Attribute

Jetzt sind wir mit dem LocalizedDescription schließlich die Enum-Deklaration machen. Der Parameter, den Sie an das LocalizedDescription-Attribut übergeben, ist der Typ der Zeichenfolge-Ressourcendatei. Jetzt, wenn der Konverter verwendet wird, sieht er das Attribut des Aufzählungstyps, ruft die Ressourcendatei ab, sucht nach der Schlüsselzeichenfolge, die mit dem Zeichenfolgenwert des bestimmten Aufzählungswerts übereinstimmt, und gibt den Wert aus der Ressourcendatei als konvertierte Zeichenfolge zurück.

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

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

Der große Nachteil dieses Ansatzes ist, dass es funktioniert nur, wenn die „Name“ Schlüssel in der Ressource-Datei die Namen Ihrer ENUM-Werte entsprechen. Dies ist die einzige Möglichkeit, auf Zeichenfolgenwerte in einer Ressourcendatei zu verweisen, ohne dass jeder Enumeration ein Beschreibungsattribut zugewiesen wird. Wie benutzt du das für die Anzeige von Werten? Hier ist ein Beispiel ...

In Ihrem XAML-Code möchten Sie einen Datenprovider erstellen, um die Werte der Enums auf Ihr UI-Element zu bekommen (ich benutze hier eine ComboBox ...). Dann sollten Sie den Konverter verfügbar machen und Ihr UI-Element für die Verwendung des Enum-Konverters vorbereiten. So, hier geht es ...

 <!-- 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, sorry das so lang ist. Ich bin mir nicht sicher, ob dies für Sie zu komplex ist, aber es ist eine andere Option. Ich hoffe es hilft!

+1

Mein Beispiel ist sehr ähnlich wie Ihres (Sie gehen einen Schritt weiter, indem Sie eine Ressourcendatei verwenden, die ich auch an anderen Orten verwende, um mehrsprachige Unterstützung zu erhalten). Der Unterschied in der Komplexität, die ich erwähne, ist, dass mein Beispiel einen Typkonverter erfordert * Auf der Eigenschaft * statt auf der Enumeration selbst. Und leider glaube ich nicht, dass unsere Beispiele übereinstimmen. Sie verwenden xaml und verwenden reine .net winforms. Sie nehmen Ihre Combobox. Ihr XAML bindet die Combobox spezifisch an den von Ihnen benötigten Konverter, was ich zu vermeiden versuche (ich glaube nicht, dass ich erfolgreich sein werde). Danke für die Antwort! – greggorob64

+0

Oh ich sehe was du meinst. Entschuldigung, ich konnte nicht helfen. – akagixxer

Verwandte Themen