Lokalizacja DisplayNameAttribute
Szukam sposobu na zlokalizowanie nazw właściwości wyświetlanych w PropertyGrid. Nazwa właściwości może być "nadpisana" przy użyciu atrybutu DisplayNameAttribute. Niestety atrybuty nie mogą mieć wyrażeń niestałych. Więc nie mogę używać mocno wpisanych zasobów, takich jak:
class Foo
{
[DisplayAttribute(Resources.MyPropertyNameLocalized)] // do not compile
string MyProperty {get; set;}
}
Rozejrzałem się i znalazłem pewną sugestię, aby dziedziczyć z DisplayNameAttribute, aby móc korzystać z zasobów. Ja bym skończył z kodem w stylu:
class Foo
{
[MyLocalizedDisplayAttribute("MyPropertyNameLocalized")] // not strongly typed
string MyProperty {get; set;}
}
Jednak tracę mocno wpisane korzyści z zasobów, co zdecydowanie nie jest dobrą rzeczą. Wtedy natknąłem się na DisplayNameResourceAttribute , który może być tym, czego szukam. Ale to powinno być w Microsofcie.VisualStudio.Modeling.Zaprojektuj przestrzeń nazw i nie mogę znaleźć referencji, którą mam dodać do tej przestrzeni nazw.
Ktoś wie, czy jest łatwiejszy sposób na uzyskanie lokalizacji DisplayName w dobry sposób ? a może jest sposób na wykorzystanie tego, co Microsoft wydaje się używać w Visual Studio ?
10 answers
Istnieje atrybut Display z systemu.ComponentModel.DataAnnotations w. Net 4. Działa na MVC 3 PropertyGrid
.
[Display(ResourceType = typeof(MyResources), Name = "UserName")]
public string UserName { get; set; }
To wyszukuje zasób o nazwie UserName
w twoim MyResources
.plik resx.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-27 23:08:25
Robimy to dla wielu atrybutów w celu obsługi wielu języków. Przyjęliśmy podobne podejście do Microsoft, gdzie nadpisują swoje atrybuty podstawowe i przekazują nazwę zasobu, a nie rzeczywisty ciąg znaków. Nazwa zasobu jest następnie używana do przeprowadzenia wyszukiwania w zasobach DLL, aby rzeczywisty łańcuch mógł powrócić.
Na przykład:
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);
}
}
}
Możesz pójść o krok dalej, gdy faktycznie używasz atrybutu i określić nazwy zasobów jako stałe w zajęcia statyczne. W ten sposób otrzymujesz deklaracje.
[LocalizedDisplayName(ResourceStrings.MyPropertyName)]
public string MyProperty
{
get
{
...
}
}
AktualizacjaResourceStrings
wygląda to tak (zauważ, że każdy łańcuch odnosi się do nazwy zasobu, który określa rzeczywisty łańcuch):
public static class ResourceStrings
{
public const string ForegroundColorDisplayName="ForegroundColorDisplayName";
public const string FontSizeDisplayName="FontSizeDisplayName";
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-06-11 09:42:07
Oto rozwiązanie, które znalazłem w osobnym zbiorze (zwanym w moim przypadku "wspólnym"):
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Event)]
public class DisplayNameLocalizedAttribute : DisplayNameAttribute
{
public DisplayNameLocalizedAttribute(Type resourceManagerProvider, string resourceKey)
: base(Utils.LookupResource(resourceManagerProvider, resourceKey))
{
}
}
Z kodem do wyszukania zasobu:
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
}
Typowe użycie to:
class Foo
{
[Common.DisplayNameLocalized(typeof(Resources.Resource), "CreationDateDisplayName"),
Common.DescriptionLocalized(typeof(Resources.Resource), "CreationDateDescription")]
public DateTime CreationDate
{
get;
set;
}
}
Co jest dość brzydkie, ponieważ używam dosłownych ciągów dla klucza zasobów. Użycie stałej oznaczałoby modyfikowanie zasobów.Projektant.cs, co chyba nie jest dobrym pomysłem.
Wniosek: nie jestem z tego zadowolony, ale jeszcze mniej cieszę się z Microsoftu, który nie może zapewnić wszystko, co przydatne do tak wspólnego zadania.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-11 13:37:57
Możesz użyć T4 do wygenerowania stałych. Napisałem jeden:
<#@ 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#>";
<# } #>
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-07-22 16:32:24
Użycie atrybutu Display (z systemu.ComponentModel.DataAnnotations) oraz wyrażenie nameof () w C # 6 otrzymasz zlokalizowane i mocno wpisane rozwiązanie.
[Display(ResourceType = typeof(MyResources), Name = nameof(MyResources.UserName))]
public string UserName { get; set; }
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-09-24 19:36:46
To stare pytanie, ale myślę, że jest to bardzo powszechny problem, a oto moje rozwiązanie w MVC 3.
Po pierwsze, szablon T4 jest potrzebny do generowania stałych, aby uniknąć nieprzyjemnych łańcuchów. Mamy etykiety plików zasobów.resx ' zawiera wszystkie ciągi etykiet. Dlatego szablon T4 używa pliku zasobów bezpośrednio,
<#@ template debug="True" hostspecific="True" language="C#" #>
<#@ output extension=".cs" #>
<#@ Assembly Name="C:\Project\trunk\Resources\bin\Development\Resources.dll" #>
<#@ import namespace="System.Collections.Generic" #>
<#@ import namespace="System.Collections" #>
<#@ import namespace="System.Globalization" #>
<#@ import namespace="System" #>
<#@ import namespace="System.Resources" #>
<#
var resourceStrings = new List<string>();
var manager = Resources.Labels.ResourceManager;
IDictionaryEnumerator enumerator = manager.GetResourceSet(CultureInfo.CurrentCulture, true, true)
.GetEnumerator();
while (enumerator.MoveNext())
{
resourceStrings.Add(enumerator.Key.ToString());
}
#>
// This file is generated automatically. Do NOT modify any content inside.
namespace Lib.Const{
public static class LabelNames{
<#
foreach (String label in resourceStrings){
#>
public const string <#=label#> = "<#=label#>";
<#
}
#>
}
}
Następnie zostanie utworzona metoda rozszerzenia, która zlokalizuje 'DisplayName',
using System.ComponentModel.DataAnnotations;
using Resources;
namespace Web.Extensions.ValidationAttributes
{
public static class ValidationAttributeHelper
{
public static ValidationContext LocalizeDisplayName(this ValidationContext context)
{
context.DisplayName = Labels.ResourceManager.GetString(context.DisplayName) ?? context.DisplayName;
return context;
}
}
}
Atrybut "DisplayName" zastępuje się atrybutem "DisplayLabel" w order to read from ' Labels.resx",
namespace Web.Extensions.ValidationAttributes
{
public class DisplayLabelAttribute :System.ComponentModel.DisplayNameAttribute
{
private readonly string _propertyLabel;
public DisplayLabelAttribute(string propertyLabel)
{
_propertyLabel = propertyLabel;
}
public override string DisplayName
{
get
{
return _propertyLabel;
}
}
}
}
Po tych wszystkich pracach przygotowawczych, czas dotknąć tych domyślnych atrybutów walidacji. Używam atrybutu "Required" jako przykładu,
using System.ComponentModel.DataAnnotations;
using Resources;
namespace Web.Extensions.ValidationAttributes
{
public class RequiredAttribute : System.ComponentModel.DataAnnotations.RequiredAttribute
{
public RequiredAttribute()
{
ErrorMessageResourceType = typeof (Errors);
ErrorMessageResourceName = "Required";
}
protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
return base.IsValid(value, validationContext.LocalizeDisplayName());
}
}
}
Teraz możemy zastosować te atrybuty w naszym modelu,
using Web.Extensions.ValidationAttributes;
namespace Web.Areas.Foo.Models
{
public class Person
{
[DisplayLabel(Lib.Const.LabelNames.HowOldAreYou)]
public int Age { get; set; }
[Required]
public string Name { get; set; }
}
}
Domyślnie, nazwa właściwości jest używana jako klucz do wyszukiwania ' Label.resx', ale jeśli ustawisz go przez 'DisplayLabel', użyje go zamiast tego.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-08-04 15:45:19
Możesz podklasować DisplayNameAttribute, aby zapewnić i18n, nadpisując jedną z metod. W ten sposób. edit: być może będziesz musiał zadowolić się używaniem stałej dla klucza.
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" } } } });
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-10 15:50:56
Używam tej metody w moim przypadku
[LocalizedDisplayName("Age", NameResourceType = typeof(RegistrationResources))]
public bool Age { get; set; }
Z kodem
public sealed class LocalizedDisplayNameAttribute : DisplayNameAttribute
{
private PropertyInfo _nameProperty;
private Type _resourceType;
public LocalizedDisplayNameAttribute(string displayNameKey)
: base(displayNameKey)
{
}
public Type NameResourceType
{
get
{
return _resourceType;
}
set
{
_resourceType = value;
_nameProperty = _resourceType.GetProperty(base.DisplayName, BindingFlags.Static | BindingFlags.Public);
}
}
public override string DisplayName
{
get
{
if (_nameProperty == null)
{
return base.DisplayName;
}
return (string)_nameProperty.GetValue(_nameProperty.DeclaringType, null);
}
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-01-12 15:04:04
Cóż, Zgromadzenie jest Microsoft.VisualStudio.Modeling.Sdk.dll
. który pochodzi z Visual Studio SDK (z pakietem integracji Visual Studio).
Ale byłby używany w prawie taki sam sposób jak twój atrybut; nie ma sposobu na użycie zasobów silnie typów w atrybutach po prostu dlatego, że nie są one stałe.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-10 15:36:03
Przepraszam za VB.NET kod, mój C# jest trochę zardzewiały... Ale wpadniesz na pomysł, prawda?
Najpierw Utwórz nową klasę: LocalizedPropertyDescriptor
, która dziedziczy PropertyDescriptor
. Nadpisanie właściwości DisplayName
w ten sposób:
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
jest menedżerem zasobów pliku zasobu, który zawiera Twoje tłumaczenia.
Następnie zaimplementuj ICustomTypeDescriptor
w klasie o zlokalizowanych właściwościach i nadpisaj metodę 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
Możesz teraz użyć atrybutu 'DisplayName` do przechowywania odniesienie do wartości w pliku zasobów...
<DisplayName("prop_description")> _
Public Property Description() As String
prop_description
jest kluczem w pliku zasobów.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-12-10 15:47:06