2009-09-23 8 views
6

Ich zeige eine Liste von Objekten in einem DataGridView an. Alles hat gut funktioniert. Spalten wurden automatisch dem DataGridView basierend auf den Eigenschaften der Objekte hinzugefügt.DataGridView zeigt keine Eigenschaften von Objekten, die ICustomTypeDescriptor implementieren

Jetzt änderte ich die Klasse, die ich im Raster anzeigen zu implementieren ICustomTypeDescriptor. Aber jetzt zeigt das Raster keine Spalten oder Zeilen mehr, wenn ich es auf eine DataSource Liste meines benutzerdefinierten Objekts einstelle.

Ich vermute, das hat etwas mit der Tatsache zu tun, dass mit ICustomTypeDescriptor jede in jeder Zeile jedes Gitters gezeigte Instanz könnte eine andere Reihe von Eigenschaften zurückgeben.

Ich implementiere ICustomTypeDescriptor, so dass ich den Benutzern erlauben kann, dynamisch zur Laufzeit benutzerdefinierte Objekte zu Objekten hinzuzufügen. Diese benutzerdefinierten Eigenschaften sollten sichtbar und über das DataGridView editierbar sein.

Warum Datagridview nicht meine ICustomTypeDescriptor Methoden sehen? Gibt es eine andere Möglichkeit, dass ich einem Objekt dynamisch Eigenschaften hinzufügen kann, die in einem DataGridView angezeigt werden?

Antwort

21

DataGridView untersucht die Listenversion von Metadaten; die Regeln hierfür sind ... Anlage:

  1. , wenn die Datenquelle IListSource implementiert, GetList() wird ausgewertet und als Datenquelle verwendet (bei 2 weiter)
  2. , wenn die Daten-Quelle ITypedList implementiert, GetProperties() Metadaten verwendet wird (exit)
  3. wenn eine typisierte (nicht object) Indexer (dh public T this[int index]) gefunden werden kann, um zu erhalten, dann wird T als Quelle über TypeDescriptor.GetProperties(type) verwendet:
    1. wenn ein TypeDescriptionProvider zugeordnet ist, die dieser für Metadaten gegen den Typen verwendet wird (exit)
    2. sonst Reflexion für Metadaten (Exit) verwendet wird
  4. Wenn die Liste nicht leer ist, wird das erste Objekt, verwendet für Metadaten über TypeDescriptor.GetProperties(list[0]):
    1. wenn ICustomTypeDescriptor implementiert wird, dann wird es verwendet (Exit) [*]
    2. wenn ein TypeDescriptionProvider zugeordnet ist, die diese für Metadaten gegen den Typ (Exit) [*]
    3. verwendet wird
    4. sonst Reflexion verwendet wird (Ausfahrt)
  5. sonst Metadaten ist nicht verfügbar (Exit)

([*] = ich nicht die Art und Weise um diese beiden gehen erinnern kann ...)

Wenn Sie List<T> (oder ähnlich) verwenden, dann treffen Sie den "einfachsten" (IMO) Fall - # 3. Wenn Sie benutzerdefinierte Metadaten bereitstellen möchten, dann; Am besten schreiben Sie eine TypeDescriptionProvider und verknüpfen Sie sie mit dem Typ. Ich kann ein Beispiel schreiben, aber es dauert eine Weile (im Zug, wahrscheinlich) ...

Edit: here's ein Beispiel, das ITypedList verwendet; Ich werde versuchen, es zu verwenden, um stattdessen TypeDescriptionProvider zu verwenden ...

