【问题标题】:Generic way to get enum attributes (with i18n/localization support)获取枚举属性的通用方法(具有 i18n/本地化支持)
【发布时间】:2016-10-27 13:33:36
【问题描述】:

在我寻求一种(更)现代解决方案以在 enum 上使用单个属性以便为条目的显示名称及其工具提示提供数据时,我遇到了 several questionsanswershere 在 SO。

但是,this 的特定答案 a) 通过在属性中使用 ResourceType = typeof(<YourResourcesFileType, e. g. "Resources"> 和在扩展方法中使用 ResourceManager 的实例和 为我指明了正确的方向>b) 鼓励我也写这个单独的问题和答案。

现在,如何编写一个好的、最好是通用的方法,利用更新的 C# 语言特性来填充 e. G。一个带有数据的下拉列表,同时为每个条目提供一个工具提示,每个 enum 条目只使用一个属性?这可能吗?

答案是:,通过使用DisplayAttribute 并利用其NameDescription 属性。

自从我为 WPF 应用程序开发它以来,我受到 MVVM 的约束,并致力于尽可能地编写干净、可读的代码。但与所有代码一样:总是有改进的余地。因此,如果您认为我们可以进一步改进,请随时提供反馈。

【问题讨论】:

  • 这看起来很适合作为 SO 文档部分的条目
  • @Yoda,如果所有内容都存储在资源中,为什么还需要属性?枚举字段名称(例如QuestionType.Answer)是一个很好的资源键。 _Entry_ToolTip 是一个约定,可以添加到键(QuestionType.Answer_ToolTip)并作为参数传递给转换器。 my variation on Enum localization - EnumLocalizationConverter(适用于任何枚举)
  • @Lithium 你能给我一些指导吗?
  • @ASh 因为a)据我了解,您的ConvertBack() 方法需要遍历整个枚举,这在更大的enums 上相当昂贵,b)似乎按照您的方式删除能够同时提供条目名称它的工具提示和c)在我看来,如果使用多个enums(我需要)和方案,您的解决方案将无法工作将不同于EnumType.Field
  • @Yoda 试试Documentation Tour,SO 的文档部分仍处于测试阶段,但它与 SO 非常相似,只是它不是 Q/A,而是专注于成为文档的来源以及像您的答案一样的实施示例。

标签: c# wpf enums attributes resources


【解决方案1】:

注意:代码的格式适合 SO 的设计,不一定是典型的 C# 行(中断)设计,因为我不喜欢水平滚动。

资源文件,Resources.resx

注意 #1: 您必须将 Access Modifier(上面截图中的右上角)设置为 Public 才能获得访问权限来自其他程序集(在我的情况下是必需的)。

注意 #2: 要利用 Visual Studio 的多语言支持概念,本地化资源文件必须命名为 Resources.xx-XX.resx,例如。 G。 Resources.de-DE.resx。

enumQuestionType.cs

namespace Yoda.Data.Interfaces.Enums
{
  using System.ComponentModel.DataAnnotations;
  using Yoda.Data.Interfaces.Properties;

  public enum QuestionType
  {
    [Display(Name = "Question_Type_Unknown_Entry",
             ResourceType = typeof(Resources),
             Description = "Question_Type_Unknown_ToolTip")]
    Unknown = 0,

    [Display(Name = "Question_Type_Question_Entry",
             ResourceType = typeof(Resources),
             Description = "Question_Type_Question_ToolTip")]
    Question = 1,

    [Display(Name = "Question_Type_Answer_Entry",
             ResourceType = typeof(Resources),
             Description = "Question_Type_Answer_ToolTip")]
    Answer = 2
  }
}

EnumExtensions.cs中的扩展方法:

namespace Yoda.Frontend.Extensions
{
  using System;
  using System.ComponentModel.DataAnnotations;
  using System.Globalization;
  using System.Linq;
  using System.Resources;

  public static class EnumExtensions
  {
    // This method can be made private if you don't use it elsewhere
    public static TAttribute GetEnumAttribute<TAttribute>(this Enum enumValue)
      where TAttribute : Attribute
    {
      var memberInfo = enumValue.GetType().GetMember(enumValue.ToString());

      return memberInfo[0].GetCustomAttributes(typeof(TAttribute), false)
                          .OfType<TAttribute>()
                          .FirstOrDefault();
    }

    public static string ToDescription(this Enum enumValue)
    {
      var displayAttribute = enumValue.GetEnumAttribute<DisplayAttribute>();

      return displayAttribute == null
        ? enumValue.ToString().Replace("_", " ")
        : new ResourceManager(displayAttribute.ResourceType)
                .GetString(displayAttribute.Description, CultureInfo.CurrentUICulture);
    }

    public static string ToName(this Enum enumValue)
    {
      var displayAttribute = enumValue.GetEnumAttribute<DisplayAttribute>();

      return displayAttribute == null
        ? enumValue.ToString().Replace("_", " ")
        : new ResourceManager(displayAttribute.ResourceType)
                .GetString(displayAttribute.Name, CultureInfo.CurrentUICulture);
    }
  }

  // Your other enum extension methods go here...
}

在 WPF 中需要 2 个转换器(而不是一个带参数的转换器)以符合 SoC 的使用示例如下所示:

Name 属性转换器,QuestionTypeNameConverter.cs

namespace Yoda.Frontend.Converters
{
  using System;
  using System.Globalization;
  using System.Windows.Data;

  using Yoda.Data.Interfaces.Enums;
  using Yoda.Frontend.Extensions;

  // Second converter would be named QuestionTypeDescriptionConverter 
  public class QuestionTypeNameConverter : IValueConverter
  {
    public object Convert(object value, Type targetType, object parameter,
                          CultureInfo culture)
    {
      // Second converter would call .ToDescription() instead
      return (value as QuestionType? ?? QuestionType.Unknown).ToName();
    }

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

注意:为了您的阅读乐趣,我将示例限制为仅显示一个转换器类,但我已包含 cmets 来描述第二个转换器所需的更改。

最后是 MainView.xaml 中的用法:

<Window x:Class="Yoda.Frontend.MainView" x:Name="MainWindow">
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

        // ... more xmlns & other basic stuff

        xmlns:c="clr-namespace:CAP.GUI.Converters"
  <Window.Resources>
    <c:QuestionTypeDescriptionConverter x:Key="QuestionTypeDescription" />
    <c:QuestionTypeNameConverter        x:Key="QuestionTypeName" />
  </Window.Resources>

  // Your window layout goes here...

  <ComboBox ItemsSource="{Binding QuestionTypes, Mode=OneWay}" Margin="3" Name="QuestionType"
            SelectedItem="{Binding SelectedItem.ValidQuestionType,
                                   Converter={StaticResource QuestionTypeName}}"
            ToolTip="{Binding SelectedItem.ValidQuestionType,
                              Converter={StaticResource QuestionTypeDescription}}">
    <ComboBox.ItemTemplate>
      <DataTemplate>
        <TextBlock Text="{Binding Converter={StaticResource QuestionTypeName}}"
                   ToolTip="{Binding Converter={StaticResource QuestionTypeDescription}}" />
      </DataTemplate>
    </ComboBox.ItemTemplate>
  </ComboBox>

  // Your other window layout goes here...

</Window>

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-14
    • 1970-01-01
    • 2011-02-16
    • 2020-03-08
    • 2016-07-07
    • 2010-12-20
    相关资源
    最近更新 更多