Die Sache mit dem TypeDescriptionProvider


Thomas Wölfer
Thomas Wölfer

07. Februar 2008


Seit .Net 2.0 gibt es mehr als eine Methode, einen CustomTypeDescriptor an eine Klasse zu hängen: Vorher gab es nur die Möglichkeite ICustomTypeDescriptor zu implementieren - seitdem gibt es auch eine weitere Methode, und die basiert auf dem TypeDescriptionProvider.

Die Dokumentation dazu ist allerdings, gelinde gesagt, nicht besonders aussagekräftig. Was man zunächst wissen muss ist die Tatsache, das man einen TypeDescriptionProvider über zwei Methoden mit einem Typ verknüpfen kann.

  1. Per Attribut an der Klassen und
  2. Dynamisch zur Laufzeit

Für den 2. Fall gibt es bei Partho eine ganz gute Beschreibung. Will man die Sache aber als Attribut benutzen, wird es unübersichtlich. (Ich habe jedenfalls heute relativ lange damit gekämpft.). Folgendes ist zu tun:

1. Die Klasse wird per TypeDescriptionProvider-Attribut mit dem Provider verknüpft.

[TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
public class MyClass
{

2. Jetzt braucht man den Provider. Um den zu bauen ist es wichtig zu wissen, das die Provider als Stack vorliegen - man kann also mehrer Schichten von Providern übereinander lagern. Beim erzeugen des Providers muss der "Parent" Provider angegeben werden - sonst endet man hinterher ganz ohne Metadaten. Ist man in der ersten Schicht, dann ist der "Parent" Provider der ursprüngliche des behandelten Typs. Die Basisklasse des eigenen Providers ist einfach TypeDescriptionProvider. Die Klasse muss mindestens eine Methode überladen, und zwar die, die den eigentlichen CustomTypeDescriptor liefert:

public sealed class MyTypeDescriptionProvider : TypeDescriptionProvider
{
   public MyTypeDescriptionProvider()
   :
base(TypeDescriptor.GetProvider(typeof(MyClass)))
   {}

   public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance)
   {
      return new MyTypeDescriptor(base.GetTypeDescriptor(objectType, instance));
   }
}

3. Jetzt braucht es nur noch den Provider selbst. Wenn der zum Beispiel GetProperties() überladen soll, dann ist es dafür nur wichtig, die "ursprünglichen" Properties nicht per TypeDescriptor.GetProperties() zu erfragen (sonst gibts logischerweise einen Stack-Überlauf (logischerweise: Ist wirklich logisch, hat mich aber trotzdem mindestens eine Stunde gekostet :-)), sondern die passende Methode der Basisklasse. Ach ja: Auch der eigene CustomTypeDescriptor hat ein "Parent". Das ganze sieht dann so aus:

    public sealed class MyTypeDescriptor : CustomTypeDescriptor
    {
        public MyTypeDescriptor(ICustomTypeDescriptor parent)
            : base(parent)
        {
        }

        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            PropertyDescriptorCollection result = base.GetProperties(attributes);
            // hier wäre eine gute stelle, an den props rumzufummeln
            return result;
        }

    }