Polymorphic Databinding Solutions

Let us assume that you have the following class hierarchy:

(Image from clipboard).png

Now, what do you think the result of this code will be?

BindingList<Animal> animals = new BindingList<Animal>();

animals.Add(new Dog());

animals.Add(new Cat());

GridView gridView = new GridView();

gridView.DataSource = animals;

gridView.DataBind();

Ten points goes to the lady on the right that said that it will produce the following error:

Unhandled Exception: System.Reflection.TargetInvocationException: Property accessor 'Name' on object 'AnimalDesc.Cat' threw the following exception:'Object does not match target type.' ---> System.Reflection.TargetException: Object does not match target type.

The reason for this bug is that the TypeDescriptors in .Net are not aware of polymorphism. Even though both Cat and Dog inherit from Animal and has a Name property, and that the list that I passed to the DataSource is BindingList<T>, the TypeDescriptor only looks at the first item in the list, and uses it to describe all types in the list. This can cause problems when the collection that you pass to the GridView contains an inheritance hierarchy.

After butting my head against this issue for too long, I finally came up with this solution:

public class AnimalTypeDescriptionProvider : TypeDescriptionProvider

{

    public AnimalTypeDescriptionProvider() 

        :base(TypeDescriptor.GetProvider(typeof(Animal)))

    {

    }

 

    public override Type GetReflectionType(Type objectType, object instance)

    {

        return base.GetReflectionType(typeof(Animal), instance);

    }

 

    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)

    {

        return base.GetTypeDescriptor(typeof(Animal), instance);

    }

   

    public static void Register()

    {

        TypeDescriptor.AddProvider(new AnimalTypeDescriptionProvider(), typeof(Animal));

    }

}

Then, I call the AnimalTypeDescriptionProvider.Register(); method in the start of the application.

What this does is it lies to the data binding infrastructure, and tells them that any type that inherits from Animal is actually an Animal, and should be treated appropriately.

This solution is good enough for now, but it will prevent me from databinding a list of Dogs, for instance.

Print | posted on Tuesday, July 25, 2006 1:56 PM

Feedback


Gravatar

#  7/25/2006 2:39 PM Ben Scheirman

This is one area where strongly typed languages suck.

Having to do all of this to get &quot;elegant&quot; databinding with polymorphism actually makes it less elegant.


Gravatar

#  7/26/2006 2:42 AM Tomas Restrepo

You might run into a few extra issues. On something I am working on, it turned out that the grid (in this case a third party grid) wouldn't let us add new records even though we were binding against an IBindingList source. Turned out that if you binded it against an empty collection, it would not be able to do exactly what you say: look at the first item to get the metadata for the columns.

In that case we had to extend our IBindingList implementation with ITypedList to ensure we could return the right metadata for an empty collection.

BTW, I think that in your specific example (where all elements in the class hierarchy basically have the same properties or you just want to restrict yourself to those defined in the base class), doing the ITypedList dance would be more elegant (i.e. less ugly) and easier to maintain. It would be farly trivial to write an ITypedList implementation for BindingList that just returned T's properties.


Gravatar

#  7/26/2006 4:49 AM Ayende Rahien

Hm, ITypedList _is_ a better solution, yes.
I'll see how it goes.


Gravatar

#  8/4/2006 10:22 PM Sean Foy

I have a general solution for this problem:
http://sean-janus.optionpc.com/me/software/DefensiveDatasource/


Gravatar

# ITypedList and PropertyDescriptors 2/3/2007 7:48 AM www.winterdom.com


Gravatar

# [.NET]既存の型コンバータを置換する - Replace Default TypeConverter 2/3/2007 7:48 AM d.hatena.ne.jp

Comments have been closed on this topic.