2012-09-18 38 views
2

我有一個Enum需要在ComboBox中顯示。我設法使用ItemsSource將枚舉值設置爲組合框,我試圖對它們進行本地化。我認爲這可以使用值轉換器,但因爲我的枚舉值已經是字符串編譯器拋出錯誤,IValueConverter不能把字符串作爲輸入。我不知道有任何其他方式將它們轉換爲其他字符串值。有沒有其他的方式來做到這一點(不是本地化,但轉換)?來自字符串的IValueConverter

我使用這個marku擴展獲得枚舉值

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

- 編輯 -

枚舉由ADO.NET圖生成,不能被改變。

+0

請顯示'UserTypesToStringConverter'的代碼 –

+0

您可以發佈'UserTypesToStringConverter'的相關代碼。 – Dennis

+0

您是否嘗試過使用衛星程序集使用WPF本地化?那麼你不需要在代碼中進行任何本地化工作。 http://msdn.microsoft。com/en-us/library/ms788718.aspx - 您可以使用您的枚舉的ToString值爲這些標籤控件提供一個UID來解決此特定問題。 –

回答

3

是,通過傳遞的值到轉換器中的時間這將是一個string作爲枚舉(EnumConverter)爲GetStandardValues(即Enum.GetValues())的默認類型轉換器返回的字段以字符串枚舉的。

解決此問題的最佳方法是編寫自定義類型轉換器來裝飾您的Enums。幸運的是,您不是第一個需要這樣做的人,請參閱下面的代碼示例。

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本地化工具(如衛星組件)

+0

這看起來不錯,而且有效。現在,我有一個問題。 Enum是從ADO.NET實體圖生成的,因此我無法更改它:/ –

+1

快速Google搜索揭示了一些討論具有自定義對象和提供程序的ADO.NET的文章。我沒有讀太多,因爲我有其他工作要做。 ...在我們的應用程序中,我們不直接使用實體對象,而是始終將它們轉換爲提供程序層中的本地類型,因爲我們發現ADO.NET對象具有不必要的變更跟蹤開銷,我們在獲取數據後並不需要。 – Dennis

+0

我相信我給了你至少一個部分的解決方案(或者如果考慮到你在原始問題中沒有提到ADO.NET,那麼就是完整的解決方案)。您應該能夠採取上述措施,並使其符合您的需求。 – Dennis

0

編輯:

您可以簡單地在您的視圖模型上添加一個屬性,返回MyEnumType[]並簡單地返回MyEnumType.GetValues()。然後在數據綁定點你將有枚舉值引用,而不是字符串值。

您指定的幫助程序類在第一次看起來似乎很乾淨,但實際上並非如此 - 涉及大量信息,並且您在視圖中做出的決策可能更適合視圖模型。根據你對問題的看法,暴露視圖模型中的枚舉值是有意義的。允許的值(即使它們全部是這些值)可以被看作業務邏輯的一個方面,而不是視圖的一個方面。

如果這還不足以解決問題,我的答案的其餘部分可能會幫助你。


編輯之前:

你可以通過簡單地使用字符串資源,除了資源鍵解決這個問題通常是硬編碼在視圖中。

所以我看着是否有動態綁定的字符串資源鍵的方式,並發現這些其他答案:

其中第二個看起來像一個乾淨和簡單的選項。

雖然我一直在尋找,我也發現這個博客帖子:

下面是從他們的樣本代碼。

查看:

<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] 
} 
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_}" /> 
0

您可能會感興趣這是在下面的博客文章中描述的LocalizedList類:http://wpfglue.wordpress.com/2010/01/14/localized-value-formatting-in-wpf/

它可以用來爲枚舉值定義本地化的翻譯,同時定義它們的順序。此外,它還提供了一個用於組合框的ItemsSource,它允許選擇本地化的字符串項目,同時設置鍵入的枚舉值。

它是用VB.net編寫的,但你應該很容易將它翻譯成C#,或者你可以使用包含二進制格式的庫。