2010-11-17 3 views
5

Es scheint, dass das DataGridView-Steuerelement nur an Datenquellen binden kann, die flach sind (alle Eigenschaften sind primative Typen). Meine Daten sind hierarchisch. Zum Beispiel:Suchen nach einem Workaround für die Unfähigkeit des DataGridView-Steuerelements, an hierarchische (OO) Daten zu binden

interface INestedObj 
{ 
    string Prop3 { get; } 
} 

interface IParentObj 
{ 
    public string Prop1 { get; } 
    public string Prop2 { get; } 
    public INestedObj NestedObj { get; } 
} 

Vor diesem Hintergrund, wie kann man binden an ein Objekt der Umsetzung IParentObj? Schließlich müssen Sie etwas tun:

grid.Columns["prop1Col"].DataPropertyName = "Prop1"; 
grid.Columns["prop2Col"].DataPropertyName = "Prop2"; 

grid.Columns["prop3Col"].DataPropertyName = "How to display Prop3?"; 

grid.Columns["prop3Col"].DataPropertyName = "NestedObj.Prop3"; // does not work 

Ich bin auf der Suche nach Beratung und/oder Work-Arounds.

TIA

Antwort

3

Hier ist eine einfache Lösung, die am Ende eines langen Tages zu mir kam.

Ich habe eine Linq-Abfrage und Projektion verwendet, um einen anonymen Typ zu erstellen, der die richtigen Informationen in der DataGridView anzeigt.

var query = from pt in parentObjCollection 
    select new {Prop1=pt.Prop1, Prop2=pt.Prop2, NestedObj.Prop3=pt.NestedObj.Prop3}; 

Ich hatte den richtigen Wert (NestedObj.Prop3) an die DataPropertyName Eigenschaft zu liefern, um den Wert zu erhalten, in dem Raster angezeigt werden soll.

Wenn ich mehr Zeit habe, werde ich versuchen, Bradleys Lösung zu implementieren.

+1

Dies ist eine wesentlich elegantere Lösung, aber Sie müssen Dinge wie Änderungsbenachrichtigungen opfern (wenn Ihre Objekte das überhaupt unterstützt haben, weiß ich nicht). Eine letzte Sache, die Sie vielleicht untersuchen möchten, ist die 'ITypedList'-Schnittstelle, die eine Sammlung implementieren kann, um benutzerdefinierte Eigenschaften verfügbar zu machen. Es ist ein wenig weniger flockig als die 'TypeDescriptorProvider' Lösung, die ich bei der Reflektion gepostet habe. –

2

Sie könnten wahrscheinlich ein ungebundenes Spalte für „NestedObj.Prop3“ hinzufügen und manuell seinen Wert handhaben. Um die Spalte zu füllen, behandeln Sie das CellFormatting-Ereignis der DataGridView, rufen Sie das DataBoundItem von der aktuellen Zeile ab und holen Sie das Prop3 daraus. Um die Datenquelle zu aktualisieren, behandeln Sie das CellValidated-Ereignis, um das DataBoundItem zu aktualisieren.

Möglicherweise gibt es mehr geeignete Ereignisse als die, die ich erwähnt habe, aber Sie bekommen die Idee.

7

Sie können aussetzen Eigenschaften von INestedObj für die Bindung, aber die Lösung ist sehr messy.To einige Hintergrundinformationen zu geben, alle WinForms-Steuerelemente, die Datenbindung Verwendung TypeDescriptor, um zu bestimmen, welche Eigenschaften unterstützen existieren auf den Objekten sie verbindlich . Über TypeDescriptionProvider und CustomTypeDescriptor können Sie das Standardverhalten überschreiben und somit Eigenschaften hinzufügen/verbergen. In diesem Fall wird die NestedObj-Eigenschaft ausgeblendet und durch alle Eigenschaften des geschachtelten Typs ersetzt.

Die Technik, die ich zeigen werde hat 2 (big-ish) Einschränkungen:

  1. Da Sie mit Schnittstellen (und nicht die konkreten Klassen) arbeiten, müssen Sie den Descriptor benutzerdefinierten Typ hinzufügen bei Laufzeit.
  2. Der benutzerdefinierte Typdeskriptor muss in der Lage sein, eine konkrete Instanz IParentObj zu erstellen, daher muss er eine solche Klasse kennen, die über einen Standardkonstruktor verfügt.

(Bitte der lange Code entschuldigen)

Zuerst müssen Sie einen Weg, um ein PropertyDescriptor aus der verschachtelten Art der Verpackung, so dass sie von dem Muttertyp zugegriffen werden können:

public class InnerPropertyDescriptor : PropertyDescriptor { 
    private PropertyDescriptor innerDescriptor; 

    public InnerPropertyDescriptor(PropertyDescriptor owner, 
     PropertyDescriptor innerDescriptor, Attribute[] attributes) 
     : base(owner.Name + "." + innerDescriptor.Name, attributes) { 
     this.innerDescriptor = innerDescriptor; 
    } 
    public override bool CanResetValue(object component) { 
     return innerDescriptor.CanResetValue(((IParentObj)component).NestedObj); 
    } 
    public override Type ComponentType { 
     get { return innerDescriptor.ComponentType; } 
    } 
    public override object GetValue(object component) { 
     return innerDescriptor.GetValue(((IParentObj)component).NestedObj); 
    } 
    public override bool IsReadOnly { 
     get { return innerDescriptor.IsReadOnly; } 
    } 
    public override Type PropertyType { 
     get { return innerDescriptor.PropertyType; } 
    } 
    public override void ResetValue(object component) { 
     innerDescriptor.ResetValue(((IParentObj)component).NestedObj); 
    } 
    public override void SetValue(object component, object value) { 
     innerDescriptor.SetValue(((IParentObj)component).NestedObj, value); 
    } 
    public override bool ShouldSerializeValue(object component) { 
     return innerDescriptor.ShouldSerializeValue(
      ((IParentObj)component).NestedObj 
     ); 
    } 
} 

Dann Sie müssen einen benutzerdefinierten Typdeskriptor schreiben, der die Eigenschaften des verschachtelten Typs freilegt:

... an d dann müssen Sie einen Weg, um den Deskriptor des Aussetzens von oben:

public class ParentObjDescriptionProvider : TypeDescriptionProvider { 
    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, 
     object instance) { 
     return new ParentObjDescriptor(); 
    } 
} 

schließlich zur Laufzeit (bevor Sie auf die DataGridView binden), so müssen die Typbeschreibung Anbieter mit der IParentObj Schnittstelle zuzuordnen. Sie können dies zur Kompilierzeit nicht tun, weil TypeDescriptionProviderAttribute nicht auf Schnittstellen platziert werden kann ...

TypeDescriptor.AddProvider(new ParentObjDescriptionProvider(), typeof(IParentObj)); 

Getestet habe ich diese durch ein DataGridView zu einem IParentObj[] Bindung und, und siehe da, es schafft Spalten für Prop1, Prop2 und NestedObj.Prop3.

Sie müssen sich fragen, aber ... ist es wirklich die Mühe wert?

+0

Wow, danke Bradley. Sie erhalten die komplette Trophäe. Letzte Nacht war ich in der Lage, eine andere (einfachere) Lösung auszuarbeiten, die ich jetzt veröffentlichen werde. –

Verwandte Themen