Вот решение, с которым я закончил в отдельном собрании (названный «Распространенным» в моем случае):
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]
public class DisplayNameLocalizedAttribute : DisplayNameAttribute
{
public DisplayNameLocalizedAttribute(Type resourceManagerProvider, string resourceKey)
: base(Utils.LookupResource(resourceManagerProvider, resourceKey))
{
}
}
с кодексом, чтобы искать ресурс:
internal static string LookupResource(Type resourceManagerProvider, string resourceKey)
{
foreach (PropertyInfo staticProperty in resourceManagerProvider.GetProperties(BindingFlags.Static | BindingFlags.NonPublic))
{
if (staticProperty.PropertyType == typeof(System.Resources.ResourceManager))
{
System.Resources.ResourceManager resourceManager = (System.Resources.ResourceManager)staticProperty.GetValue(null, null);
return resourceManager.GetString(resourceKey);
}
}
return resourceKey; // Fallback with the key name
}
Типичное использование было бы:
class Foo
{
[Common.DisplayNameLocalized(typeof(Resources.Resource), "CreationDateDisplayName"),
Common.DescriptionLocalized(typeof(Resources.Resource), "CreationDateDescription")]
public DateTime CreationDate
{
get;
set;
}
}
, Что в значительной степени ужасно, поскольку я использую буквальные последовательности для ключа ресурса. Используя константу там означал бы изменять Ресурсы. Designer.cs, который является, вероятно, не хорошей идеей.
Заключение: Я не доволен этим, но я еще менее доволен Microsoft, которая не может обеспечить ничего полезного для такой общей задачи.
Мы делаем это для многих атрибутов для поддержки нескольких язык. Мы проявили аналогичный подход к Microsoft, где они переопределяют свои основные атрибуты и передают имя ресурса, а не фактическую строку. Имя ресурса тогда используется для выполнения поиска в ресурсах DLL для фактической строки для возврата.
, Например:
class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
private readonly string resourceName;
public LocalizedDisplayNameAttribute(string resourceName)
: base()
{
this.resourceName = resourceName;
}
public override string DisplayName
{
get
{
return Resources.ResourceManager.GetString(this.resourceName);
}
}
}
можно взять это шаг вперед, на самом деле использование атрибута, и определяет имена ресурса как константы в статическом классе. Тем путем Вы получаете объявления как.
[LocalizedDisplayName(ResourceStrings.MyPropertyName)]
public string MyProperty
{
get
{
...
}
}
Обновление
ResourceStrings
посмотрело бы что-то как (примечание, каждая строка будет относиться к названию ресурса, который определяет фактическую строку):
public static class ResourceStrings
{
public const string ForegroundColorDisplayName="ForegroundColorDisplayName";
public const string FontSizeDisplayName="FontSizeDisplayName";
}
Можно разделить DisplayNameAttribute на подклассы для обеспечения i18n путем переопределения одного из методов. Как так. редактирование: Вам, возможно, придется согласиться на использование константы для ключа.
using System;
using System.ComponentModel;
using System.Windows.Forms;
class Foo {
[MyDisplayName("bar")] // perhaps use a constant: SomeType.SomeResName
public string Bar {get; set; }
}
public class MyDisplayNameAttribute : DisplayNameAttribute {
public MyDisplayNameAttribute(string key) : base(Lookup(key)) {}
static string Lookup(string key) {
try {
// get from your resx or whatever
return "le bar";
} catch {
return key; // fallback
}
}
}
class Program {
[STAThread]
static void Main() {
Application.Run(new Form { Controls = {
new PropertyGrid { SelectedObject =
new Foo { Bar = "abc" } } } });
}
}
Ну, блок Microsoft.VisualStudio.Modeling.Sdk.dll
. который идет с SDK Visual Studio (С Пакетом Интеграции Visual Studio).
, Но это использовалось бы в значительной степени тем же способом в качестве Вашего атрибута; нет никакого способа использовать сильно ресурсы типов в атрибутах просто, потому что они не являются постоянными.
Я приношу извинения за код VB.NET, мой C# немного ржав... Но Вы получите идею, правильно?
, В первую очередь, создайте новый класс: LocalizedPropertyDescriptor
, который наследовался PropertyDescriptor
. Переопределите DisplayName
свойство как это:
Public Overrides ReadOnly Property DisplayName() As String
Get
Dim BaseValue As String = MyBase.DisplayName
Dim Translated As String = Some.ResourceManager.GetString(BaseValue)
If String.IsNullOrEmpty(Translated) Then
Return MyBase.DisplayName
Else
Return Translated
End If
End Get
End Property
Some.ResourceManager
ResourceManager файла ресурсов, который содержит Ваши переводы.
Затем, реализация ICustomTypeDescriptor
в классе с локализованными свойствами и переопределении GetProperties
метод:
Public Function GetProperties() As PropertyDescriptorCollection Implements System.ComponentModel.ICustomTypeDescriptor.GetProperties
Dim baseProps As PropertyDescriptorCollection = TypeDescriptor.GetProperties(Me, True)
Dim LocalizedProps As PropertyDescriptorCollection = New PropertyDescriptorCollection(Nothing)
Dim oProp As PropertyDescriptor
For Each oProp In baseProps
LocalizedProps.Add(New LocalizedPropertyDescriptor(oProp))
Next
Return LocalizedProps
End Function
можно теперь использовать атрибут 'DisplayName' для хранения ссылки на значение в файле ресурсов...
<DisplayName("prop_description")> _
Public Property Description() As String
prop_description
ключ в файле ресурсов.
Вы можете использовать T4 для генерации констант. Я написал один:
<#@ template debug="false" hostspecific="true" language="C#" #>
<#@ output extension=".cs" #>
<#@ assembly name="System.Xml.dll" #>
<#@ import namespace="System.Xml" #>
<#@ import namespace="System.Xml.XPath" #>
using System;
using System.ComponentModel;
namespace Bear.Client
{
/// <summary>
/// Localized display name attribute
/// </summary>
public class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
readonly string _resourceName;
/// <summary>
/// Initializes a new instance of the <see cref="LocalizedDisplayNameAttribute"/> class.
/// </summary>
/// <param name="resourceName">Name of the resource.</param>
public LocalizedDisplayNameAttribute(string resourceName)
: base()
{
_resourceName = resourceName;
}
/// <summary>
/// Gets the display name for a property, event, or public void method that takes no arguments stored in this attribute.
/// </summary>
/// <value></value>
/// <returns>
/// The display name.
/// </returns>
public override String DisplayName
{
get
{
return Resources.ResourceManager.GetString(this._resourceName);
}
}
}
partial class Constants
{
public partial class Resources
{
<#
var reader = XmlReader.Create(Host.ResolvePath("resources.resx"));
var document = new XPathDocument(reader);
var navigator = document.CreateNavigator();
var dataNav = navigator.Select("/root/data");
foreach (XPathNavigator item in dataNav)
{
var name = item.GetAttribute("name", String.Empty);
#>
public const String <#= name#> = "<#= name#>";
<# } #>
}
}
}