6

次のシナリオがあります。列挙型があり、それをバインドして、DataGridViewTextBoxColumnのDataGridView(データバインド)に表示します。

これが私の列挙型です:

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

そして、これを使用する単純なプロパティは次のとおりです。

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

上記の状況では、タイプコンバーターは問題なく動作します。それは私のために変換を行います。

ただし、これは私の理想的なソリューションよりも複雑です。タイプコンバーターを列挙型自体に配置し(上記のコードのコメントを解除)、プロパティでタイプコンバーターをコメントアウトすると、タイプコンバーターは呼び出されなくなります。

私は他のクラスでこの規則を実行しましたが、問題なく動作します。

タイプコンバータを列挙型に直接配置できないのはなぜですか?

参考までに、これが私のタイプコンバータです。

  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);
      }
   };
4

1 に答える 1

3

「これは私の理想的なソリューションよりも複雑です」という意味がわかりません。私はあなたのものとは異なるこれを行う方法を持っていますが、それほど複雑ではないかもしれません. 私のやり方では、前もってオーバーヘッドが増えますが、アプリケーションで使用すればするほど報われます。アプリケーションをローカライズできるように準備し、各列挙値に属性を追加する必要がないことを意味します。

1) Resource Manager キャッシュを作成する

この部分はオプションです。ただし、私のように複数のリソース ファイルを何度も使用すると、リフレクションの量が減り、パフォーマンスが向上する可能性があります。

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) ローカライズされた説明属性を作成する

これは、列挙型に適用する属性です。これの優れた点は、列挙型宣言の上に一度だけ、各列挙型に属性を追加する必要がないことです。

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) ローカライズされた説明コンバーターを作成する

これにより、列挙値が文字列リソース (.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) リソース (.resx) 文字列ファイルを作成する

次に、Enums キー値スタイルに必要な説明を含むリソース ファイルを作成します。つまり、文字列リソースの「名前」列には個々の列挙型の正確な名前を入力し、「値」列にはその列挙型を変換するときに取得する文字列を入力します。
たとえば、次の列挙型があるとします。

public enum MyColors
{
  Black,
  Blue,
  White
}

次に、文字列リソース ファイルは次のようになります...

名前 | 価値

ブラック | ダークカラー
ブルー | 写真 クールな色
ホワイト | 明るい色

5) 属性を持つ列挙型を作成する

最後に、LocalizedDescription を使用して Enum 宣言を行います。LocalizedDescription 属性に渡すパラメーターは、文字列リソース ファイルの種類です。コンバーターを使用すると、列挙型の属性が表示され、リソース ファイルが取得され、特定の列挙値の文字列値に一致するキー文字列が検索され、リソース ファイルから値が変換された文字列として返されます。

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

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

このアプローチの主な欠点は、リソース ファイルの "Name" キーが列挙値の名前と一致する場合にのみ機能することです。これは、各列挙型に説明属性を指定せずにリソース ファイル内の文字列値を参照する唯一の方法です。では、値を表示するためにこれをどのように使用しますか? ここに例があります...

xaml コードでは、列挙型の値を UI 要素に取得するデータ プロバイダーを作成する必要があります (ここでは ComboBox を使用しています...)。次に、コンバーターを使用可能にし、UI 要素をテンプレート化して enum コンバーターを使用するようにします。だからここに行く...

      <!-- 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}}">

うわー、すみません、これはとても長いです。これがあなたにとって複雑すぎるかどうかはわかりませんが、別のオプションです。それが役に立てば幸い!

于 2013-03-02T23:48:07.143 に答える