Zweite Bearbeitung: ein vollständiges (noch minimales) Beispiel unter Verwendung TypeDescriptionProvider folgt; Lange Codewarnung ...

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Windows.Forms; 
// example 
static class Program { 
    [STAThread] 
    static void Main() { 
     PropertyBag.AddProperty("UserName", typeof(string), new DisplayNameAttribute("User Name")); 
     PropertyBag.AddProperty("DateOfBirth", typeof(DateTime), new DisplayNameAttribute("Date of Birth")); 
     BindingList<PropertyBag> list = new BindingList<PropertyBag>() { 
      new PropertyBag().With("UserName", "Fred").With("DateOfBirth", new DateTime(1998,12,1)), 
      new PropertyBag().With("UserName", "William").With("DateOfBirth", new DateTime(1997,4,23)) 
     }; 

     Application.Run(new Form { 
      Controls = { 
       new DataGridView { // prove it works for complex bindings 
        Dock = DockStyle.Fill, 
        DataSource = list, 
        ReadOnly = false, AllowUserToAddRows = true 
       } 
      }, 
      DataBindings = { 
       {"Text", list, "UserName"} // prove it works for simple bindings 
      } 
     }); 
    } 
} 
// PropertyBag file 1; the core bag 
partial class PropertyBag : INotifyPropertyChanged { 
    private static PropertyDescriptorCollection props; 
    public event PropertyChangedEventHandler PropertyChanged; 
    void OnPropertyChanged(string propertyName) { 
     PropertyChangedEventHandler handler = PropertyChanged; 
     if (handler != null) handler(this, new PropertyChangedEventArgs(propertyName)); 
    } 
    static PropertyBag() { 
     props = new PropertyDescriptorCollection(new PropertyDescriptor[0], true); 
     // init the provider; I'm avoiding TypeDescriptionProviderAttribute so that we 
     // can exploit the default implementation for fun and profit 
     TypeDescriptionProvider defaultProvider = TypeDescriptor.GetProvider(typeof(PropertyBag)), 
      customProvider = new PropertyBagTypeDescriptionProvider(defaultProvider); 
     TypeDescriptor.AddProvider(customProvider, typeof(PropertyBag)); 
    } 
    private static readonly object syncLock = new object(); 
    public static void AddProperty(string name, Type type, params Attribute[] attributes) { 
     lock (syncLock) 
     { // append the new prop, into a *new* collection, so that downstream 
      // callers don't have to worry about the complexities 
      PropertyDescriptor[] newProps = new PropertyDescriptor[props.Count + 1]; 
      props.CopyTo(newProps, 0); 
      newProps[newProps.Length - 1] = new PropertyBagPropertyDescriptor(name, type, attributes); 
      props = new PropertyDescriptorCollection(newProps, true); 
     } 
    } 
    private readonly Dictionary<string, object> values; 
    public PropertyBag() 
    { // mainly want to enforce that we have a public parameterless ctor 
     values = new Dictionary<string, object>(); 
    }  
    public object this[string key] { 
     get { 
      if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key"); 
      object value; 
      values.TryGetValue(key, out value); 
      return value; 
     } 
     set { 
      if (string.IsNullOrEmpty(key)) throw new ArgumentNullException("key"); 
      var prop = props[key]; 
      if (prop == null) throw new ArgumentException("Invalid property: " + key, "key"); 
      values[key] = value; 
      OnPropertyChanged(key); 
     } 
    } 
    internal void Reset(string key) { 
     values.Remove(key); 
    } 
    internal bool ShouldSerialize(string key) { 
     return values.ContainsKey(key); 
    } 
} 

static class PropertyBagExt 
{ 
    // cheeky fluent API to make the example code easier: 
    public static PropertyBag With(this PropertyBag obj, string name, object value) { 
     obj[name] = value; 
     return obj; 
    } 
} 

// PropertyBag file 2: provider/type-descriptor 
partial class PropertyBag { 
    class PropertyBagTypeDescriptionProvider : TypeDescriptionProvider, ICustomTypeDescriptor { 
     readonly ICustomTypeDescriptor defaultDescriptor; 
     public PropertyBagTypeDescriptionProvider(TypeDescriptionProvider parent) : base(parent) { 
      this.defaultDescriptor = parent.GetTypeDescriptor(typeof(PropertyBag)); 
     } 
     public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { 
      return this; 
     } 
     AttributeCollection ICustomTypeDescriptor.GetAttributes() { 
      return defaultDescriptor.GetAttributes(); 
     } 
     string ICustomTypeDescriptor.GetClassName() { 
      return defaultDescriptor.GetClassName(); 
     } 
     string ICustomTypeDescriptor.GetComponentName() { 
      return defaultDescriptor.GetComponentName(); 
     } 
     TypeConverter ICustomTypeDescriptor.GetConverter() { 
      return defaultDescriptor.GetConverter(); 
     } 
     EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() { 
      return defaultDescriptor.GetDefaultEvent(); 
     } 
     PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() { 
      return defaultDescriptor.GetDefaultProperty(); 
     } 
     object ICustomTypeDescriptor.GetEditor(Type editorBaseType) { 
      return defaultDescriptor.GetEditor(editorBaseType); 
     } 
     EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes) { 
      return defaultDescriptor.GetEvents(attributes); 
     } 
     EventDescriptorCollection ICustomTypeDescriptor.GetEvents() { 
      return defaultDescriptor.GetEvents(); 
     } 
     PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes) { 
      return PropertyBag.props; // should really be filtered, but meh! 
     } 
     PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties() { 
      return PropertyBag.props; 
     } 
     object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd) { 
      return defaultDescriptor.GetPropertyOwner(pd); 
     } 
    } 
} 
// PropertyBag file 3: property descriptor 
partial class PropertyBag { 
    class PropertyBagPropertyDescriptor : PropertyDescriptor { 
     private readonly Type type; 
     public PropertyBagPropertyDescriptor(string name, Type type, Attribute[] attributes) 
      : base(name, attributes) { 
      this.type = type; 
     } 
     public override object GetValue(object component) { 
      return ((PropertyBag)component)[Name]; 
     } 
     public override void SetValue(object component, object value) { 
      ((PropertyBag)component)[Name] = value; 
     } 
     public override void ResetValue(object component) { 
      ((PropertyBag)component).Reset(Name); 
     } 
     public override bool CanResetValue(object component) { 
      return true; 
     } 
     public override bool ShouldSerializeValue(object component) { 
      return ((PropertyBag)component).ShouldSerialize(Name); 
     } 
     public override Type PropertyType { 
      get { return type; } 
     } 
     public override bool IsReadOnly { 
      get { return false; } 
     } 
     public override Type ComponentType { 
      get { return typeof(PropertyBag); } 
     } 
    } 
} 
Verwandte Themen