【问题标题】:Adding Editor / EditorAttribute at Run-time (Dynamically) to an Object's Property在运行时(动态)将 Editor / EditorAttribute 添加到对象的属性
【发布时间】:2011-01-03 20:31:01
【问题描述】:

如何在运行时将EditorAttribute(编辑器)添加到对象的属性中?

我有My.Settings.ExcludeFiles,它由设置设计器创建为Public Property ExcludedFiles() As Global.System.Collections.Specialized.StringCollection。通过属性网格编辑ExcludedFiles 时,“字符串集合编辑器”会生成“找不到类型'System.String' 的构造函数”运行时异常。

我无法更改ExcludeFiles 属性的属性,因为它们将在下次进行任何设置更改时被覆盖。因此,我必须在运行时附加/添加 Editor/EditorAttribute。

我想做的是在运行时添加StringCollectionEditor,如下所示作为设计时属性。

    <Editor(GetType(StringCollectionEditor), GetType(UITypeEditor))> _

解决方案

方法#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

更灵活。见尼古拉斯凯迪拉克 在Adding Editor / EditorAttribute at Run-time (Dynamically) to an Object's Property 下方回答。它使用派生的 CustomTypeDescriptor 和 TypeDescriptionProvider 类。您只需添加一次提供程序,例如应用程序初始化。

【问题讨论】:

    标签: winforms attributes propertygrid my.settings


    【解决方案1】:

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

    我从这里抓取了代码,并大致将其更改为以下内容:

    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 不起作用,因为我已经使用设计时属性尝试过它。我想一旦我的代码工作起来,我就会确定要使用的正确编辑器。
    • 我也可以使用 PropertyDescriptor 来做到这一点,对吗?如果有,出于好奇,你有代码吗?
    • 是的,你可以,但这要长得多。顺便说一句,看看我的另一个答案! ;)
    【解决方案2】:

    在给你我的第一个答案后,我想起了 Marc Gravell 给出的另一个解决方案,我什至发表了评论。信不信由你,你只需要调用 TypeDescriptor.AddAttributes()。

    这里是:How do I inject a custom UITypeEditor for all properties of a closed-source type?

    对于你的情况,它给出:

    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 执行更复杂的操作时,我之前的帖子仍然为您提供了一种很好的技巧。

    【讨论】:

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

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

    【讨论】:

    • 这在 .NET 领域没有意义,几乎所有事情都可以在运行时完成。有很多例子可以做到这一点。我只是找不到任何简单的东西来说明这个具体案例。
    • 好吧,然后等待新的答案,也许有人知道一种方法......但我严重怀疑它。属性是 static 元数据,无法在运行时修改现有类型(但您可以动态创建新类型,派生自现有类型;例如,这用于生成透明代理)
    • 我相信这就是TypeDescriptor 的用途。文章做到了,但很难阅读,而不是一个简单的例子。 minalabib.wordpress.com/2009/11/08/…>
    • 你不必接受我的回答,我也不必删除它;)。 Nicolas 的回答很有启发性,但正如我在对他的回答的评论中提到的那样,这种技术不适用于一般情况:它对使用反射可以看到的内容没有影响。该类型实际上并没有被修改,只有通过 TypeDescriptor 对该类型的感知受到影响。所以我的回答仍然有效,尽管我承认它对您的需求不是很有用;)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-01-12
    • 2011-10-05
    • 2017-10-14
    • 2017-04-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多