Why doesn't my TypeConverter get called?

Consider the following code:

public class MyControl : Control {

[TypeConverter(typeof(SomePropertyConverter))]
public Foo SomeProperty {
get { ... }
}

}

[TypeConverter(typeof(FooConverter))]
public class Foo {

   ...
}

Now here are a couple of questions:

  1. Let's say both SomePropertyConverter and FooConverter implement conversion to and from String, given an instance of Foo. If I drop MyControl on a Form designer and try to edit SomeProperty in the property grid, which TypeConverter will get called?
  2. Let's say both SomePropertyConverter and FooConverter implement conversion to InstanceDescriptor, given an instance of Foo. The intention is to control how an object of type Foo gets serialized to code. Which TypeConverter will get called?

The answer to question 1 is SomePropertyConverter. This is because the property grid will query the TypeConverter off the PropertyDescriptor for SomeProperty. The converter look up for properties gives precedence to TypeConverters attached to the property itself over the TypeConverter attached to the object or type. This allows a class like MyControl to control the behavior of the type Foo in the property grid, as related to SomeProperty.

What about question 2? One might expect the behavior to be consistent with 1. Well, no, it is not - the correct answer is FooConverter. This is because the serialization code queries the TypeConverter off the object or type itself, not off the PropertyDescriptor.

It is not immediately obvious why this is the case, and many people wonder if this 'inconsistency' is a bug in the component model. No, it is actually by design. To understand this, let's assume for a moment that the serializer did query the TypeConverter off the PropertyDescriptor. What would happen if the same instance of Foo is assigned to several different properties, each of which had their own TypeConverters specified? How then would the serializer know which TypeConverter to pick? After all, the object itself can be serialized in only one way. It is to avoid such ambiguities that type serializers ignore TypeConverters (and certain other metadata) attached to properties in determining how to serialize objects. This is also why the DesignerSerializerAttribute can only be applied to types, not to properties.

Note that this doesn't mean MyControl doesn't have a say in the serialization of SomeProperty. As I have explained in an earlier post, there are several ways it can control how the property itself is serialized. Note that there is a distinction between type serializers and member serializers. This distinction is made clear in Whidbey by bifurcating the CodeDomSerializer class hierarchy into two base classes, called TypeCodeDomSerializer and MemberCodeDomSerializer, both of which derive from a common base class called CodeDomSerializerBase.