obtener - recorrer elementos de un xml c#
¿C#atributo de texto del archivo de recursos? (8)
Tengo un atributo y quiero cargar texto al atributo desde un archivo de recursos.
[IntegerValidation(1, 70, ErrorMessage = Data.Messages.Speed)]
private int i_Speed;
Pero sigo recibiendo "Un argumento de atributo debe ser una expresión constante, tipo de expresión o expresión de creación de matriz de un tipo de parámetro de atributo"
Funciona perfectamente si agrego una cadena en lugar de Data.Messages.Text, como:
[IntegerValidation(1, 70, ErrorMessage = "Invalid max speed")]
¿Algunas ideas?
Aquí está la versión modificada de la que armé:
[System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple = false)]
public class ProviderIconAttribute : Attribute
{
public Image ProviderIcon { get; protected set; }
public ProviderIconAttribute(Type resourceType, string resourceName)
{
var value = ResourceHelper.GetResourceLookup<Image>(resourceType, resourceName);
this.ProviderIcon = value;
}
}
//From http://.com/questions/1150874/c-sharp-attribute-text-from-resource-file
//Only thing I changed was adding NonPublic to binding flags since our images come from other dll''s
// and making it generic, as the original only supports strings
public class ResourceHelper
{
public static T GetResourceLookup<T>(Type resourceType, string resourceName)
{
if ((resourceType != null) && (resourceName != null))
{
PropertyInfo property = resourceType.GetProperty(resourceName, BindingFlags.Public | BindingFlags.Static | BindingFlags.NonPublic);
if (property == null)
{
return default(T);
}
return (T)property.GetValue(null, null);
}
return default(T);
}
}
Aquí está mi solución. He agregado las propiedades resourceName y resourceType al atributo, como lo ha hecho Microsoft en DataAnnotations.
public class CustomAttribute : Attribute
{
public CustomAttribute(Type resourceType, string resourceName)
{
Message = ResourceHelper.GetResourceLookup(resourceType, resourceName);
}
public string Message { get; set; }
}
public class ResourceHelper
{
public static string GetResourceLookup(Type resourceType, string resourceName)
{
if ((resourceType != null) && (resourceName != null))
{
PropertyInfo property = resourceType.GetProperty(resourceName, BindingFlags.Public | BindingFlags.Static);
if (property == null)
{
throw new InvalidOperationException(string.Format("Resource Type Does Not Have Property"));
}
if (property.PropertyType != typeof(string))
{
throw new InvalidOperationException(string.Format("Resource Property is Not String Type"));
}
return (string)property.GetValue(null, null);
}
return null;
}
}
Aquí hay algo que escribí ya que no pude encontrar nada más que haga esto:
Entrada
Escribe una clase de cadena constante en el proyecto A.
[GenerateResource]
public static class ResourceFileName
{
public static class ThisSupports
{
public static class NestedClasses
{
[Comment("Comment value")]
public const string ResourceKey = "Resource Value";
}
}
}
Salida
Y se generará un recurso en el proyecto que contiene la clase de constantes.
Todo lo que necesitas hacer es tener este código en algún lugar:
Fuente
public class CommentAttribute : Attribute
{
public CommentAttribute(string comment)
{
this.Comment = comment;
}
public string Comment { get; set; }
}
public class GenerateResourceAttribute : Attribute
{
public string FileName { get; set; }
}
public class ResourceGenerator
{
public ResourceGenerator(IEnumerable<Assembly> assemblies)
{
// Loop over the provided assemblies.
foreach (var assembly in assemblies)
{
// Loop over each type in the assembly.
foreach (var type in assembly.GetTypes())
{
// See if the type has the GenerateResource attribute.
var attribute = type.GetCustomAttribute<GenerateResourceAttribute>(false);
if (attribute != null)
{
// If so determine the output directory. First assume it''s the current directory.
var outputDirectory = Directory.GetCurrentDirectory();
// Is this assembly part of the output directory?
var index = outputDirectory.LastIndexOf(typeof(ResourceGenerator).Assembly.GetName().Name);
if (index >= 0)
{
// If so remove it and anything after it.
outputDirectory = outputDirectory.Substring(0, index);
// Is the concatenation of the output directory and the target assembly name not a directory?
outputDirectory = Path.Combine(outputDirectory, type.Assembly.GetName().Name);
if (!Directory.Exists(outputDirectory))
{
// If that is the case make it the current directory.
outputDirectory = Directory.GetCurrentDirectory();
}
}
// Use the default file name (Type + "Resources") if one was not provided.
var fileName = attribute.FileName;
if (fileName == null)
{
fileName = type.Name + "Resources";
}
// Add .resx to the end of the file name.
fileName = Path.Combine(outputDirectory, fileName);
if (!fileName.EndsWith(".resx", StringComparison.InvariantCultureIgnoreCase))
{
fileName += ".resx";
}
using (var resx = new ResXResourceWriter(fileName))
{
var tuples = this.GetTuplesRecursive("", type).OrderBy(t => t.Item1);
foreach (var tuple in tuples)
{
var key = tuple.Item1 + tuple.Item2.Name;
var value = tuple.Item2.GetValue(null);
string comment = null;
var commentAttribute = tuple.Item2.GetCustomAttribute<CommentAttribute>();
if (commentAttribute != null)
{
comment = commentAttribute.Comment;
}
resx.AddResource(new ResXDataNode(key, value) { Comment = comment });
}
}
}
}
}
}
private IEnumerable<Tuple<string, FieldInfo>> GetTuplesRecursive(string prefix, Type type)
{
// Get the properties for the current type.
foreach (var field in type.GetFields(BindingFlags.Public | BindingFlags.Static))
{
yield return new Tuple<string, FieldInfo>(prefix, field);
}
// Get the properties for each child type.
foreach (var nestedType in type.GetNestedTypes())
{
foreach (var tuple in this.GetTuplesRecursive(prefix + nestedType.Name, nestedType))
{
yield return tuple;
}
}
}
}
Y luego haga un pequeño proyecto que tenga una referencia a todos sus ensamblajes con [GenerateResource]
public class Program
{
static void Main(string[] args)
{
var assemblies = AppDomain.CurrentDomain.GetAssemblies().ToList();
string path = Directory.GetCurrentDirectory();
foreach (string dll in Directory.GetFiles(path, "*.dll"))
{
assemblies.Add(Assembly.LoadFile(dll));
}
assemblies = assemblies.Distinct().ToList();
new ResourceGenerator(assemblies);
}
}
Luego, sus atributos pueden usar la clase estática ResourceFileName.ThisSupports.NestedClasses.ResourceKey
mientras que otros códigos pueden usar el archivo de recursos.
Es posible que necesite adaptarlo a sus necesidades específicas.
Encontré este problema con el nombre para mostrar del atributo e hice los siguientes cambios:
Para nuestro archivo de recursos, cambié la propiedad de la herramienta personalizada a PublicResXFileCodeGenerator
Luego agregó esto al atributo:
[Display(Name = "MyResourceName", ResourceType = typeof(Resources.MyResources))]
La naturaleza de los atributos es tal que los datos que usted ingresa en las propiedades de los atributos deben ser constantes. Estos valores se almacenarán dentro de un ensamblaje, pero nunca darán como resultado el código compilado que se ejecuta. Por lo tanto, no puede tener valores de atributo que dependan de la ejecución para calcular los resultados.
Los valores de atributo están codificados en el ensamblaje cuando compila. Si desea hacer algo en el momento de la ejecución, deberá usar una constante como clave , luego ingrese un código en la clase de atributo para cargar el recurso.
Tengo un caso similar, donde necesito poner cadenas de recursos en atributos. En C # 6, tenemos la capacidad nameof()
, y eso parece hacer el truco.
En mi caso, puedo usar [SomeAttribute(nameof(Resources.SomeResourceKey))]
y compila bien. Luego solo tengo que hacer un pequeño trabajo en el otro extremo para usar ese valor para obtener la cadena correcta del archivo de Recursos.
En tu caso, puedes intentar:
[IntegerValidation(1, 70, ErrorMessageResourceKey = nameof(Data.Messages.Speed))]
private int i_Speed;
Entonces puedes hacer algo en la línea de (pseudo código):
Properties.Resources.ResourceManager.GetString(attribute.ErrorMessageResourceKey);
Utilice una cadena que es el nombre del recurso. .NET hace esto con algunos atributos internos.