简体   繁体   English

如何在运行时更改DisplayNameAttribute以在Property Grid C#中使用

[英]How to change the DisplayNameAttribute on runtime to use in a Property Grid C#

I am wondering how to change the DisplayNameAttribute on runtime, I want the displayName to be Feet instead of Meters in my property grid when I do some conversions, is that possible? 我想知道如何在运行时更改DisplayNameAttribute,当我进行一些转换时,我希望displayName在我的属性网格中是Feet而不是Meters,这可能吗?

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

There are a number of different ways to do this. 有许多不同的方法可以做到这一点。 The simplest is to do something akin to how certain i18n products do it - subclass the attribute and intercept the text; 最简单的是做一些类似于某些i18n产品的做法 - 将属性子类化并截取文本; but this only works if you own the type, and from an attribute you can't access the context. 但这仅在您拥有该类型时才有效,并且您无法访问该上下文。

The next things to look at would be TypeConverter , since this provides access to the component-model view on the properties, and is simpler than the next two options ;-p This will work with PropertyGrid , but not DataGridView etc. 接下来要看的是TypeConverter ,因为它提供了对属性的组件模型视图的访问,并且比接下来的两个选项更简单;-p这将适用于PropertyGrid ,但不适用于DataGridView等。

Next on the list is ICustomTypeDescriptor - not a fun interface to implement, but you can swap in your own property-descriptor. 列表中的下一个是ICustomTypeDescriptor - 不是一个有趣的实现接口,但您可以交换自己的属性描述符。 This requires that you own the type (in order to provide the interface support). 这要求您拥有该类型(以提供接口支持)。

Finally, CustomTypeDescriptor is like the last, but works even for types you don't own, and allows access to tweaked metadata at both the type and object level (everything else only supports object ). 最后, CustomTypeDescriptor就像是最后一个,但它甚至适用于您不拥有的类型,并允许在类型对象级别访问调整的元数据(其他一切只支持对象 )。

Which to choose? 选择哪个? I suspect that TypeConverter would be the most sensible; 我怀疑TypeConverter是最明智的; you need the object-context (which a subclassed attribute doesn't provide), but you don't need the extra complexity. 你需要对象上下文(子类属性不提供),但你不需要额外的复杂性。

Here's an example; 这是一个例子; note that in the TypeConverter code we have access to the object-context. 请注意,在TypeConverter代码中,我们可以访问对象上下文。 If the name is simple, then putting it in the TypeConverter (when creating the property) should suffice. 如果名称很简单,那么将它放入TypeConverter (创建属性时)就足够了。

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
                }}
        }});
    }
}

Attributes are compiled as part of the type, so they cannot be changed at runtime. 属性被编译为类型的一部分,因此无法在运行时更改它们。

An alternative solution could be to determine an internal unit of measure which you always store all values. 另一种解决方案可以是确定您始终存储所有值的内部计量单位。 Meters is a good candidate. 米是一个很好的候选人。 Then create "translator" services which sit between your consumer class and the original class, which is responsible for converting all the values to a different format. 然后创建位于您的使用者类和原始类之间的“翻译器”服务,该类负责将所有值转换为不同的格式。

It is a bit odd to set the unit in the display name, but if this is really what you want to do, then your only solution is to publish your properties with custom PropertyDescriptors (thanks to a TypeConverter or a custom Type Descriptor) and to override the DisplayName property. 在显示名称中设置单位有点奇怪,但如果这确实是您想要做的,那么您唯一的解决方案是使用自定义PropertyDescriptors发布您的属性(感谢TypeConverter或自定义类型描述符)并且覆盖DisplayName属性。

Answered here too: Change DisplayName attribute for a property 这里也回答: 更改属性的DisplayName属性

I don't know if this will work, but your DisplayName is an Attribute. 我不知道这是否有效,但您的DisplayName是一个属性。 Each class and each class' members may have attributes set. 每个类和每个类的成员都可以设置属性。 That said, it makes sens that the PropertyInfo will give you acces to this Attribute. 也就是说,PropertyInfo会让您访问此属性。 Now, if you go this way and come accross PropertyInfo.GetCustomAttributes() or something like it, and you retrieve your Attribute value, is that it you say that is read-only as you said to Nick? 现在,如果你走这条路并来到PropertyInfo.GetCustomAttributes()或类似的东西,并且你检索你的属性值,你说这是只读的,就像你对尼克说的那样吗?

If the displayname attribute already exists (like this situation) and you just want to change the name, you can use the propertydescriptor of this property. 如果displayname属性已存在(如此情况)并且您只想更改名称,则可以使用此属性的propertydescriptor。 Just look through the attributes property until you find the attribute and change the value. 只需查看属性属性,直到找到属性并更改值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM