Question

How to add the EditorAttribute (Editor) to an object's property at run-time?

I have My.Settings.ExcludeFiles, which is created by the settings designer as Public Property ExcludedFiles() As Global.System.Collections.Specialized.StringCollection. When editing ExcludedFiles via a property grid, the "String Collection Editor" generates a "Constructor on type 'System.String' not found" run-time exception.

I cannot change the attributes of the ExcludeFiles property because they will be overwritten the next time any setting changes are made. Therefore, I must attach/add the Editor/EditorAttribute at run-time.

What I want to do is add the StringCollectionEditor at run-time, shown below as design-time attribute.

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

You only have to add this attribute once, such as application initialization.

Method #2

More flexible. See Nicolas Cadilhac answer below at Adding Editor / EditorAttribute at Run-time (Dynamically) to an Object's Property. It uses derived CustomTypeDescriptor and TypeDescriptionProvider classes. You only have to add the provider once, such as application initialization.

Was it helpful?

Solution

After giving you my first answer, I remembered another solution given by Marc Gravell that I even commented. Believe it or not, you just need to call TypeDescriptor.AddAttributes().

This is here: How do I inject a custom UITypeEditor for all properties of a closed-source type?.

For your case it gives:

TypeDescriptor.AddAttributes(
    typeof(StringCollection),
    new EditorAttribute("System.Windows.Forms.Design.StringCollectionEditor,
        System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a",
        typeof(UITypeEditor)))

So maybe you should uncheck my previous answer and confirm this one as the solution (although all the credit goes to Marc). But my previous post still gives you a good technique when you need to do more complex stuff with a TypeDescriptor.

OTHER TIPS

You can't. An attribute can only be defined at compile time (unless you generate the type dynamically of course)

Yes it is possible to dynamically change a TypeDescriptor so that you return the UITypeEditor you want. This is explained in this article. But note that it will add it for all properties of this type.

I grabbed the code from here and roughly changed it for the following:

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

Then you register your provider:

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

This works well, except that it will make you discover that you have another issue: MultilineStringEditor is an editor that works with the String type, not with the StringCollection type. What you actually need is the private StringCollectionEditor in the .Net framework. So let's replace GetEditor by:

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

I hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top