2

Enum表示する必要がある がありますComboBox。を使用してコンボボックスに列挙値を取得できましたが、ItemsSourceそれらをローカライズしようとしています。値コンバーターを使用してそれを行うことができると思いましたが、列挙型の値は既に文字列であるため、コンパイラーはIValueConverter文字列を入力として受け取ることができないというエラーをスローします。それらを他の文字列値に変換する他の方法は知りません。それを行う他の方法はありますか (ローカリゼーションではなく変換)?

このマーク拡張機能を使用して列挙値を取得しています

[MarkupExtensionReturnType(typeof (IEnumerable))]
public class EnumValuesExtension : MarkupExtension {
    public EnumValuesExtension() {}

    public EnumValuesExtension(Type enumType) {
        this.EnumType = enumType;
    }

    [ConstructorArgument("enumType")]
    public Type EnumType { get; set; }
    public override object ProvideValue(IServiceProvider serviceProvider) {
        if (this.EnumType == null)
            throw new ArgumentException("The enum type is not set");
        return Enum.GetValues(this.EnumType);
    }
}

および Window.xaml で

<Converters:UserTypesToStringConverter x:Key="userTypeToStringConverter" />
....
<ComboBox ItemsSource="{Helpers:EnumValuesExtension Data:UserTypes}" 
            Margin="2" Grid.Row="0" Grid.Column="1" SelectedIndex="0" TabIndex="1" IsTabStop="False">
    <ComboBox.ItemTemplate>
        <DataTemplate DataType="{x:Type Data:UserTypes}">
            <Label Content="{Binding Converter=userTypeToStringConverter}" />
        </DataTemplate>
    </ComboBox.ItemTemplate>
</ComboBox>

これがコンバーター クラスです。これは単なるテスト クラスであり、ローカライズはまだ行われていません。

public class UserTypesToStringConverter : IValueConverter {
    public object Convert(object value, Type targetType, object parameter, CultureInfo culture) {
        return (int) ((Data.UserTypes) value) == 0 ? "Fizička osoba" : "Pravna osoba";
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) {
         return default(Data.UserTypes);
     }
}

- 編集 -

Enum は ADO.NET ダイアグラムによって生成され、変更できません。

4

4 に答える 4

3

はい、値をコンバーターに渡すときまでに、string列挙型(EnumConverter)のデフォルトの型コンバーターとしてGetStandardValues(つまりEnum.GetValues())フィールドの列挙型を文字列として返します。

これを解決する最良の方法は、列挙型を装飾するカスタム型コンバーターを作成することです。幸い、これを必要としたのはあなたが最初ではありません。コードサンプルについては以下を参照してください。

public class EnumTypeConverter : EnumConverter
{
    public EnumTypeConverter()
        : base(typeof(Enum))
    {
    }

    public EnumTypeConverter(Type type)
        : base(type)
    {
    }

    public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
    {
        return sourceType == typeof(string) || TypeDescriptor.GetConverter(typeof(Enum)).CanConvertFrom(context, sourceType);
    }

    public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
    {
        if (value is string)
            return GetEnumValue(EnumType, (string)value);

        if (value is Enum)
            return GetEnumDescription((Enum)value);

        return base.ConvertFrom(context, culture, value);
    }

    public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
    {
        if (value is Enum && destinationType == typeof(string))
            return GetEnumDescription((Enum)value);

        if (value is string && destinationType == typeof(string))
            return GetEnumDescription(EnumType, (string)value);

        return base.ConvertTo(context, culture, value, destinationType);
    }

    public static bool GetIsEnumBrowsable(Enum value)
    {
        var fieldInfo = value.GetType().GetField(value.ToString());
        var attributes = (BrowsableAttribute[])fieldInfo.GetCustomAttributes(typeof(BrowsableAttribute), false);

        return !(attributes.Length > 0) || attributes[0].Browsable;
    }

    public static string GetEnumDescription(Enum value)
    {
        var fieldInfo = value.GetType().GetField(value.ToString());
        var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

        return (attributes.Length > 0) ? attributes[0].Description : value.ToString();
    }

