Question

I am trying to understand why the below code is not working as expected; the TypeDescriptor is simply not picking up the custom converter from the attributes. I can only assume I have made an obvious mistake but I cannot see it.

-- edit -- this code seems to work when I run it in a console on its own, I'm actually calling a converter from within a much more complex application and from a different namespace.

-- edit -- alternatively any suggestions on how I can debug the TypeDescriptor so I can see what is going on and then I can probably answer this myself.

-- edit -- this problem is almost certainly related to pieces being in different assemblies.

-- edit -- It looks like this is not working because of some quirk of loading assemblies dynamically - this code is running under a plugin like architecture.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.ComponentModel;

namespace MyTest
{

    public class TestTester
    {
        public static void Main(string[] args)
        {
            object v = TypeDescriptor.GetConverter(typeof(MyTest.Test)).ConvertFromInvariantString("Test");
        }
    }

    public class TestConverter : TypeConverter
    {

        public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
        {
            return false;
        }

        public override bool CanConvertFrom(ITypeDescriptorContext context, System.Type sourceType)
        {
            if (sourceType == typeof(string) || base.CanConvertFrom(context, sourceType))
            {
                return true;
            }
            return base.CanConvertFrom(context, sourceType);
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            if (destinationType == typeof(Test) || base.CanConvertTo(destinationType))
            {
                return true;
            }
            return base.CanConvertTo(context, destinationType);
        }

        public override object ConvertFrom(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value)
        {
            if (value.GetType() == typeof(string))
            {
                Test t = new Test();
                t.TestMember = value as string;
                return t;
            }
            return base.ConvertFrom(context, culture, value);
        }

        public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, Type destinationType)
        {
            if (destinationType == typeof(string) && value.GetType() == typeof(Test))
            {
                return ((Test)value).TestMember;
            }
            return base.ConvertTo(context, culture, value, destinationType);
        }

    }

    [TypeConverterAttribute(typeof(TestConverter))]
    public struct Test
    {
        public string TestMember { get; set; }
    }
}
Was it helpful?

Solution

This is a little bit late, but this issue appeared when I asked for a TypeConverter that reside in another assembly which is not directly referenced by the executable assembly.

OTHER TIPS

I had this problem as well and a workaround to the problem is to subscribe to the AssemblyResolve event of the current application domain and resolve the assembly manually.

This is far from a good solution, but it seems to work. I have no idea why the framework behaves this way. I would myself really want to find a less hackish way of resolving this problem.

public void DoMagic()
{
    // NOTE: After this, you can use your typeconverter.
    AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve);
}

private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args)
{
    AppDomain domain = (AppDomain)sender;
    foreach (Assembly asm in domain.GetAssemblies())
    {
        if (asm.FullName == args.Name)
        {
            return asm;
        }
    }
    return null;
}

The answer to this other question should be applicable here. It is a much simpler solution than subscribing to AssemblyResolve.

In summary, the idea is to set the TypeConverter attribute using the full string name of the type converter class, rather than using typeof to provide the class name.

In other words, instead of doing this:

[TypeConverterAttribute(typeof(TestConverter))]
public struct Test
{
    ...
}

do this:

[TypeConverterAttribute("MyTest.TestConverter")]
public struct Test
{
    ...
}

I have seen cases, where I could not pickup attributes on internal fields from other assemblies. Not sure if it was a .NET bug or if it has been fixed.

The only thing I can thing of is that in the complex scenario, you may not have Reflection permission.

We've also observed this behavior in pluggable systems involving loading assemblies from outside the appbase folder.

The root of all evil is a flaw in the TypeDescriptorAttribute implementation.

The attribute has two constructor overloads, one for a plaintext type specification (which is — not unexpectedly — pure magic at runtime) and one for an early-bound typeof() reference. If you use the second path, what could possibly go wrong? Well, actually the attribute only uses the first path. The real and correct runtime type reference is flattened into plaintext, and here there be dragons. So it's no use writing a typeof() — it's always the plaintext-and-magic scenario inside.

The solution? No ideal one, but in our case we were consuming the type conversions within our system only, so we picked the ValueSerializerAttribute instead. Which is basically the WPF's way of doing the same stuff. Its implementation is correct around the typeof() .ctor overload in that it succeeds in preserving the early-bound type identity and always loads the correct type, as written in code.

If you want system (or WinForms) code to use the type converter, this won't help.

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