2010-03-10 43 views
5

Me pregunto cómo cambiar DisplayNameAttribute en tiempo de ejecución, quiero que la displayName sea Pies en lugar de contadores en mi cuadrícula de propiedades cuando hago algunas conversiones, es eso ¿posible?Cómo cambiar DisplayNameAttribute en tiempo de ejecución para usar en una cuadrícula de propiedad C#

[DisplayName("Meters")] 
public double Distance 
    { 
    get{return distance;} 
    } 
+0

Siempre puede configurarlo en 'Distancia' e incluir las unidades en el valor, p. "Distancia | 34 metros" y "Distancia | 112 pies". –

+0

¿Cómo haré eso si mi tipo es un doble por ejemplo? – DogDog

Respuesta

11

Hay varias maneras diferentes de hacerlo. Lo más simple es hacer algo similar a cómo ciertos productos i18n lo hacen: subclasificar el atributo e interceptar el texto; pero esto solo funciona si posee el tipo, y desde un atributo no puede acceder al contexto.

Los siguientes cosas para mirar serían TypeConverter, ya que esto facilita el acceso a la vista de componentes-modelo sobre las propiedades, y es más simple que los siguientes dos opciones ;-P Esto funciona con PropertyGrid, pero no DataGridView etc.

Siguiente en la lista es ICustomTypeDescriptor - no es una interfaz divertida de implementar, pero puede cambiar su propio descriptor de propiedad. Esto requiere que sea el propietario del tipo (para proporcionar el soporte de interfaz).

Por último, CustomTypeDescriptor es como el pasado, pero funciona incluso para tipos que no posee, y permite el acceso a los metadatos ajustado, tanto en el tipo y nivel de objetos (todo lo demás sólo es compatible con objeto ).

¿Qué elegir? Sospecho que TypeConverter sería el más sensato; necesita el contexto del objeto (que no proporciona un atributo subclasificado), pero no necesita la complejidad adicional.

Aquí hay un ejemplo; tenga en cuenta que en el código TypeConverter tenemos acceso al contexto del objeto. Si el nombre es simple, ponerlo en el TypeConverter (al crear la propiedad) debería ser suficiente.

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Windows.Forms; 
class MyFunkyTypeConverter : ExpandableObjectConverter 
{ 
    public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes) 
    { 
     PropertyDescriptorCollection props = base.GetProperties(context, value, attributes); 
     List<PropertyDescriptor> list = new List<PropertyDescriptor>(props.Count); 
     foreach (PropertyDescriptor prop in props) 
     { 
      switch (prop.Name) 
      { 
       case "Distance": 
        list.Add(new DisplayNamePropertyDescriptor(
         prop, "your magic code here")); 
        break; 
       default: 
        list.Add(prop); 
        break; 
      } 
     } 
     return new PropertyDescriptorCollection(list.ToArray(), true); 
    } 
} 
class DisplayNamePropertyDescriptor : PropertyDescriptor 
{ 
    private readonly string displayName; 
    private readonly PropertyDescriptor parent; 
    public DisplayNamePropertyDescriptor(
     PropertyDescriptor parent, string displayName) : base(parent) 
    { 
     this.displayName = displayName; 
     this.parent = parent; 
    } 
    public override string DisplayName 
    {get { return displayName; } } 

    public override bool ShouldSerializeValue(object component) 
    { return parent.ShouldSerializeValue(component); } 

    public override void SetValue(object component, object value) { 
     parent.SetValue(component, value); 
    } 
    public override object GetValue(object component) 
    { 
     return parent.GetValue(component); 
    } 
    public override void ResetValue(object component) 
    { 
     parent.ResetValue(component); 
    } 
    public override bool CanResetValue(object component) 
    { 
     return parent.CanResetValue(component); 
    } 
    public override bool IsReadOnly 
    { 
     get { return parent.IsReadOnly; } 
    } 
    public override void AddValueChanged(object component, EventHandler handler) 
    { 
     parent.AddValueChanged(component, handler); 
    } 
    public override void RemoveValueChanged(object component, EventHandler handler) 
    { 
     parent.RemoveValueChanged(component, handler); 
    } 
    public override bool SupportsChangeEvents 
    { 
     get { return parent.SupportsChangeEvents; } 
    } 
    public override Type PropertyType 
    { 
     get { return parent.PropertyType; } 
    } 
    public override TypeConverter Converter 
    { 
     get { return parent.Converter; } 
    } 
    public override Type ComponentType 
    { 
     get { return parent.ComponentType; } 
    } 
    public override string Description 
    { 
     get { return parent.Description; } 
    } 
    public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter) 
    { 
     return parent.GetChildProperties(instance, filter); 
    } 
    public override string Name 
    { 
     get { return parent.Name; } 
    } 

} 

[TypeConverter(typeof(MyFunkyTypeConverter))] 
class MyFunkyType 
{ 
    public double Distance {get;set;} 

    public double AnotherProperty { get; set; } 
} 
static class Program 
{ 
    [STAThread] 
    static void Main() 
    { 
     Application.EnableVisualStyles(); 
     Application.Run(new Form { Controls = { 
      new PropertyGrid { Dock = DockStyle.Fill, 
       SelectedObject = new MyFunkyType { 
        Distance = 123.45 
       }} 
     }}); 
    } 
} 
0

Los atributos se compilan como parte del tipo, por lo que no pueden modificarse en tiempo de ejecución.

Una solución alternativa podría ser determinar una unidad de medida interna que siempre almacena todos los valores. Meters es un buen candidato. Luego cree servicios de "traductor" que se encuentren entre su clase de consumidor y la clase original, que es responsable de convertir todos los valores a un formato diferente.

+0

¿Ni siquiera puede hacerlo a través de Reflection? – Nick

+0

Lo mejor que podría hacer sería generar un nuevo conjunto con el atributo que desea. Pero no sé si sería útil. –

+0

@Nick reflection es "de solo lectura", p. puede decirle información sobre tipos, incluida la información necesaria para ejecutarlos, pero no puede cambiarlos. Como señala @Nader, puede generar un nuevo ensamblaje con los cambios que desee, pero probablemente no resuelva ningún problema. –

0

Es un poco extraño configurar la unidad en el nombre para mostrar, pero si esto es realmente lo que quiere hacer, entonces su única solución es publicar sus propiedades con PropertyDescriptors personalizados (gracias a un TypeConverter o un tipo personalizado Descriptor) y para anular la propiedad DisplayName.

respondida aquí también: Change DisplayName attribute for a property

0

No sé si esto va a funcionar, pero su DisplayName es un atributo. Cada clase y los miembros de cada clase pueden tener atributos establecidos. Dicho esto, tiene sentido que PropertyInfo le otorgue acceso a este Atributo. Ahora, si vas por este camino y encuentras PropertyInfo.GetCustomAttributes() o algo así, y recuperas el valor del atributo, ¿es que dices que es de solo lectura como le dijiste a Nick?

0

Si el atributo de idioma ya existe (como esta situación) y lo que desea cambiar el nombre, puede utilizar el PropertyDescriptor de esta propiedad. Solo mira a través de la propiedad de atributos hasta que encuentres el atributo y cambies el valor.

Cuestiones relacionadas