如何在运行时更改 DisplayNameAttribute 以在属性网格 C# 中使用

发布于 2024-08-24 11:48:14 字数 194 浏览 5 评论 0原文

我想知道如何在运行时更改 DisplayNameAttribute,当我进行一些转换时,我希望属性网格中的 displayName 为英尺而不是米,这可能吗?

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

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?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

儭儭莪哋寶赑 2024-08-31 11:48:14

有多种不同的方法可以做到这一点。最简单的方法是执行类似于某些 i18n 产品的操作方式 - 子类化属性并拦截文本;但这仅在您拥有该类型时才有效,并且您无法从属性访问上下文。

接下来要考虑的是 TypeConverter,因为它提供了对属性的组件模型视图的访问,并且比接下来的两个选项更简单;-p 这将与 PropertyGrid 一起使用,但不是 DataGridView 等。

列表中的下一个是 ICustomTypeDescriptor - 不是一个有趣的实现接口,但您可以交换自己的属性描述符。这要求您拥有该类型(以便提供接口支持)。

最后,CustomTypeDescriptor 与最后一个类似,但甚至适用于您不拥有的类型,并且允许访问类型对象处的调整元数据em> 级别(其他所有内容仅支持对象)。

选择哪个?我怀疑 TypeConverter 是最明智的;您需要对象上下文(子类属性不提供),但不需要额外的复杂性。

这是一个例子;请注意,在 TypeConverter 代码中,我们可以访问对象上下文。如果名称很简单,那么将其放入 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
                }}
        }});
    }
}

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; 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.

Next on the list is ICustomTypeDescriptor - not a fun interface to implement, but you can swap in your own property-descriptor. 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).

Which to choose? I suspect that TypeConverter would be the most sensible; 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. If the name is simple, then putting it in the TypeConverter (when creating the property) should suffice.

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
                }}
        }});
    }
}
打小就很酷 2024-08-31 11:48:14

如果displayname属性已经存在(像这种情况)并且您只想更改名称,则可以使用该属性的propertydescriptor。只需查看attributes 属性,直到找到该属性并更改其值。

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. Just look through the attributes property until you find the attribute and change the value.

睫毛上残留的泪 2024-08-31 11:48:14

属性被编译为类型的一部分,因此无法在运行时更改。

另一种解决方案可能是确定始终存储所有值的内部测量单位。米是一个不错的候选人。然后创建位于您的消费者类和原始类之间的“翻译器”服务,该服务负责将所有值转换为不同的格式。

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.

乖乖兔^ω^ 2024-08-31 11:48:14

在显示名称中设置单位有点奇怪,但如果这确实是您想要做的,那么您唯一的解决方案是使用自定义 PropertyDescriptor 发布属性(感谢 TypeConverter 或自定义类型描述符)并覆盖 DisplayName 属性。

也在这里回答:更改属性的 DisplayName 属性

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.

Answered here too: Change DisplayName attribute for a property

夜雨飘雪 2024-08-31 11:48:14

我不知道这是否有效,但您的 DisplayName 是一个属性。每个类和每个类的成员可以具有属性集。也就是说,PropertyInfo 将使您能够访问此属性,这是有道理的。现在,如果您走这条路并遇到 PropertyInfo.GetCustomAttributes() 或类似的东西,并且您检索您的属性值,那么您是否像您对尼克所说的那样说它是只读的?

I don't know if this will work, but your DisplayName is an Attribute. 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. 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?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文