    public static string GetEnumDescription(Type value, string name)
    {
        var fieldInfo = value.GetField(name);
        var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);
        return (attributes.Length > 0) ? attributes[0].Description : name;
    }

    public static object GetEnumValue(Type value, string description)
    {
        var fields = value.GetFields();
        foreach (var fieldInfo in fields)
        {
            var attributes = (DescriptionAttribute[])fieldInfo.GetCustomAttributes(typeof(DescriptionAttribute), false);

            if (attributes.Length > 0 && attributes[0].Description == description)
                return fieldInfo.GetValue(fieldInfo.Name);

            if (fieldInfo.Name == description)
                return fieldInfo.GetValue(fieldInfo.Name);
        }

        return description;
    }

    public override bool GetPropertiesSupported(ITypeDescriptorContext context)
    {
        return true;
    }

    public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
    {
        return base.GetStandardValues(context);
    }

}

使用法

[TypeConverter(typeof(EnumTypeConverter))]
public enum UserTypes : int
{
    [Browsable(false)]
    Unkown,    
    [Description("Local")]
    LocalUser,
    [Description("Network")]
    NetworkUser,
    [Description("Restricted")]
    RestrictedUser
} 

ご覧のとおり、上記の列挙型では、Description属性を使用して各フィールドをユーザーフレンドの説明で装飾し、タイプコンバーターをオーバーライドして最初にこの属性を探しました。

100%ではありませんが、これをコードで機能させるMarkupExtensionには、次のように変更する必要もあります(注:これはテストしていないため、ユーザー側でいくつかの作業が必要です)。

[MarkupExtensionReturnType(typeof (IEnumerable))]
public class EnumValuesExtension : MarkupExtension {

    public EnumValuesExtension() {}

    public EnumValuesExtension(Type enumType) 
    {
        this.EnumType = enumType;
    }

    [ConstructorArgument("enumType")]
    public Type EnumType { get; set; }

    public override object ProvideValue(IServiceProvider serviceProvider) 
    {
        if (this.EnumType == null)
            throw new ArgumentException("The enum type is not set");

        var converter = TypeDescriptor.GetConverter(this.EnumType);
        if (converter != null && converter.GetStandardValuesSupported(this.EnumType))        
            return converter.GetStandardValues(this.EnumType);

        return Enum.GetValues(this.EnumType);
    }
}

また、アプリケーションのローカリゼーションは限られていますが、既存の.NETローカリゼーションツール(衛星アセンブリなど)を活用できるため、これが最良で最も保守しやすいアプローチであると思います。

于 2012-09-18T15:18:45.517 に答える
0

次のブログ投稿で説明されている LocalizedList クラスに興味があるかもしれません: http://wpfglue.wordpress.com/2010/01/14/localized-value-formatting-in-wpf/

列挙値のローカライズされた翻訳を定義すると同時に、それらの順序を定義するために使用できます。また、ComboBoxes の ItemsSource を提供します。これにより、型指定された列挙値を設定しながら、ローカライズされた文字列項目を選択できます。

これは VB.net で書かれていますが、C# に簡単に変換できるはずです。または、バイナリ形式で含まれているライブラリを使用することもできます。

于 2012-09-18T17:18:40.760 に答える
0

編集:

を返すプロパティをビューモデルに追加するだけでMyEnumType[]、単にを返すことができますMyEnumType.GetValues()。次に、データバインディングの時点で、文字列値ではなく列挙値を参照します。

あなたが指定したヘルパー クラスは一見きれいに見えますが、実際にはそうではありません。問題の見方によっては、ビュー モデルで列挙型の値を公開するのが理にかなっています。許可された値 (すべての値であっても) は、ビューの側面ではなく、ビジネス ロジックの側面と見なすことができます。

これが問題を解決するのに十分でない場合、私の答えの残りはまだあなたを助けるかもしれません.


編集前:

この問題は、文字列リソースを使用するだけで解決できますが、通常、リソース キーはビューでハードコードされています。

そこで、文字列リソース キーを動的にバインドする方法があるかどうかを調べたところ、次のような回答が見つかりました。

これらの 2 番目は、クリーンでシンプルなオプションのようです。

調べていたらこんなブログ記事も発見。

サンプルのコードは次のとおりです。

意見:

<DataTemplate>
  <Button Command="{Binding}" Padding="2" Margin="2" Width="100" Height="100">
    <StackPanel>
      <Image HorizontalAlignment="Center"
             Width="60"
             app:ResourceKeyBindings.SourceResourceKeyBinding="{Binding Converter={StaticResource ResourceKeyConverter}, ConverterParameter=Image.{0}}"/>
      <TextBlock Text="{ext:ResourceKeyBinding Path=Name, StringFormat=Caption.{0} }" HorizontalAlignment="Center" FontWeight="Bold" Margin="0,2,0,0"/>
    </StackPanel>
  </Button>
</DataTemplate>

資力:

<Application.Resources>
  <BitmapImage x:Key="Image.AngryCommand" UriSource="Angry.png"/>
  <BitmapImage x:Key="Image.CoolCommand" UriSource="Cool.png"/>
  <BitmapImage x:Key="Image.HappyCommand" UriSource="Happy.png"/>

  <sys:String x:Key="Caption.Angry">Angry. Rrrr!</sys:String>
  <sys:String x:Key="Caption.Happy">Happy. Ha ha!</sys:String>
  <sys:String x:Key="Caption.Cool">Chilled out</sys:String>
</Application.Resources>

文字列リソース キー バインドを有効にするマークアップ拡張機能:

public class ResourceKeyBindingExtension : MarkupExtension
{
    public override object ProvideValue(IServiceProvider serviceProvider)
    {
        var resourceKeyBinding = new Binding()
        {
            BindsDirectlyToSource = BindsDirectlyToSource,
            Mode = BindingMode.OneWay,
            Path = Path,
            XPath = XPath,
        };

        //Binding throws an InvalidOperationException if we try setting all three
        // of the following properties simultaneously: thus make sure we only set one
        if (ElementName != null)
        {
            resourceKeyBinding.ElementName = ElementName;
        }
        else if (RelativeSource != null)
        {
            resourceKeyBinding.RelativeSource = RelativeSource;
        }
        else if (Source != null)
        {
            resourceKeyBinding.Source = Source;
        }

        var targetElementBinding = new Binding();
        targetElementBinding.RelativeSource = new RelativeSource()
        {
            Mode = RelativeSourceMode.Self
        };

        var multiBinding = new MultiBinding();
        multiBinding.Bindings.Add(targetElementBinding);
        multiBinding.Bindings.Add(resourceKeyBinding);

        // If we set the Converter on resourceKeyBinding then, for some reason,
        // MultiBinding wants it to produce a value matching the Target Type of the MultiBinding
        // When it doesn't, it throws a wobbly and passes DependencyProperty.UnsetValue through
        // to our MultiBinding ValueConverter. To circumvent this, we do the value conversion ourselves.
        // See http://social.msdn.microsoft.com/forums/en-US/wpf/thread/af4a19b4-6617-4a25-9a61-ee47f4b67e3b
        multiBinding.Converter = new ResourceKeyToResourceConverter()
        {
            ResourceKeyConverter = Converter,
            ConverterParameter = ConverterParameter,
            StringFormat = StringFormat,
        };

        return multiBinding.ProvideValue(serviceProvider);
    }

    [DefaultValue("")]
    public PropertyPath Path { get; set; }

    // [snipped rather uninteresting declarations for all the other properties]
}
于 2012-09-18T15:19:54.223 に答える
0

これを行うには、一般的なリソース コンバーターを使用します。使用するリソース マネージャーを指定し、コンバーターのパラメーターとしてプレフィックスを渡すだけです。

class ResourceConverter : IValueConverter
{
    public ResourceManager ResourceManager { get; set; }

    public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
    {
        if (ResourceManager == null)
            throw new InvalidOperationException("The resource manager is not set");

        if (value == null)
            return string.Empty;
        string prefix = parameter as string ?? string.Empty;
        string resourceKey = prefix + value;
        if (string.IsNullOrEmpty(resourceKey))
            return string.Empty;

        return ResourceManager.GetString(resourceKey);
    }

    public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
    {
        throw new NotSupportedException();
    }
}

次のような列挙型があるとします。

class MyEnum
{
    Foo,
    Bar,
    Baz
}

MyEnum_Foo、MyEnum_Bar、および MyEnum_Baz という名前のリソースは、次のように使用できます。

<Window.Resources>
    <my:ResourceConverter x:Key="resourceConverter" ResourceManager="{x:Static prop:Resources.ResourceManager}" />
</Window.Resources>

...


<Label Content="{Binding Converter=resourceConverter, ConverterParameter=MyEnum_}" />
于 2012-09-18T15:28:47.807 に答える