在运行时(动态地)为对象的属性添加编辑器/EditorAttribute

5
如何在运行时将EditorAttribute(Editor)添加到对象的属性?
我有一个名为 My.Settings.ExcludeFiles 的属性,由设置设计器创建为 Public Property ExcludedFiles() As Global.System.Collections.Specialized.StringCollection 。当通过属性网格编辑 ExcludedFiles 时,“String Collection Editor”会生成“未找到类型为'System.String'的构造函数”的运行时异常。
我无法更改 ExcludeFiles 属性的属性,因为它们将在进行任何设置更改时被覆盖。因此,我必须在运行时附加/添加Editor / EditorAttribute。
我想要做的是在运行时添加 StringCollectionEditor ,如下所示为设计时属性。
    <Editor(GetType(StringCollectionEditor), GetType(UITypeEditor))> _

Solutions

Method #1

TypeDescriptor.AddAttributes( _
    GetType(Specialized.StringCollection), _
    New EditorAttribute( _
        "System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a", _
         GetType(System.Drawing.Design.UITypeEditor)))

您只需添加此属性一次,例如应用程序初始化。

方法 #2

更加灵活。请参见 Nicolas Cadilhac 在 Adding Editor / EditorAttribute at Run-time (Dynamically) to an Object's Property 中的答案。它使用了派生自 CustomTypeDescriptor 和 TypeDescriptionProvider 类的类。您只需添加提供程序一次,例如应用程序初始化。

3个回答

6
在给出我的第一个答案后,我想起了Marc Gravell提供的另一种解决方案,我甚至还评论过。信不信由你,你只需要调用TypeDescriptor.AddAttributes()。
这里是链接:如何为封闭源类型的所有属性注入自定义UITypeEditor?
对于您的情况,它提供了以下内容:
TypeDescriptor.AddAttributes(
    typeof(StringCollection),
    new EditorAttribute("System.Windows.Forms.Design.StringCollectionEditor,
        System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
        typeof(UITypeEditor)))

也许你应该取消选中我的先前答案,并确认这个作为解决方案(尽管所有的功劳都归于Marc)。但是,当你需要使用TypeDescriptor做更复杂的事情时,我的先前帖子仍然给了你一个很好的技巧。请保留HTML标签。

我不确定它是否有效,因为它使用类的编辑器而不是属性的编辑器。我之前尝试过这个,但无法使其工作。现在我有了一个例子,稍后会再次尝试并进行测试。你的第一个答案很好,因为它允许使用.NET内部编辑器。 - AMissico
通过像我这样传递StringCollectionEditor类型,Marc的示例也可以工作,甚至可能只需要命名空间和程序集。请注意,我的解决方案也适用于一个类型(StringCollection),而不仅仅是一个属性。对于一个特定的属性,您需要从自定义TypeDescriptor发布自定义PropertyDescriptor。 - Nicolas Cadilhac
哦,不错……我从来没有意识到这是可能的。但是它只能在使用TypeDescriptor检查类型时起作用,而不能通过“真正”的反射实现:例如,Attribute.GetCustomAttributes不会返回使用TypeDescriptor.AddAttributes添加的属性。无论如何,在这种情况下,它完全实现了预期的目标,所以+1;) - Thomas Levesque

1

是的,可以动态更改TypeDescriptor,以便返回所需的UITypeEditor。这在文章中有解释。但请注意,它将为此类型的所有属性添加它。

我从这里获取了代码,并粗略地更改了以下内容:

private class StringCollectionTypeDescriptor : CustomTypeDescriptor
{
    private Type _objectType;
    private StringCollectionTypeDescriptionProvider _provider;

    public StringCollectionTypeDescriptor(
        StringCollectionTypeDescriptionProvider provider,
        ICustomTypeDescriptor descriptor, Type objectType)
        :
        base(descriptor)
    {
        if (provider == null) throw new ArgumentNullException("provider");
        if (descriptor == null)
            throw new ArgumentNullException("descriptor");
        if (objectType == null)
            throw new ArgumentNullException("objectType");
        _objectType = objectType;
        _provider = provider;
    }

    /* Here is your customization */
    public override object GetEditor(Type editorBaseType)
    {
        return new MultilineStringEditor();
    }
}

public class StringCollectionTypeDescriptionProvider : TypeDescriptionProvider
{
    private TypeDescriptionProvider _baseProvider;

    public StringCollectionTypeDescriptionProvider(Type t)
    {
        _baseProvider = TypeDescriptor.GetProvider(t);
    }

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
    {
        return new StringCollectionTypeDescriptor(this, _baseProvider.GetTypeDescriptor(objectType, instance), objectType);
    }
}

然后您注册您的提供者:

TypeDescriptor.AddProvider(new StringCollectionTypeDescriptionProvider
    (typeof(System.Collections.Specialized.StringCollection)),
    typeof(System.Collections.Specialized.StringCollection));

这个方法很好用,但是你会发现还有一个问题:MultilineStringEditor只能处理String类型,而不能处理StringCollection类型。实际上,你需要的是.Net框架中的私有StringCollectionEditor。所以我们要将GetEditor替换为:

public override object GetEditor(Type editorBaseType)
{
    Type t = Type.GetType("System.Windows.Forms.Design.StringCollectionEditor, System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a");
    return TypeDescriptor.CreateInstance(null, t, new Type[] { typeof(Type) }, new object[] { typeof(string) });
}

希望这能有所帮助。


你真棒!简单、扼要地提供了参考和代码,并且还发现了我在 MultilineStringEditor 引用上的错误。我知道 MultilineStringEditor 不起作用,因为我已经尝试过使用设计时特性。我想一旦代码能够正常工作,我就会确定正确的编辑器使用方式。 - AMissico
我也可以使用 PropertyDescriptor 来实现这个,是吗?如果是的话,出于好奇,你有代码吗? - AMissico
是的,你可以这样做,但这会更加冗长。顺便说一下,请看一下我的另一个答案!! ;) - Nicolas Cadilhac

-1

不行。属性只能在编译时定义(除非你当然可以动态生成类型)


在 .NET 领域中,几乎所有的事情都可以在运行时完成,因此这并没有意义。有很多相关的例子,但我找不到任何简单的例子来说明这个特定的情况。 - AMissico
好的,那就等待新的答案吧,也许有人知道如何做到这一点...但我真的很怀疑。属性是静态元数据,在运行时没有修改现有类型的方法(但您可以动态创建派生自现有类型的新类型;例如,这用于生成透明代理)。 - Thomas Levesque
我认为这就是 TypeDescriptor 的作用。虽然文章中有相关内容,但阅读起来很困难,也没有简单的示例。 http://minalabib.wordpress.com/2009/11/08/run-time-dynamic-attributes-in-c-2/ - AMissico
你不必接受我的答案,我也没有必要将其删除。Nicolas的回答非常启发人,但正如我在他的答案评论中提到的,这种技术并不适用于一般情况:它对使用反射所能看到的内容没有影响。类型实际上并没有被修改,只是通过TypeDescriptor感知到该类型的方式受到影响。因此,我的答案仍然有效,尽管我承认它对您的需求来说并不是非常有用;)。 - Thomas Levesque

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接