Why is this TypeConverter not working?
Asked Answered
N

6

14

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; }
    }
}
Nysa answered 31/8, 2010 at 20:22 Comment(0)
A
3

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.

Adrenalin answered 5/2, 2013 at 5:5 Comment(0)
C
13

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;
}
Charlottcharlotta answered 13/8, 2011 at 18:18 Comment(1)
I can't help but quote Elton John: "Someone saved my life tonight ..." ;-)Nitre
D
6

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
{
    ...
}
Disaccustom answered 7/3, 2017 at 18:24 Comment(7)
Tried this. Didn't work :( But then again, neither does AssemblyResolve, with or without typeof.Macri
In fact, I can add the TypeConverter using TypeDescriptor.AddAttributes, and correctly retrieve it using TypeDescriptor.GetConverter by type or by object, but it STILL doesn't get using by the DataBinding.Macri
@RogerWillcocks is your converter in another assembly by any chance? Perhaps it is not referenced by the one using the attribute?Disaccustom
Just figured it out at last. It was the DataBinding. TypeConverters apparently don't get called on unless "formattingEnabled" is true. @UuDdLrLrSsMacri
It is a bit more the hardcoded way, but it works! I have a .net-dll with a COM-Interop-DLL as an interface to MS Office. The type convertet worked fine in a .net-application consuming the .net-dll but a COM-Interop-DLL consuming a .net-DLL could not resolve typeof and the String did its job. Thanks @UuDdLrLrSs.Osburn
@Osburn I don't like the hardcoded nature of this either, but unfortunately sometimes with WPF I think we just have to live with some ugliness. Glad it helped you out.Disaccustom
This worked for me. I had a strange case when type converter worked in Data Grid View but not in Check Box List. Then I've changed from: [TypeConverter(typeof(EnumDescriptionConverter))] to [TypeConverter("my_namespace.EnumDescriptionConverter")] and then it worked everywhere. The converted is defined in another assembly than the code that is using it.Legislature
A
3

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.

Adrenalin answered 5/2, 2013 at 5:5 Comment(0)
C
1

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.

Chip answered 1/9, 2010 at 10:29 Comment(1)
I am thinking it is most likely to do with it being in different assemblies - I had just tried moving bits around; when I had a PropertyGrid referring to items in another assembly it could no longer use its TypeConverters et al to display the property. I would be surprised if there was no solution to this though.Nysa
B
1

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.

Buttons answered 26/8, 2013 at 18:24 Comment(1)
Make sure there's a DLL reference from this DLL to the one with that type — after you compile it, not just in the project. Or compile with a fresh compiler version which uses Roslyn, it does not ghost out DLL references like the original one.Buttons
M
0

OK. Having spent pretty much an entire day trying to figure this out for Nullable type conversion.

txt.DataBindings.Add(nameof(txt.Text), this.Setting, propertyName);  //fails
txt.DataBindings.Add(nameof(txt.Text), this.Setting, propertyName, false, DataSourceUpdateMode.OnValidation, null, format); //fails
txt.DataBindings.Add(nameof(txt.Text), this.Setting, propertyName, **true**, DataSourceUpdateMode.OnValidation, null, format); //WORKS
Macri answered 11/4, 2020 at 0:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.